# Copyright (c) 2025-2026 马建军. All rights reserved. """HTTP routes for plans module.""" from __future__ import annotations from datetime import date, datetime from flask import ( Response, flash, jsonify, redirect, render_template, request, send_file, session, stream_with_context, url_for, ) def register(deps) -> None: app = deps.app login_required = deps.login_required require_nav = deps.require_nav get_db = deps.get_db get_setting = deps.get_setting set_setting = deps.set_setting fetch_price = deps.fetch_price send_wechat_msg = deps.send_wechat_msg touch_stats_cache = deps.touch_stats_cache get_stats_data = deps.get_stats_data build_market_quote_payload = deps.build_market_quote_payload today_str = deps.today_str expire_old_plans = deps.expire_old_plans TZ = deps.tz DB_PATH = deps.db_path UPLOAD_DIR = deps.upload_dir OPEN_TYPES = deps.open_types EXIT_TRIGGERS = deps.exit_triggers BEHAVIOR_TAGS = deps.behavior_tags KLINE_PERIODS = deps.kline_periods KLINE_CUTOFFS = deps.kline_cutoffs calc_holding_duration = deps.calc_holding_duration holding_to_minutes = deps.holding_to_minutes classify_close_result = deps.classify_close_result calc_rr_ratio = deps.calc_rr_ratio calc_theoretical_pnl = deps.calc_theoretical_pnl parse_review_date_filter = deps.parse_review_date_filter _trading_mode = deps.trading_mode _ua_is_phone = deps.ua_is_phone _static_asset_v = deps.static_asset_v @app.route("/api/plan_prices") @login_required def api_plan_prices(): """今日计划:批量现价与距决策区间上/下沿距离。""" today = today_str() conn = get_db() rows = conn.execute( "SELECT id, symbol, market_code, sina_code, zone_upper, zone_lower " "FROM order_plans WHERE plan_date=? AND status IN ('planned', 'active')", (today,), ).fetchall() conn.close() out = [] for r in rows: sym = r["symbol"] market = r["market_code"] or "" sina = r["sina_code"] or "" upper = float(r["zone_upper"]) lower = float(r["zone_lower"]) price = fetch_price(sym, market, sina) dist_upper = None dist_lower = None in_zone = False if price is not None: dist_upper = round(upper - price, 2) dist_lower = round(price - lower, 2) in_zone = lower <= price <= upper out.append({ "id": r["id"], "price": price, "dist_upper": dist_upper, "dist_lower": dist_lower, "in_zone": in_zone, }) return jsonify(out) @app.route("/plans") @login_required @require_nav("plans") def plans(): today = today_str() start = request.args.get("start", "") end = request.args.get("end", "") conn = get_db() plan_list = conn.execute( "SELECT * FROM order_plans WHERE plan_date=? AND status IN ('planned', 'active') ORDER BY id DESC", (today,), ).fetchall() sql = "SELECT * FROM order_plans WHERE plan_date < ? OR status IN ('closed', 'expired')" params: list = [today] if start: sql += " AND plan_date >= ?" params.append(start) if end: sql += " AND plan_date <= ?" params.append(end) sql += " ORDER BY plan_date DESC, id DESC LIMIT 200" history = conn.execute(sql, params).fetchall() conn.close() return render_template( "plans.html", plans=plan_list, history=history, today=today, start=start, end=end, ) @app.route("/add_plan", methods=["POST"]) @login_required def add_plan(): d = request.form direction = d.get("direction") symbol = d.get("symbol", "").strip() symbol_name = d.get("symbol_name", "").strip() market_code = d.get("market_code", "").strip() sina_code = d.get("sina_code", "").strip() if not direction: flash("请选择多空方向") return redirect(url_for("plans")) if not symbol or not market_code: flash("请从下拉列表选择品种(同花顺合约代码)") return redirect(url_for("plans")) conn = get_db() conn.execute( """INSERT INTO order_plans (symbol, symbol_name, market_code, sina_code, direction, zone_upper, zone_lower, stop_loss, take_profit, plan_date, decision_reason) VALUES (?,?,?,?,?,?,?,?,?,?,?)""", ( symbol, symbol_name, market_code, sina_code, direction, float(d["zone_upper"]), float(d["zone_lower"]), float(d["stop_loss"]), float(d["take_profit"]), today_str(), d.get("decision_reason", "").strip(), ), ) conn.commit() conn.close() flash("开单计划已添加") return redirect(url_for("plans")) @app.route("/del_plan/") @login_required def del_plan(pid): conn = get_db() conn.execute("DELETE FROM order_plans WHERE id=?", (pid,)) conn.commit() conn.close() flash("已删除") return redirect(url_for("plans"))