feat: show review fields in symbol archive trade table
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+114
-7
@@ -4,6 +4,12 @@ from __future__ import annotations
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
from strategy_trade_labels import (
|
||||
MONITOR_TYPE_ROLL,
|
||||
MONITOR_TYPE_TREND_PULLBACK,
|
||||
entry_reason_for_monitor_type,
|
||||
)
|
||||
|
||||
TRADE_COMPLETED_RESULTS = (
|
||||
"止盈",
|
||||
"止损",
|
||||
@@ -78,6 +84,74 @@ def _effective_field(d: dict, reviewed_key: str, base_key: str, default: Any = N
|
||||
return default
|
||||
|
||||
|
||||
def format_hold_minutes(minutes: Any) -> str:
|
||||
try:
|
||||
total = int(minutes or 0)
|
||||
except (TypeError, ValueError):
|
||||
return "0分钟"
|
||||
if total <= 0:
|
||||
return "0分钟"
|
||||
hours = total // 60
|
||||
mins = total % 60
|
||||
if hours:
|
||||
return f"{hours}小时{mins}分钟"
|
||||
return f"{mins}分钟"
|
||||
|
||||
|
||||
def _normalize_monitor_type_label(raw: Any) -> str:
|
||||
mt = str(raw or "").strip()
|
||||
if mt in ("trend_pullback", "trend"):
|
||||
return MONITOR_TYPE_TREND_PULLBACK
|
||||
if mt in ("roll",):
|
||||
return MONITOR_TYPE_ROLL
|
||||
return mt
|
||||
|
||||
|
||||
def effective_entry_type(d: dict) -> str:
|
||||
"""复盘开仓类型优先,与实例交易记录 effective_entry_reason 一致。"""
|
||||
er = _effective_field(d, "reviewed_entry_reason", "entry_reason")
|
||||
if er is not None and str(er).strip():
|
||||
return str(er).strip()
|
||||
mt = _normalize_monitor_type_label(d.get("monitor_type"))
|
||||
er2 = entry_reason_for_monitor_type(mt)
|
||||
if er2:
|
||||
return er2
|
||||
kst = str(d.get("key_signal_type") or "").strip()
|
||||
if kst:
|
||||
return kst
|
||||
return mt
|
||||
|
||||
|
||||
def effective_hold_minutes(
|
||||
d: dict,
|
||||
*,
|
||||
opened_ms: int | None = None,
|
||||
closed_ms: int | None = None,
|
||||
) -> int:
|
||||
hm = _effective_field(d, "reviewed_hold_minutes", "hold_minutes")
|
||||
if hm is not None and str(hm).strip() != "":
|
||||
try:
|
||||
return max(0, int(hm))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
hs = _effective_field(d, "reviewed_hold_seconds", "hold_seconds")
|
||||
if hs is not None and str(hs).strip() != "":
|
||||
try:
|
||||
return max(0, int(int(hs) // 60))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
oms = opened_ms if opened_ms is not None else d.get("opened_at_ms")
|
||||
cms = closed_ms if closed_ms is not None else d.get("closed_at_ms")
|
||||
try:
|
||||
oms_i = int(oms) if oms not in (None, "") else None
|
||||
cms_i = int(cms) if cms not in (None, "") else None
|
||||
except (TypeError, ValueError):
|
||||
oms_i = cms_i = None
|
||||
if oms_i and cms_i and cms_i > oms_i:
|
||||
return max(0, int((cms_i - oms_i) // 60_000))
|
||||
return 0
|
||||
|
||||
|
||||
def _effective_pnl(d: dict) -> float:
|
||||
reviewed = d.get("reviewed_pnl_amount")
|
||||
if reviewed is not None and str(reviewed).strip() != "":
|
||||
@@ -204,6 +278,18 @@ def _normalize_archive_trade_row(
|
||||
trade_id = int(d.get("id"))
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
opened_ms_i = int(opened_ms) if opened_ms else None
|
||||
closed_ms_i = int(closed_ms) if closed_ms else None
|
||||
hold_m = effective_hold_minutes(d, opened_ms=opened_ms_i, closed_ms=closed_ms_i)
|
||||
entry_type = effective_entry_type(d)
|
||||
reviewed = bool(
|
||||
d.get("reviewed_at")
|
||||
or d.get("reviewed_result")
|
||||
or d.get("reviewed_opened_at")
|
||||
or d.get("reviewed_closed_at")
|
||||
or d.get("reviewed_entry_reason")
|
||||
or d.get("reviewed_hold_minutes")
|
||||
)
|
||||
return {
|
||||
"id": trade_id,
|
||||
"exchange_key": (exchange_key or "").strip().lower(),
|
||||
@@ -213,17 +299,20 @@ def _normalize_archive_trade_row(
|
||||
"pnl_amount": round(pnl, 4),
|
||||
"closed_at": closed_at,
|
||||
"opened_at": opened_at,
|
||||
"opened_at_ms": int(opened_ms) if opened_ms else None,
|
||||
"closed_at_ms": int(closed_ms) if closed_ms else None,
|
||||
"monitor_type": d.get("monitor_type"),
|
||||
"opened_at_ms": opened_ms_i,
|
||||
"closed_at_ms": closed_ms_i,
|
||||
"monitor_type": _normalize_monitor_type_label(d.get("monitor_type")),
|
||||
"entry_type": entry_type,
|
||||
"entry_reason": entry_type,
|
||||
"hold_minutes": hold_m,
|
||||
"hold_minutes_text": format_hold_minutes(hold_m),
|
||||
"actual_rr": d.get("actual_rr"),
|
||||
"planned_rr": d.get("planned_rr"),
|
||||
"trade_style": d.get("trade_style"),
|
||||
"entry_reason": d.get("entry_reason"),
|
||||
"trigger_price": d.get("trigger_price"),
|
||||
"stop_loss": _effective_field(d, "reviewed_stop_loss", "stop_loss"),
|
||||
"take_profit": _effective_field(d, "reviewed_take_profit", "take_profit"),
|
||||
"reviewed": bool(d.get("reviewed_at") or d.get("reviewed_result")),
|
||||
"reviewed": reviewed,
|
||||
"trading_day": trading_day_from_dt(close_dt, reset_hour),
|
||||
}
|
||||
|
||||
@@ -278,10 +367,16 @@ def _archive_trade_select_sql(cols: set[str]) -> str:
|
||||
"closed_at_ms",
|
||||
"created_at",
|
||||
"monitor_type",
|
||||
"key_signal_type",
|
||||
"actual_rr",
|
||||
"planned_rr",
|
||||
"trade_style",
|
||||
"entry_reason",
|
||||
"reviewed_entry_reason",
|
||||
"hold_minutes",
|
||||
"reviewed_hold_minutes",
|
||||
"hold_seconds",
|
||||
"reviewed_hold_seconds",
|
||||
"trigger_price",
|
||||
"stop_loss",
|
||||
"take_profit",
|
||||
@@ -341,7 +436,15 @@ def _normalize_snapshot_archive_row(
|
||||
except (TypeError, ValueError):
|
||||
pnl = 0.0
|
||||
st = str(snap.get("strategy_type") or "").strip()
|
||||
monitor_type = "trend_pullback" if st == "trend_pullback" else ("roll" if st == "roll" else st)
|
||||
monitor_type = _normalize_monitor_type_label(
|
||||
"trend_pullback" if st == "trend_pullback" else ("roll" if st == "roll" else st)
|
||||
)
|
||||
hold_m = effective_hold_minutes(
|
||||
{},
|
||||
opened_ms=opened_ms,
|
||||
closed_ms=closed_ms,
|
||||
)
|
||||
entry_type = entry_reason_for_monitor_type(monitor_type) or monitor_type
|
||||
return {
|
||||
"id": -snap_id,
|
||||
"symbol": (snap.get("symbol") or "").strip().upper(),
|
||||
@@ -353,10 +456,14 @@ def _normalize_snapshot_archive_row(
|
||||
"opened_at_ms": opened_ms,
|
||||
"closed_at_ms": closed_ms,
|
||||
"monitor_type": monitor_type,
|
||||
"entry_reason": "trend_pullback" if st == "trend_pullback" else monitor_type,
|
||||
"entry_type": entry_type,
|
||||
"entry_reason": entry_type,
|
||||
"hold_minutes": hold_m,
|
||||
"hold_minutes_text": format_hold_minutes(hold_m),
|
||||
"from_snapshot": True,
|
||||
"snapshot_id": snap_id,
|
||||
"trend_plan_id": snap.get("source_id"),
|
||||
"reviewed": False,
|
||||
"trading_day": trading_day_from_dt(close_dt, reset_hour),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user