fix(trend): use money RR, track DCA fills, snapshot before close
Align running-plan header and DCA table with risk-budget RR, record actual fill prices after each leg, and save pre-close snapshots on stop/TP/handoff across hub and exchanges. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+42
-46
@@ -374,18 +374,6 @@ def enrich_trend_plan_for_hub(cfg: dict, raw: dict) -> dict:
|
||||
d = enrich_trend_plan(cfg, dict(raw or {}))
|
||||
d["monitor_source"] = "趋势回调计划"
|
||||
m = _m(cfg)
|
||||
direction = (d.get("direction") or "long").lower()
|
||||
try:
|
||||
avg_e = float(d["avg_entry_price"])
|
||||
sl = float(d["stop_loss"])
|
||||
tp = float(d["take_profit"])
|
||||
rr_fn = getattr(m, "calc_rr_ratio", None)
|
||||
if callable(rr_fn):
|
||||
rr = rr_fn(direction, avg_e, sl, tp)
|
||||
if rr is not None:
|
||||
d["planned_rr"] = float(rr)
|
||||
except (TypeError, ValueError, KeyError):
|
||||
pass
|
||||
try:
|
||||
snap = float(d.get("snapshot_available_usdt") or 0)
|
||||
margin = float(d.get("plan_margin_capital") or 0)
|
||||
@@ -497,8 +485,15 @@ def enrich_trend_plan(cfg: dict, row) -> dict:
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
from strategy_snapshot_lib import attach_trend_dca_levels
|
||||
from strategy_trend_lib import calc_trend_plan_money_metrics
|
||||
|
||||
d = attach_trend_dca_levels(d)
|
||||
money = calc_trend_plan_money_metrics(d)
|
||||
if money.get("money_rr") is not None:
|
||||
d["money_rr"] = money["money_rr"]
|
||||
d["planned_rr"] = money["money_rr"]
|
||||
if money.get("risk_amount_u") is not None:
|
||||
d["risk_amount_u"] = money["risk_amount_u"]
|
||||
try:
|
||||
d["breakeven_default_offset_pct"] = float(cfg.get("breakeven_offset_pct", 0.3))
|
||||
except (TypeError, ValueError):
|
||||
@@ -567,6 +562,19 @@ def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float) -
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
planned_rr = m.calc_rr_ratio(direction, avg_e, float(row["stop_loss"]), float(row["take_profit"]))
|
||||
try:
|
||||
from strategy_snapshot_lib import save_trend_plan_snapshot
|
||||
|
||||
save_trend_plan_snapshot(
|
||||
cfg,
|
||||
conn,
|
||||
row,
|
||||
result_label=result_label,
|
||||
exit_price=float(exit_price) if exit_price is not None else None,
|
||||
pnl_amount=float(pnl_amount) if pnl_amount is not None else None,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
cancel_symbol_orders(cfg, ex_sym)
|
||||
except Exception:
|
||||
@@ -594,24 +602,6 @@ def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float) -
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
closed = conn.execute(
|
||||
"SELECT * FROM trend_pullback_plans WHERE id=?", (plan_id,)
|
||||
).fetchone()
|
||||
if closed:
|
||||
from strategy_snapshot_lib import save_trend_plan_snapshot
|
||||
|
||||
save_trend_plan_snapshot(
|
||||
cfg,
|
||||
conn,
|
||||
closed,
|
||||
result_label=result_label,
|
||||
exit_price=float(exit_price),
|
||||
pnl_amount=float(pnl_amount) if pnl_amount is not None else None,
|
||||
)
|
||||
conn.commit()
|
||||
except Exception:
|
||||
pass
|
||||
if _trend_plan_trade_exists(conn, plan_id):
|
||||
return
|
||||
session_date = row["session_date"] or m.get_trading_day()
|
||||
@@ -808,10 +798,16 @@ def check_trend_pullback_plans(cfg: dict) -> None:
|
||||
old_open = float(row["order_amount_open"] or 0)
|
||||
new_avg = _weighted_avg(old_avg, old_open, fill_px, amt)
|
||||
legs_done += 1
|
||||
from strategy_trend_lib import append_leg_fill_price_json
|
||||
|
||||
fills_json = append_leg_fill_price_json(
|
||||
row["leg_fill_prices_json"] if "leg_fill_prices_json" in row.keys() else None,
|
||||
fill_px,
|
||||
)
|
||||
conn.execute(
|
||||
"UPDATE trend_pullback_plans SET legs_done=?, avg_entry_price=?, "
|
||||
"order_amount_open=?, last_mark_price=? WHERE id=?",
|
||||
(legs_done, new_avg, old_open + amt, pf, row["id"]),
|
||||
"order_amount_open=?, last_mark_price=?, leg_fill_prices_json=? WHERE id=?",
|
||||
(legs_done, new_avg, old_open + amt, pf, fills_json, row["id"]),
|
||||
)
|
||||
row = conn.execute(
|
||||
"SELECT * FROM trend_pullback_plans WHERE id=?", (row["id"],)
|
||||
@@ -991,6 +987,14 @@ def apply_manual_breakeven(cfg: dict, conn, row, offset_pct=None) -> tuple[bool,
|
||||
if not ok_live:
|
||||
return False, live_reason or "实盘未就绪"
|
||||
plan_id = int(row["id"])
|
||||
try:
|
||||
from strategy_snapshot_lib import save_trend_plan_snapshot
|
||||
|
||||
save_trend_plan_snapshot(
|
||||
cfg, conn, row, result_label="保本移交", exit_price=None, pnl_amount=None
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
handoff_row = {
|
||||
"symbol": sym,
|
||||
"exchange_symbol": ex_sym,
|
||||
@@ -1060,18 +1064,6 @@ def apply_manual_breakeven(cfg: dict, conn, row, offset_pct=None) -> tuple[bool,
|
||||
if callable(wl):
|
||||
lines.insert(1, f"**账户:{wl()}**")
|
||||
send("\n".join(lines))
|
||||
try:
|
||||
handoff = conn.execute(
|
||||
"SELECT * FROM trend_pullback_plans WHERE id=?", (plan_id,)
|
||||
).fetchone()
|
||||
if handoff:
|
||||
from strategy_snapshot_lib import save_trend_plan_snapshot
|
||||
|
||||
save_trend_plan_snapshot(
|
||||
cfg, conn, handoff, result_label="保本移交", exit_price=None, pnl_amount=None
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return True, None
|
||||
|
||||
|
||||
@@ -1275,12 +1267,15 @@ def register_trend_routes(app: Flask, cfg: dict) -> None:
|
||||
trading_day = m.get_trading_day(m.app_now())
|
||||
opened_at = m.app_now_str()
|
||||
opened_ms = getattr(m, "_to_ms_with_fallback", lambda a, b: None)(None, opened_at)
|
||||
from strategy_trend_lib import append_leg_fill_price_json
|
||||
|
||||
fills_json = append_leg_fill_price_json(None, fill1)
|
||||
cur = conn.execute(
|
||||
"""INSERT INTO trend_pullback_plans (
|
||||
status,symbol,exchange_symbol,direction,leverage,stop_loss,initial_stop_loss,add_upper,take_profit,risk_percent,
|
||||
snapshot_available_usdt,snapshot_at,plan_margin_capital,target_order_amount,first_order_amount,remainder_total,
|
||||
dca_legs,per_leg_amount,grid_prices_json,leg_amounts_json,legs_done,first_order_done,last_mark_price,avg_entry_price,order_amount_open,opened_at,opened_at_ms,session_date,message
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
dca_legs,per_leg_amount,grid_prices_json,leg_amounts_json,legs_done,first_order_done,last_mark_price,avg_entry_price,order_amount_open,opened_at,opened_at_ms,session_date,message,leg_fill_prices_json
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(
|
||||
"active",
|
||||
symbol,
|
||||
@@ -1311,6 +1306,7 @@ def register_trend_routes(app: Flask, cfg: dict) -> None:
|
||||
opened_ms,
|
||||
trading_day,
|
||||
f"预览ID:{pid[:8]}…",
|
||||
fills_json,
|
||||
),
|
||||
)
|
||||
new_id = int(cur.lastrowid)
|
||||
|
||||
Reference in New Issue
Block a user