feat: 盈亏比与亏损额度展示,市价FAK报单,修复止盈止损保存失败
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+40
-78
@@ -531,6 +531,9 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
"current_price": mark,
|
||||
"margin": pos_metrics.get("margin"),
|
||||
"position_pct": pos_metrics.get("position_pct"),
|
||||
"risk_amount": pos_metrics.get("risk_amount") if sl is not None else None,
|
||||
"risk_pct": pos_metrics.get("risk_pct") if sl is not None else None,
|
||||
"rr_ratio": pos_metrics.get("rr_ratio") if sl is not None and tp is not None else None,
|
||||
"float_pnl": float_pnl,
|
||||
"est_fee": fee_info["total_fee"],
|
||||
"est_fee_open": fee_info["open_fee"],
|
||||
@@ -818,7 +821,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
@app.route("/api/trading/monitor/upsert", methods=["POST"])
|
||||
@login_required
|
||||
def api_trading_monitor_upsert():
|
||||
"""为已有 CTP 持仓补充/更新本地止盈止损监控。"""
|
||||
"""为已有持仓补充/更新本地止盈止损监控。"""
|
||||
d = request.get_json(silent=True) or {}
|
||||
sym = (d.get("symbol_code") or d.get("symbol") or "").strip()
|
||||
direction = (d.get("direction") or "long").strip().lower()
|
||||
@@ -834,87 +837,46 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
if sl is None and tp is None:
|
||||
return jsonify({"ok": False, "error": "请至少填写止损或止盈"}), 400
|
||||
mode = get_trading_mode(get_setting)
|
||||
if not ctp_status(mode).get("connected"):
|
||||
return jsonify({"ok": False, "error": "请先连接 CTP"}), 400
|
||||
has_pos = False
|
||||
for p in _ctp_positions(mode):
|
||||
if int(p.get("lots") or 0) <= 0:
|
||||
continue
|
||||
if (p.get("direction") or "long") != direction:
|
||||
continue
|
||||
if _match_ctp_symbol(p.get("symbol") or "", sym):
|
||||
has_pos = True
|
||||
lots = int(p.get("lots") or lots)
|
||||
entry = float(p.get("avg_price") or entry or 0)
|
||||
sym = (p.get("symbol") or sym).strip()
|
||||
break
|
||||
if not has_pos:
|
||||
return jsonify({"ok": False, "error": "柜台无对应持仓"}), 400
|
||||
conn = get_db()
|
||||
try:
|
||||
init_strategy_tables(conn)
|
||||
mon = None
|
||||
for r in conn.execute(
|
||||
"SELECT * FROM trade_order_monitors WHERE status='active'"
|
||||
).fetchall():
|
||||
row = dict(r)
|
||||
if row.get("direction") != direction:
|
||||
continue
|
||||
if _match_ctp_symbol(sym, row.get("symbol") or ""):
|
||||
mon = row
|
||||
break
|
||||
codes = ths_to_codes(sym)
|
||||
now_s = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
if "trailing_be" in d:
|
||||
trailing_be = 1 if d.get("trailing_be") else 0
|
||||
elif mon:
|
||||
trailing_be = int(mon.get("trailing_be") or 0)
|
||||
else:
|
||||
trailing_be = 0
|
||||
ensure_monitor_order_columns(conn)
|
||||
if mon:
|
||||
initial_sl = mon.get("initial_stop_loss")
|
||||
if sl is not None and initial_sl is None:
|
||||
initial_sl = sl
|
||||
conn.execute(
|
||||
"""UPDATE trade_order_monitors SET stop_loss=?, take_profit=?, lots=?, entry_price=?,
|
||||
initial_stop_loss=?, trailing_be=?
|
||||
WHERE id=?""",
|
||||
(
|
||||
sl, tp, lots, entry or mon.get("entry_price"),
|
||||
initial_sl, trailing_be,
|
||||
mon["id"],
|
||||
),
|
||||
)
|
||||
mid = mon["id"]
|
||||
else:
|
||||
conn.execute(
|
||||
"""INSERT INTO trade_order_monitors (
|
||||
symbol, symbol_name, market_code, direction, lots, entry_price,
|
||||
stop_loss, take_profit, initial_stop_loss, trailing_be,
|
||||
open_time, monitor_type, status
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?, 'active')""",
|
||||
(
|
||||
sym,
|
||||
codes.get("name", sym) if codes else sym,
|
||||
codes.get("market_code", "") if codes else "",
|
||||
direction,
|
||||
lots,
|
||||
entry,
|
||||
sl,
|
||||
tp,
|
||||
sl,
|
||||
trailing_be,
|
||||
now_s,
|
||||
"manual",
|
||||
),
|
||||
)
|
||||
mid = conn.execute("SELECT last_insert_rowid()").fetchone()[0]
|
||||
mon = _find_active_monitor(conn, sym, direction)
|
||||
has_pos = bool(mon)
|
||||
ths_sym = sym
|
||||
if ctp_status(mode).get("connected"):
|
||||
for p in _ctp_positions(mode, refresh_if_empty=False):
|
||||
if int(p.get("lots") or 0) <= 0:
|
||||
continue
|
||||
if (p.get("direction") or "long") != direction:
|
||||
continue
|
||||
if _match_ctp_symbol(p.get("symbol") or "", sym):
|
||||
has_pos = True
|
||||
lots = int(p.get("lots") or lots)
|
||||
entry = float(p.get("avg_price") or entry or 0)
|
||||
ths_sym = _ctp_pos_to_ths_code(p) or sym
|
||||
break
|
||||
if not has_pos:
|
||||
return jsonify({"ok": False, "error": "未找到对应持仓"}), 400
|
||||
trailing_be = 1 if d.get("trailing_be") else (
|
||||
int(mon.get("trailing_be") or 0) if mon else 0
|
||||
)
|
||||
mid = _upsert_open_monitor(
|
||||
conn,
|
||||
sym=ths_sym,
|
||||
direction=direction,
|
||||
lots=lots,
|
||||
price=entry,
|
||||
sl=sl,
|
||||
tp=tp,
|
||||
trailing_be=trailing_be,
|
||||
)
|
||||
conn.commit()
|
||||
mon_row = conn.execute(
|
||||
"SELECT * FROM trade_order_monitors WHERE id=?", (mid,),
|
||||
).fetchone()
|
||||
return jsonify({"ok": True, "monitor_id": mid, "message": "止盈止损已保存,程序本地监控"})
|
||||
_push_position_snapshot_async()
|
||||
return jsonify({
|
||||
"ok": True,
|
||||
"monitor_id": mid,
|
||||
"message": "止盈止损已保存,程序本地监控",
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user