Turn position/margin/sizing/direction env fields into dropdown selects.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-17 19:41:47 +08:00
parent 53e55aa4b4
commit aabbbef0a9
5 changed files with 150 additions and 10 deletions
+59
View File
@@ -102,6 +102,28 @@ SENSITIVE_EXACT = frozenset({
SENSITIVE_SUBSTR = ("_SECRET", "_PASSPHRASE", "_API_KEY", "_PASSWORD")
# env 配置页下拉:value → 中文标签
SELECT_OPTIONS: dict[str, tuple[tuple[str, str], ...]] = {
"OKX_TD_MODE": (("cross", "全仓"), ("isolated", "逐仓")),
"OKX_POS_MODE": (("hedge", "双向"), ("net", "单向净持仓")),
"BINANCE_MARGIN_MODE": (("cross", "全仓"), ("isolated", "逐仓")),
"BINANCE_POSITION_MODE": (("hedge", "双向"), ("one_way", "单向")),
"GATE_TD_MODE": (("cross", "全仓"), ("isolated", "逐仓")),
"GATE_POS_MODE": (("hedge", "双向"), ("single", "单向")),
"POSITION_SIZING_MODE": (("risk", "以损定仓"), ("full_margin", "全仓杠杆")),
"TRADE_DIRECTION": (
("both", "双向均可"),
("long_only", "仅做多"),
("short_only", "仅做空"),
),
}
_SELECT_ALIASES: dict[str, dict[str, str]] = {
"OKX_TD_MODE": {"cross_margin": "cross", "isolated_margin": "isolated"},
"BINANCE_MARGIN_MODE": {"cross_margin": "cross", "isolated_margin": "isolated"},
"GATE_TD_MODE": {"cross_margin": "cross", "isolated_margin": "isolated"},
}
def _is_sensitive(key: str) -> bool:
if key in SENSITIVE_EXACT:
@@ -109,6 +131,27 @@ def _is_sensitive(key: str) -> bool:
return any(s in key for s in SENSITIVE_SUBSTR)
def select_options_for(key: str) -> list[dict[str, str]]:
opts = SELECT_OPTIONS.get(key) or ()
return [{"value": v, "label": lab} for v, lab in opts]
def normalize_select_value(key: str, value: Optional[str]) -> str:
raw = (value or "").strip()
if not raw:
return ""
low = raw.lower()
aliases = _SELECT_ALIASES.get(key) or {}
if low in aliases:
return aliases[low]
allowed = {v for v, _ in (SELECT_OPTIONS.get(key) or ())}
if low in allowed:
return low
if raw in allowed:
return raw
return raw
def _restart_required(key: str) -> bool:
if key in HOT_RELOAD_EXACT:
return False
@@ -126,6 +169,8 @@ def _hot_reload(key: str) -> bool:
def _field_type(key: str, value: str) -> str:
if key in SELECT_OPTIONS:
return "select"
low = (value or "").strip().lower()
if low in ("true", "false"):
return "bool"
@@ -286,6 +331,20 @@ def validate_env_updates(groups: list[dict], updates: dict[str, str]) -> tuple[d
errors.append(f"{key} 须为 true/false")
continue
val = "true" if low in ("true", "1", "yes", "on") else "false"
elif ftype == "select" or key in SELECT_OPTIONS:
allowed_vals = {
str(o.get("value") if isinstance(o, dict) else o[0]).lower()
for o in (allowed[key].get("options") or select_options_for(key))
}
norm = normalize_select_value(key, val)
if allowed_vals and norm.lower() not in allowed_vals:
labels = " / ".join(
f"{o['value']}({o['label']})" if isinstance(o, dict) else f"{o[0]}({o[1]})"
for o in (allowed[key].get("options") or select_options_for(key))
)
errors.append(f"{key} 须为: {labels}")
continue
val = norm
clean[key] = val
return clean, errors