feat: show entry type as category/subtype (反转/启动A)
Format持仓与交易记录开仓类型为两级展示;新平仓写入与旧短标签自动展开。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -185,6 +185,7 @@ from lib.trade.entry_model_lib import (
|
|||||||
open_position_button_label,
|
open_position_button_label,
|
||||||
parse_manual_order_style_fields,
|
parse_manual_order_style_fields,
|
||||||
resolve_effective_trade_entry_reason,
|
resolve_effective_trade_entry_reason,
|
||||||
|
format_entry_type_display,
|
||||||
resolve_trade_record_entry_reason,
|
resolve_trade_record_entry_reason,
|
||||||
trend_manual_entry_reason_count,
|
trend_manual_entry_reason_count,
|
||||||
)
|
)
|
||||||
@@ -2115,6 +2116,7 @@ def to_effective_trade_dict(row):
|
|||||||
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
|
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
|
||||||
reviewed_entry_reason=reviewed_er,
|
reviewed_entry_reason=reviewed_er,
|
||||||
entry_reason=item.get("entry_reason"),
|
entry_reason=item.get("entry_reason"),
|
||||||
|
entry_model=item.get("entry_model"),
|
||||||
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
|
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
|
||||||
monitor_type=item.get("monitor_type"),
|
monitor_type=item.get("monitor_type"),
|
||||||
trade_style=item.get("trade_style"),
|
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 ""
|
er0 = (r["entry_reason"] or "").strip() if r["entry_reason"] else ""
|
||||||
er1 = (r["reviewed_entry_reason"] or "").strip() if r["reviewed_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 ""
|
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"]
|
snap = r["initial_stop_loss"] if r["initial_stop_loss"] not in (None, "") else r["stop_loss"]
|
||||||
data.append((
|
data.append((
|
||||||
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
|
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ from lib.trade.entry_model_lib import (
|
|||||||
open_position_button_label,
|
open_position_button_label,
|
||||||
parse_manual_order_style_fields,
|
parse_manual_order_style_fields,
|
||||||
resolve_effective_trade_entry_reason,
|
resolve_effective_trade_entry_reason,
|
||||||
|
format_entry_type_display,
|
||||||
resolve_trade_record_entry_reason,
|
resolve_trade_record_entry_reason,
|
||||||
trend_manual_entry_reason_count,
|
trend_manual_entry_reason_count,
|
||||||
)
|
)
|
||||||
@@ -2075,6 +2076,7 @@ def to_effective_trade_dict(row):
|
|||||||
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
|
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
|
||||||
reviewed_entry_reason=reviewed_er,
|
reviewed_entry_reason=reviewed_er,
|
||||||
entry_reason=item.get("entry_reason"),
|
entry_reason=item.get("entry_reason"),
|
||||||
|
entry_model=item.get("entry_model"),
|
||||||
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
|
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
|
||||||
monitor_type=item.get("monitor_type"),
|
monitor_type=item.get("monitor_type"),
|
||||||
trade_style=item.get("trade_style"),
|
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 ""
|
er0 = (r["entry_reason"] or "").strip() if r["entry_reason"] else ""
|
||||||
er1 = (r["reviewed_entry_reason"] or "").strip() if r["reviewed_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 ""
|
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"]
|
snap = r["initial_stop_loss"] if r["initial_stop_loss"] not in (None, "") else r["stop_loss"]
|
||||||
data.append((
|
data.append((
|
||||||
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
|
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ from lib.trade.entry_model_lib import (
|
|||||||
open_position_button_label,
|
open_position_button_label,
|
||||||
parse_manual_order_style_fields,
|
parse_manual_order_style_fields,
|
||||||
resolve_effective_trade_entry_reason,
|
resolve_effective_trade_entry_reason,
|
||||||
|
format_entry_type_display,
|
||||||
resolve_trade_record_entry_reason,
|
resolve_trade_record_entry_reason,
|
||||||
trend_manual_entry_reason_count,
|
trend_manual_entry_reason_count,
|
||||||
)
|
)
|
||||||
@@ -2022,6 +2023,7 @@ def to_effective_trade_dict(row):
|
|||||||
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
|
item["effective_entry_reason"] = resolve_effective_trade_entry_reason(
|
||||||
reviewed_entry_reason=reviewed_er,
|
reviewed_entry_reason=reviewed_er,
|
||||||
entry_reason=item.get("entry_reason"),
|
entry_reason=item.get("entry_reason"),
|
||||||
|
entry_model=item.get("entry_model"),
|
||||||
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
|
key_signal_type=(item.get("key_signal_type") or "").strip() or None,
|
||||||
monitor_type=item.get("monitor_type"),
|
monitor_type=item.get("monitor_type"),
|
||||||
trade_style=item.get("trade_style"),
|
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 ""
|
er0 = (r["entry_reason"] or "").strip() if r["entry_reason"] else ""
|
||||||
er1 = (r["reviewed_entry_reason"] or "").strip() if r["reviewed_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 ""
|
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"]
|
snap = r["initial_stop_loss"] if r["initial_stop_loss"] not in (None, "") else r["stop_loss"]
|
||||||
data.append((
|
data.append((
|
||||||
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
|
r["id"], r["symbol"], r["monitor_type"], kst, r["direction"], r["trigger_price"],
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from lib.strategy.strategy_trade_labels import (
|
|||||||
entry_reason_for_monitor_type,
|
entry_reason_for_monitor_type,
|
||||||
)
|
)
|
||||||
from lib.trade.time_close_lib import TIME_CLOSE_RESULT
|
from lib.trade.time_close_lib import TIME_CLOSE_RESULT
|
||||||
|
from lib.trade.entry_model_lib import format_entry_type_display
|
||||||
|
|
||||||
TRADE_COMPLETED_RESULTS = (
|
TRADE_COMPLETED_RESULTS = (
|
||||||
"止盈",
|
"止盈",
|
||||||
@@ -132,7 +133,13 @@ def display_entry_type_label(d: dict) -> str:
|
|||||||
label = effective_entry_type(d).strip()
|
label = effective_entry_type(d).strip()
|
||||||
if not label:
|
if not label:
|
||||||
return "—"
|
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(
|
def effective_hold_minutes(
|
||||||
|
|||||||
@@ -223,6 +223,55 @@ def entry_model_label(code: Optional[str]) -> str:
|
|||||||
return _CODE_TO_LABEL.get(c, "")
|
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:
|
def trade_style_for_entry_model(code: Optional[str]) -> str:
|
||||||
c = normalize_entry_model_code(code)
|
c = normalize_entry_model_code(code)
|
||||||
return _CODE_TO_STYLE.get(c, "trend")
|
return _CODE_TO_STYLE.get(c, "trend")
|
||||||
@@ -297,7 +346,7 @@ def resolve_trade_record_entry_reason(
|
|||||||
er = (entry_reason or "").strip()
|
er = (entry_reason or "").strip()
|
||||||
if er:
|
if er:
|
||||||
return er
|
return er
|
||||||
label = entry_model_label(entry_model)
|
label = entry_model_display_label(entry_model)
|
||||||
if label:
|
if label:
|
||||||
return label
|
return label
|
||||||
kst = (key_signal_type or "").strip()
|
kst = (key_signal_type or "").strip()
|
||||||
@@ -330,14 +379,22 @@ def resolve_effective_trade_entry_reason(
|
|||||||
for raw in (reviewed_entry_reason, entry_reason):
|
for raw in (reviewed_entry_reason, entry_reason):
|
||||||
er = (raw or "").strip()
|
er = (raw or "").strip()
|
||||||
if er:
|
if er:
|
||||||
return er
|
return format_entry_type_display(
|
||||||
return resolve_trade_record_entry_reason(
|
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,
|
entry_model=entry_model,
|
||||||
key_signal_type=key_signal_type,
|
|
||||||
monitor_type=monitor_type,
|
|
||||||
trade_style=trade_style,
|
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"))
|
code = normalize_entry_model_code(item.get("entry_model"))
|
||||||
if code:
|
if code:
|
||||||
item["entry_model"] = 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)
|
cat = entry_model_category(code)
|
||||||
if cat:
|
if cat:
|
||||||
item["entry_model_category"] = cat
|
item["entry_model_category"] = cat
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ from lib.trade.entry_model_lib import (
|
|||||||
build_trend_div_entry_reason_options,
|
build_trend_div_entry_reason_options,
|
||||||
entry_model_categories,
|
entry_model_categories,
|
||||||
entry_model_category,
|
entry_model_category,
|
||||||
|
entry_model_display_label,
|
||||||
entry_model_label,
|
entry_model_label,
|
||||||
|
format_entry_type_display,
|
||||||
hub_meta_entry_context,
|
hub_meta_entry_context,
|
||||||
intraday_entry_model_options,
|
intraday_entry_model_options,
|
||||||
is_intraday_trading_profile,
|
is_intraday_trading_profile,
|
||||||
@@ -123,9 +125,16 @@ class TestEntryModelLib(unittest.TestCase):
|
|||||||
|
|
||||||
def test_resolve_entry_reason_from_model(self):
|
def test_resolve_entry_reason_from_model(self):
|
||||||
er = resolve_trade_record_entry_reason(entry_model=ENTRY_MODEL_BIG_DIV_B)
|
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)
|
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):
|
def test_resolve_entry_reason_trade_style_fallback(self):
|
||||||
er = resolve_trade_record_entry_reason(trade_style="swing")
|
er = resolve_trade_record_entry_reason(trade_style="swing")
|
||||||
|
|||||||
Reference in New Issue
Block a user