feat: two-level entry model UI for trend accounts (reversal/trend/swing)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""大分歧 / 小分歧开仓类型(趋势户);日内户沿用 trade_style,单独 profile。"""
|
||||
"""趋势户开仓类型:反转·启动 / 顺势·大分歧 / 波段·小分歧;日内户单独 profile。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
@@ -9,19 +9,35 @@ from lib.trade.trade_policy_lib import TradePolicy
|
||||
PROFILE_TREND_DIV = "trend_div"
|
||||
PROFILE_INTRADAY = "intraday"
|
||||
|
||||
ENTRY_CATEGORY_REVERSAL = "reversal"
|
||||
ENTRY_CATEGORY_TREND = "trend"
|
||||
ENTRY_CATEGORY_SWING = "swing"
|
||||
|
||||
ENTRY_MODEL_LAUNCH_A = "launch_a"
|
||||
ENTRY_MODEL_LAUNCH_B = "launch_b"
|
||||
ENTRY_MODEL_BIG_DIV_A = "big_div_a"
|
||||
ENTRY_MODEL_BIG_DIV_B = "big_div_b"
|
||||
ENTRY_MODEL_SMALL_DIV = "small_div"
|
||||
|
||||
VALID_ENTRY_MODEL_CODES = frozenset(
|
||||
{
|
||||
ENTRY_MODEL_LAUNCH_A,
|
||||
ENTRY_MODEL_LAUNCH_B,
|
||||
ENTRY_MODEL_BIG_DIV_A,
|
||||
ENTRY_MODEL_BIG_DIV_B,
|
||||
ENTRY_MODEL_SMALL_DIV,
|
||||
}
|
||||
)
|
||||
|
||||
ENTRY_CATEGORY_LABELS: dict[str, str] = {
|
||||
ENTRY_CATEGORY_REVERSAL: "反转",
|
||||
ENTRY_CATEGORY_TREND: "顺势",
|
||||
ENTRY_CATEGORY_SWING: "波段",
|
||||
}
|
||||
|
||||
TREND_DIV_ENTRY_REASON_LABELS: Tuple[str, ...] = (
|
||||
"启动A",
|
||||
"启动B",
|
||||
"大分歧A",
|
||||
"大分歧B",
|
||||
"小分歧",
|
||||
@@ -35,31 +51,56 @@ INTRADAY_LEGACY_TREND_ENTRY_REASONS: Tuple[str, ...] = (
|
||||
"波段单:5m顺势突破,确认条件:2根k线+成交量放大+4h同向+日成交量前20",
|
||||
)
|
||||
|
||||
_ENTRY_SPECS: Tuple[Tuple[str, str, str, str], ...] = (
|
||||
# code, label, category, trade_style, help
|
||||
_ENTRY_SPECS: Tuple[Tuple[str, str, str, str, str], ...] = (
|
||||
(
|
||||
ENTRY_MODEL_LAUNCH_A,
|
||||
"启动A",
|
||||
ENTRY_CATEGORY_REVERSAL,
|
||||
"trend",
|
||||
"反转链结构内:摸参考极值前小收敛,或 B 失败后 5m N 字试仓(不单列)",
|
||||
),
|
||||
(
|
||||
ENTRY_MODEL_LAUNCH_B,
|
||||
"启动B",
|
||||
ENTRY_CATEGORY_REVERSAL,
|
||||
"trend",
|
||||
"第二次到参考极值附近、无小收敛时的实体突破",
|
||||
),
|
||||
(
|
||||
ENTRY_MODEL_BIG_DIV_A,
|
||||
"大分歧A",
|
||||
ENTRY_CATEGORY_TREND,
|
||||
"trend",
|
||||
"突破前收敛小结构,不创新低企稳(空:不创新高)",
|
||||
"主升已确立:突破前收敛,不创新低企稳(空:不创新高)",
|
||||
),
|
||||
(
|
||||
ENTRY_MODEL_BIG_DIV_B,
|
||||
"大分歧B",
|
||||
ENTRY_CATEGORY_TREND,
|
||||
"trend",
|
||||
"结构突破确认后入场",
|
||||
"主升已确立:结构实体突破确认后入场",
|
||||
),
|
||||
(
|
||||
ENTRY_MODEL_SMALL_DIV,
|
||||
"小分歧",
|
||||
ENTRY_CATEGORY_SWING,
|
||||
"swing",
|
||||
"二次探底 N 字突破,或 5m 三均线重新多头(空:二次探顶 / 空头均线)",
|
||||
"主升已确立:二次探底 N 字或 5m 三均线重新多头(空:二次探顶 / 空头均线)",
|
||||
),
|
||||
)
|
||||
|
||||
_CODE_TO_LABEL = {code: label for code, label, _, _ in _ENTRY_SPECS}
|
||||
_CODE_TO_STYLE = {code: style for code, _, style, _ in _ENTRY_SPECS}
|
||||
_LABEL_TO_CODE = {label: code for code, label, _, _ in _ENTRY_SPECS}
|
||||
_CODE_TO_HELP = {code: help for code, _, _, help in _ENTRY_SPECS}
|
||||
_CODE_TO_LABEL = {code: label for code, label, _, _, _ in _ENTRY_SPECS}
|
||||
_CODE_TO_STYLE = {code: style for code, _, _, style, _ in _ENTRY_SPECS}
|
||||
_CODE_TO_CATEGORY = {code: cat for code, _, cat, _, _ in _ENTRY_SPECS}
|
||||
_LABEL_TO_CODE = {label: code for code, label, _, _, _ in _ENTRY_SPECS}
|
||||
_CODE_TO_HELP = {code: help for code, _, _, _, help in _ENTRY_SPECS}
|
||||
|
||||
_CATEGORY_ORDER: Tuple[str, ...] = (
|
||||
ENTRY_CATEGORY_REVERSAL,
|
||||
ENTRY_CATEGORY_TREND,
|
||||
ENTRY_CATEGORY_SWING,
|
||||
)
|
||||
|
||||
_INTRADAY_WHITELIST = frozenset({"BTC", "ETH"})
|
||||
|
||||
@@ -68,6 +109,7 @@ _INTRADAY_WHITELIST = frozenset({"BTC", "ETH"})
|
||||
class EntryModelOption:
|
||||
code: str
|
||||
label: str
|
||||
category: str
|
||||
trade_style: str
|
||||
help: str
|
||||
|
||||
@@ -87,11 +129,43 @@ def order_entry_profile(policy: TradePolicy) -> str:
|
||||
|
||||
def entry_model_options() -> Tuple[EntryModelOption, ...]:
|
||||
return tuple(
|
||||
EntryModelOption(code=code, label=label, trade_style=style, help=help)
|
||||
for code, label, style, help in _ENTRY_SPECS
|
||||
EntryModelOption(code=code, label=label, category=cat, trade_style=style, help=help)
|
||||
for code, label, cat, style, help in _ENTRY_SPECS
|
||||
)
|
||||
|
||||
|
||||
def entry_model_categories() -> list[dict[str, Any]]:
|
||||
"""两级 UI:反转 / 顺势 / 波段 → 子选项。"""
|
||||
opts = entry_model_options()
|
||||
out: list[dict[str, Any]] = []
|
||||
for cat_key in _CATEGORY_ORDER:
|
||||
children = [
|
||||
{
|
||||
"code": o.code,
|
||||
"label": o.label,
|
||||
"trade_style": o.trade_style,
|
||||
"help": o.help,
|
||||
}
|
||||
for o in opts
|
||||
if o.category == cat_key
|
||||
]
|
||||
if not children:
|
||||
continue
|
||||
out.append(
|
||||
{
|
||||
"key": cat_key,
|
||||
"label": ENTRY_CATEGORY_LABELS.get(cat_key, cat_key),
|
||||
"options": children,
|
||||
}
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
def entry_model_category(code: Optional[str]) -> str:
|
||||
c = normalize_entry_model_code(code)
|
||||
return _CODE_TO_CATEGORY.get(c, "")
|
||||
|
||||
|
||||
def normalize_entry_model_code(raw: Optional[str]) -> str:
|
||||
v = (raw or "").strip().lower()
|
||||
if v in VALID_ENTRY_MODEL_CODES:
|
||||
@@ -160,7 +234,7 @@ def parse_manual_order_style_fields(
|
||||
|
||||
entry_model = normalize_entry_model_code(form.get("entry_model"))
|
||||
if not entry_model:
|
||||
return "", None, "请选择开仓类型(大分歧A / 大分歧B / 小分歧)"
|
||||
return "", None, "请选择开仓类型(反转 / 顺势 / 波段)"
|
||||
trade_style = trade_style_for_entry_model(entry_model)
|
||||
return trade_style, entry_model, None
|
||||
|
||||
@@ -197,6 +271,10 @@ def enrich_entry_model_display(item: dict) -> dict:
|
||||
if code:
|
||||
item["entry_model"] = code
|
||||
item["entry_model_label"] = entry_model_label(code)
|
||||
cat = entry_model_category(code)
|
||||
if cat:
|
||||
item["entry_model_category"] = cat
|
||||
item["entry_model_category_label"] = ENTRY_CATEGORY_LABELS.get(cat, "")
|
||||
else:
|
||||
item.setdefault("entry_model_label", "")
|
||||
return item
|
||||
@@ -209,10 +287,18 @@ def order_entry_template_context(policy: TradePolicy) -> dict:
|
||||
"order_entry_profile": profile,
|
||||
"intraday_discipline": profile == PROFILE_INTRADAY,
|
||||
"entry_model_options": [
|
||||
{"code": o.code, "label": o.label, "trade_style": o.trade_style, "help": o.help}
|
||||
{
|
||||
"code": o.code,
|
||||
"label": o.label,
|
||||
"category": o.category,
|
||||
"trade_style": o.trade_style,
|
||||
"help": o.help,
|
||||
}
|
||||
for o in opts
|
||||
],
|
||||
"entry_model_categories": entry_model_categories(),
|
||||
"entry_model_trade_style_map": {o.code: o.trade_style for o in opts},
|
||||
"entry_model_code_to_category": {o.code: o.category for o in opts},
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user