feat: show entry type as category/subtype (反转/启动A)

Format持仓与交易记录开仓类型为两级展示;新平仓写入与旧短标签自动展开。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-07 00:27:53 +08:00
parent 99eb16dbd8
commit f580e4542d
6 changed files with 105 additions and 14 deletions
+7 -1
View File
@@ -185,6 +185,7 @@ from lib.trade.entry_model_lib import (
open_position_button_label,
parse_manual_order_style_fields,
resolve_effective_trade_entry_reason,
format_entry_type_display,
resolve_trade_record_entry_reason,
trend_manual_entry_reason_count,
)
@@ -2115,6 +2116,7 @@ def to_effective_trade_dict(row):
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
reviewed_entry_reason=reviewed_er,
entry_reason=item.get("entry_reason"),
entry_model=item.get("entry_model"),
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
monitor_type=item.get("monitor_type"),
trade_style=item.get("trade_style"),
@@ -8856,7 +8858,11 @@ def export_trade_records():
er0 = (r["entry_reason"] or "").strip() if r["entry_reason"] else ""
er1 = (r["reviewed_entry_reason"] or "").strip() if r["reviewed_entry_reason"] else ""
kst = (r["key_signal_type"] or "").strip() if "key_signal_type" in r.keys() else ""
eff = er1 or er0 or entry_reason_from_key_signal(kst) or ""
eff = format_entry_type_display(
er1 or er0 or entry_reason_from_key_signal(kst) or "",
entry_model=r["entry_model"] if "entry_model" in r.keys() else None,
trade_style=r["trade_style"] if "trade_style" in r.keys() else None,
)
snap = r["initial_stop_loss"] if r["initial_stop_loss"] not in (None, "") else r["stop_loss"]
data.append((
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
+7 -1
View File
@@ -185,6 +185,7 @@ from lib.trade.entry_model_lib import (
open_position_button_label,
parse_manual_order_style_fields,
resolve_effective_trade_entry_reason,
format_entry_type_display,
resolve_trade_record_entry_reason,
trend_manual_entry_reason_count,
)
@@ -2075,6 +2076,7 @@ def to_effective_trade_dict(row):
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
reviewed_entry_reason=reviewed_er,
entry_reason=item.get("entry_reason"),
entry_model=item.get("entry_model"),
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
monitor_type=item.get("monitor_type"),
trade_style=item.get("trade_style"),
@@ -8751,7 +8753,11 @@ def export_trade_records():
er0 = (r["entry_reason"] or "").strip() if r["entry_reason"] else ""
er1 = (r["reviewed_entry_reason"] or "").strip() if r["reviewed_entry_reason"] else ""
kst = (r["key_signal_type"] or "").strip() if "key_signal_type" in r.keys() else ""
eff = er1 or er0 or entry_reason_from_key_signal(kst) or ""
eff = format_entry_type_display(
er1 or er0 or entry_reason_from_key_signal(kst) or "",
entry_model=r["entry_model"] if "entry_model" in r.keys() else None,
trade_style=r["trade_style"] if "trade_style" in r.keys() else None,
)
snap = r["initial_stop_loss"] if r["initial_stop_loss"] not in (None, "") else r["stop_loss"]
data.append((
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
+7 -1
View File
@@ -183,6 +183,7 @@ from lib.trade.entry_model_lib import (
open_position_button_label,
parse_manual_order_style_fields,
resolve_effective_trade_entry_reason,
format_entry_type_display,
resolve_trade_record_entry_reason,
trend_manual_entry_reason_count,
)
@@ -2022,6 +2023,7 @@ def to_effective_trade_dict(row):
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
reviewed_entry_reason=reviewed_er,
entry_reason=item.get("entry_reason"),
entry_model=item.get("entry_model"),
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
monitor_type=item.get("monitor_type"),
trade_style=item.get("trade_style"),
@@ -8276,7 +8278,11 @@ def export_trade_records():
er0 = (r["entry_reason"] or "").strip() if r["entry_reason"] else ""
er1 = (r["reviewed_entry_reason"] or "").strip() if r["reviewed_entry_reason"] else ""
kst = (r["key_signal_type"] or "").strip() if "key_signal_type" in r.keys() else ""
eff = er1 or er0 or entry_reason_from_key_signal(kst) or ""
eff = format_entry_type_display(
er1 or er0 or entry_reason_from_key_signal(kst) or "",
entry_model=r["entry_model"] if "entry_model" in r.keys() else None,
trade_style=r["trade_style"] if "trade_style" in r.keys() else None,
)
snap = r["initial_stop_loss"] if r["initial_stop_loss"] not in (None, "") else r["stop_loss"]
data.append((
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
+8 -1
View File
@@ -10,6 +10,7 @@ from lib.strategy.strategy_trade_labels import (
entry_reason_for_monitor_type,
)
from lib.trade.time_close_lib import TIME_CLOSE_RESULT
from lib.trade.entry_model_lib import format_entry_type_display
TRADE_COMPLETED_RESULTS = (
"止盈",
@@ -132,7 +133,13 @@ def display_entry_type_label(d: dict) -> str:
label = effective_entry_type(d).strip()
if not label:
return ""
return _normalize_monitor_type_label(label) or label
formatted = format_entry_type_display(
label,
entry_model=d.get("entry_model"),
trade_style=d.get("trade_style"),
)
out = _normalize_monitor_type_label(formatted) or formatted
return out or ""
def effective_hold_minutes(
+65 -8
View File
@@ -223,6 +223,55 @@ def entry_model_label(code: Optional[str]) -> str:
return _CODE_TO_LABEL.get(c, "")
def entry_category_display_prefix(category: str) -> str:
"""两级展示用的一级前缀:反转 / 顺势 / 波段单(含日内)。"""
cat = (category or "").strip()
if cat in (ENTRY_CATEGORY_SWING, ENTRY_CATEGORY_INTRADAY):
return "波段单"
return ENTRY_CATEGORY_LABELS.get(cat, "")
def entry_model_display_label(code: Optional[str]) -> str:
"""两级展示:反转/启动A、顺势/大分歧A、波段单/小分歧、波段单/假破。"""
c = normalize_entry_model_code(code)
if not c:
return ""
label = entry_model_label(c)
if not label:
return ""
prefix = entry_category_display_prefix(entry_model_category(c))
if prefix:
return f"{prefix}/{label}"
return label
def format_entry_type_display(
text: Optional[str] = None,
*,
entry_model: Optional[str] = None,
trade_style: Optional[str] = None,
) -> str:
"""交易记录/持仓展示:已知 entry_model 或短标签 → 两级文案。"""
if entry_model:
disp = entry_model_display_label(entry_model)
if disp:
return disp
raw = (text or "").strip()
if not raw:
ts = (trade_style or "").strip().lower()
if ts in ("trend", "swing"):
return trade_style_label_zh(ts)
return ""
if "/" in raw:
return raw
code = normalize_entry_model_code(raw)
if code:
disp = entry_model_display_label(code)
if disp:
return disp
return raw
def trade_style_for_entry_model(code: Optional[str]) -> str:
c = normalize_entry_model_code(code)
return _CODE_TO_STYLE.get(c, "trend")
@@ -297,7 +346,7 @@ def resolve_trade_record_entry_reason(
er = (entry_reason or "").strip()
if er:
return er
label = entry_model_label(entry_model)
label = entry_model_display_label(entry_model)
if label:
return label
kst = (key_signal_type or "").strip()
@@ -330,14 +379,22 @@ def resolve_effective_trade_entry_reason(
for raw in (reviewed_entry_reason, entry_reason):
er = (raw or "").strip()
if er:
return er
return resolve_trade_record_entry_reason(
return format_entry_type_display(
er,
entry_model=entry_model,
trade_style=trade_style,
)
return format_entry_type_display(
resolve_trade_record_entry_reason(
entry_model=entry_model,
key_signal_type=key_signal_type,
monitor_type=monitor_type,
trade_style=trade_style,
entry_reason_from_key_signal=entry_reason_from_key_signal,
entry_reason_for_monitor_type=entry_reason_for_monitor_type,
),
entry_model=entry_model,
key_signal_type=key_signal_type,
monitor_type=monitor_type,
trade_style=trade_style,
entry_reason_from_key_signal=entry_reason_from_key_signal,
entry_reason_for_monitor_type=entry_reason_for_monitor_type,
)
@@ -345,7 +402,7 @@ def enrich_entry_model_display(item: dict) -> dict:
code = normalize_entry_model_code(item.get("entry_model"))
if code:
item["entry_model"] = code
item["entry_model_label"] = entry_model_label(code)
item["entry_model_label"] = entry_model_display_label(code)
cat = entry_model_category(code)
if cat:
item["entry_model_category"] = cat
+11 -2
View File
@@ -11,7 +11,9 @@ from lib.trade.entry_model_lib import (
build_trend_div_entry_reason_options,
entry_model_categories,
entry_model_category,
entry_model_display_label,
entry_model_label,
format_entry_type_display,
hub_meta_entry_context,
intraday_entry_model_options,
is_intraday_trading_profile,
@@ -123,9 +125,16 @@ class TestEntryModelLib(unittest.TestCase):
def test_resolve_entry_reason_from_model(self):
er = resolve_trade_record_entry_reason(entry_model=ENTRY_MODEL_BIG_DIV_B)
self.assertEqual(er, "大分歧B")
self.assertEqual(er, "顺势/大分歧B")
er2 = resolve_trade_record_entry_reason(entry_model=ENTRY_MODEL_LAUNCH_A)
self.assertEqual(er2, "启动A")
self.assertEqual(er2, "反转/启动A")
def test_entry_model_display_label(self):
self.assertEqual(entry_model_display_label(ENTRY_MODEL_LAUNCH_A), "反转/启动A")
self.assertEqual(entry_model_display_label(ENTRY_MODEL_SMALL_DIV), "波段单/小分歧")
self.assertEqual(entry_model_display_label("liquidity_false_break"), "波段单/假破")
self.assertEqual(format_entry_type_display("启动A"), "反转/启动A")
self.assertEqual(entry_model_label(ENTRY_MODEL_LAUNCH_B), "启动B")
def test_resolve_entry_reason_trade_style_fallback(self):
er = resolve_trade_record_entry_reason(trade_style="swing")