Restructure into modules/ with single-process CTP and config/ layout.
Move business code under modules/, env template to config/, PM2 single qihuo process, and _legacy shims for old imports. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
# 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/<int:pid>")
|
||||
@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"))
|
||||
Reference in New Issue
Block a user