This commit is contained in:
2026-09-15 20:02:05 +08:00
parent bfe89ba122
commit 04daeff141
37 changed files with 2674 additions and 1426 deletions

View File

@@ -1,5 +1,5 @@
import socket
from dataclasses import dataclass, field
from dataclasses import dataclass, field, fields
from pathlib import Path
import yaml
@@ -43,9 +43,7 @@ class AccountConfig:
host_key: str = ""
buy_value: float = 0
min_cash_ratio: float = 0
loss_trigger_pct: float = 0
grid_step_pct: float = 1
min_profit_pct: float = 0
enable_loss_add_position: bool = False
enable_auto_ipo: bool = True
signal_allow: list[str] = field(default_factory=list)
@@ -55,6 +53,10 @@ class AccountConfig:
zt_sell_ratio: float = 0.5
zt_buy_fall_pct: float = 1.0
zt_max_price: float = 200.0
# 正T/反T 中性带:现价在建仓价 ±N% 内不动手,避免来回摩擦。
zt_t_band_pct: float = 1.0
# 单轮最长持有自然日;超期告警并放弃继续平仓,残量留作隔夜持仓。
zt_max_hold_days: int = 5
# 当前账户启用的策略名称,例如 trend。
strategy: str = ""
@@ -129,7 +131,7 @@ def load(
# 策略状态文件写入该目录,启动时提前确保目录存在。
Path(global_config.qmt_data_dir).mkdir(parents=True, exist_ok=True)
account_config = AccountConfig(**_yaml(root / account_file))
account_config = AccountConfig(**_account_values(root / account_file))
if account_config.buy_value <= 0 or account_config.grid_step_pct <= 0:
raise ValueError("buy_value、grid_step_pct 必须大于 0")
if type(account_config.zt_open_hands) is not int or account_config.zt_open_hands < 0:
@@ -138,6 +140,10 @@ def load(
raise ValueError("zt_sell_ratio 必须在 (0, 1] 区间")
if account_config.zt_buy_fall_pct <= 0 or account_config.zt_max_price <= 0:
raise ValueError("zt_buy_fall_pct、zt_max_price 必须大于 0")
if account_config.zt_t_band_pct < 0:
raise ValueError("zt_t_band_pct 不能为负数")
if type(account_config.zt_max_hold_days) is not int or account_config.zt_max_hold_days <= 0:
raise ValueError("zt_max_hold_days 必须为正整数")
if not account_config.strategy.strip():
raise ValueError("strategy 不能为空")
@@ -156,3 +162,18 @@ def _yaml(path: Path) -> dict:
return yaml.safe_load(handle) or {}
except (OSError, yaml.YAMLError) as exc:
raise ValueError(f"读取或解析配置 {path} 失败: {exc}") from exc
def _account_values(path: Path) -> dict:
"""读取账户配置,并拒绝拼错或已废弃的字段。
以前未知字段会被 ``AccountConfig(**raw)`` 抛成 TypeError绕开 main()
的异常分支并以裸 traceback 退出;这里改成带文件名的 ValueError。
"""
raw = _yaml(path)
if not isinstance(raw, dict):
raise ValueError(f"账户配置 {path} 的根节点必须是对象")
unknown = sorted(set(raw) - {item.name for item in fields(AccountConfig)})
if unknown:
raise ValueError(f"账户配置 {path} 存在未知字段: {', '.join(unknown)}")
return raw