fix(trend): align hub and four-exchange trend plan display
Unify gate_bot with shared enrich_trend_plan for strategy pages and hub monitor, reconcile DCA avg with live entry price, and fix missing fill price display. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+117
-6
@@ -359,6 +359,116 @@ def append_leg_fill_price_json(existing_json: str | None, fill_px: float) -> str
|
||||
return json.dumps(fills, ensure_ascii=False, separators=(",", ":"))
|
||||
|
||||
|
||||
def _trend_leg_contracts(
|
||||
leg_idx: int, first_amt: float, leg_amounts: list[float]
|
||||
) -> float:
|
||||
if leg_idx == 0:
|
||||
return float(first_amt)
|
||||
li = leg_idx - 1
|
||||
if 0 <= li < len(leg_amounts):
|
||||
return float(leg_amounts[li])
|
||||
return 0.0
|
||||
|
||||
|
||||
def _infer_trend_fill_from_target_avg(
|
||||
leg_idx: int,
|
||||
prices: list[float],
|
||||
*,
|
||||
first_amt: float,
|
||||
leg_amounts: list[float],
|
||||
legs_done: int,
|
||||
target_avg: float,
|
||||
) -> float:
|
||||
"""已知其余档位成交价时,反推单档成交价使加权均价等于 target_avg。"""
|
||||
total_contracts = 0.0
|
||||
known_cost = 0.0
|
||||
unknown_amt = _trend_leg_contracts(leg_idx, first_amt, leg_amounts)
|
||||
for i in range(legs_done + 1):
|
||||
amt = _trend_leg_contracts(i, first_amt, leg_amounts)
|
||||
if amt <= 0:
|
||||
continue
|
||||
total_contracts += amt
|
||||
if i == leg_idx:
|
||||
continue
|
||||
known_cost += float(prices[i]) * amt
|
||||
if unknown_amt <= 0 or total_contracts <= 0:
|
||||
return float(prices[leg_idx])
|
||||
return (float(target_avg) * total_contracts - known_cost) / unknown_amt
|
||||
|
||||
|
||||
def reconcile_trend_leg_fill_prices(plan: dict) -> list[float]:
|
||||
"""
|
||||
首仓(0)+已补仓(1..legs_done) 成交价。
|
||||
优先 leg_fill_prices_json;缺口用计划网格价;再对齐 avg_entry_price。
|
||||
"""
|
||||
p = plan or {}
|
||||
if int(p.get("first_order_done") or 0) == 0:
|
||||
return []
|
||||
try:
|
||||
legs_done = int(p.get("legs_done") or 0)
|
||||
except (TypeError, ValueError):
|
||||
legs_done = 0
|
||||
try:
|
||||
first_amt = float(p.get("first_order_amount"))
|
||||
except (TypeError, ValueError):
|
||||
first_amt = 0.0
|
||||
try:
|
||||
target_avg = float(p.get("avg_entry_price"))
|
||||
except (TypeError, ValueError):
|
||||
target_avg = None
|
||||
|
||||
fills = parse_leg_fill_prices(p)
|
||||
try:
|
||||
grid = [float(x) for x in json.loads(p.get("grid_prices_json") or "[]")]
|
||||
except Exception:
|
||||
grid = []
|
||||
try:
|
||||
leg_amounts = [float(x) for x in json.loads(p.get("leg_amounts_json") or "[]")]
|
||||
except Exception:
|
||||
leg_amounts = []
|
||||
|
||||
def _default_px(leg_idx: int) -> float:
|
||||
if leg_idx == 0:
|
||||
if target_avg is not None and legs_done == 0:
|
||||
return target_avg
|
||||
try:
|
||||
return float(p.get("avg_entry_price"))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
try:
|
||||
ref = p.get("live_price_ref")
|
||||
if ref not in (None, ""):
|
||||
return float(ref)
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return 0.0
|
||||
gi = leg_idx - 1
|
||||
if 0 <= gi < len(grid):
|
||||
return float(grid[gi])
|
||||
return _default_px(0)
|
||||
|
||||
result: list[float] = []
|
||||
estimated: list[int] = []
|
||||
for leg_idx in range(legs_done + 1):
|
||||
if len(fills) > leg_idx:
|
||||
result.append(float(fills[leg_idx]))
|
||||
else:
|
||||
result.append(_default_px(leg_idx))
|
||||
estimated.append(leg_idx)
|
||||
|
||||
if target_avg is not None and estimated:
|
||||
adjust_idx = estimated[0] if len(estimated) == 1 else estimated[-1]
|
||||
result[adjust_idx] = _infer_trend_fill_from_target_avg(
|
||||
adjust_idx,
|
||||
result,
|
||||
first_amt=first_amt,
|
||||
leg_amounts=leg_amounts,
|
||||
legs_done=legs_done,
|
||||
target_avg=target_avg,
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def calc_trend_plan_money_metrics(plan: dict) -> dict:
|
||||
"""运行中计划头部:按快照风险金额计算盈亏比(止盈盈利 U / 风险 U)。"""
|
||||
out = {"money_rr": None, "risk_amount_u": None}
|
||||
@@ -526,12 +636,12 @@ def enrich_trend_dca_levels_with_tp(plan: dict, levels: list[dict]) -> list[dict
|
||||
if risk_u is None or risk_u <= 0:
|
||||
return levels
|
||||
|
||||
fills = parse_leg_fill_prices(p)
|
||||
try:
|
||||
legs_done = int(p.get("legs_done") or 0)
|
||||
except (TypeError, ValueError):
|
||||
legs_done = 0
|
||||
first_done = int(p.get("first_order_done") or 0) != 0
|
||||
reconciled_fills = reconcile_trend_leg_fill_prices(p)
|
||||
|
||||
ref_raw = p.get("live_price_ref")
|
||||
if ref_raw in (None, ""):
|
||||
@@ -561,8 +671,9 @@ def enrich_trend_dca_levels_with_tp(plan: dict, levels: list[dict]) -> list[dict
|
||||
except (TypeError, ValueError):
|
||||
amt_f = first_amt
|
||||
if first_done:
|
||||
fill_px = fills[0] if fills else None
|
||||
if fill_px is None:
|
||||
if reconciled_fills:
|
||||
fill_px = float(reconciled_fills[0])
|
||||
else:
|
||||
try:
|
||||
fill_px = float(p.get("avg_entry_price") or ref)
|
||||
except (TypeError, ValueError):
|
||||
@@ -571,6 +682,7 @@ def enrich_trend_dca_levels_with_tp(plan: dict, levels: list[dict]) -> list[dict
|
||||
cum_contracts = amt_f
|
||||
row_cum = cum_contracts
|
||||
row["avg_entry"] = float(fill_px)
|
||||
row["price"] = fill_px
|
||||
else:
|
||||
accumulated = [(ref, amt_f)]
|
||||
cum_contracts = amt_f
|
||||
@@ -592,9 +704,8 @@ def enrich_trend_dca_levels_with_tp(plan: dict, levels: list[dict]) -> list[dict
|
||||
leg_contracts = 0.0
|
||||
done = row.get("status") == "done" or (leg_num > 0 and leg_num <= legs_done)
|
||||
if done and leg_contracts > 0:
|
||||
fill_idx = leg_num
|
||||
if len(fills) > fill_idx:
|
||||
fill_px = float(fills[fill_idx])
|
||||
if leg_num < len(reconciled_fills):
|
||||
fill_px = float(reconciled_fills[leg_num])
|
||||
elif grid_trigger_f is not None:
|
||||
fill_px = grid_trigger_f
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user