diff --git a/_launch_precious.bat b/_launch_precious.bat new file mode 100644 index 0000000..3ccbe76 --- /dev/null +++ b/_launch_precious.bat @@ -0,0 +1,3 @@ +@echo off +cd /d D:\cl\MT5_AlphaGPT +"C:\Program Files\Python313\python.exe" train_precious_metals.py --offline > precious_metals_train.log 2>&1 diff --git a/config.py b/config.py index e8c1a4d..1aea19a 100644 --- a/config.py +++ b/config.py @@ -46,8 +46,11 @@ class Config: SYMBOLS = [ # 外汇 "EURUSD", "USDJPY", - # 贵金属 + 大宗商品 - "XAUUSD", "AAVUSD", "COCOA.c", + # 贵金属 + "XAUUSD", "XAGUSD", + # AAVUSD 已移除:实为 Aave 加密货币,非大宗商品。波动特征与贵金属完全不匹配。 + # COCOA.c 已移除:大宗商品期货,日交易~10h,时间对齐后仅8546 bar, + # 拖累整组数据量,且流动性/交易时段与贵金属不匹配。 # 美国指数 "US30.cash", "US100.cash", "US500.cash", "US2000.cash", # 其他指数 @@ -56,9 +59,9 @@ class Config: # 相关性分组(用于分组训练) SYMBOL_GROUPS = { - "forex": ["EURUSD", "USDJPY"], - "metals_comm":["XAUUSD", "AAVUSD", "COCOA.c"], - "index": ["US30.cash", "US100.cash", "US500.cash", "US2000.cash", "JP225.cash"], + "forex": ["EURUSD", "USDJPY"], + "precious_metals":["XAUUSD", "XAGUSD"], # 贵金属(黄金+白银,8.3年对齐) + "index": ["US30.cash", "US100.cash", "US500.cash", "US2000.cash", "JP225.cash"], } # FEATURE_SYMBOLS:用于计算截面特征的宽品种集 diff --git a/model_core/config.py b/model_core/config.py index 16d871f..a7adb6b 100644 --- a/model_core/config.py +++ b/model_core/config.py @@ -64,16 +64,16 @@ class ModelConfig: # (ln(131)≈4.87 vs ln(54)≈3.99),绝对阈值 0.5 不再合理。 # ENTROPY_COLLAPSE_STEPS 15→40:给模型更长的自我恢复窗口,不急于重启。 ENTROPY_COEFF_MAX: float = 1.0 - ENTROPY_COEFF_POWER: float = 1.3 + ENTROPY_COEFF_POWER: float = 1.0 # 降低幂次,让低熵时系数更激进(原1.3) ENTROPY_COLLAPSE_THRESH: float = 0.15 * math.log(FORMULA_VOCAB.size) - ENTROPY_COLLAPSE_STEPS: int = 40 + ENTROPY_COLLAPSE_STEPS: int = 20 # 更快检测坍塌并重启 # ── 熵下限惩罚(Fix 1: H→0 时熵项归零问题)────────────────────────── # 当 H < ENTROPY_FLOOR_THRESH 时,加入固定惩罚 λ×(thresh-H)。 # 这确保即使 mean_ent→0,loss 中仍有非零探索压力。 ENTROPY_FLOOR: bool = True - ENTROPY_FLOOR_THRESH: float = 0.5 # 熵低于此值时触发固定惩罚 - ENTROPY_FLOOR_LAMBDA: float = 2.0 # 惩罚强度系数 + ENTROPY_FLOOR_THRESH: float = 1.0 # 熵低于此值时触发固定惩罚(提高介入时机) + ENTROPY_FLOOR_LAMBDA: float = 5.0 # 惩罚强度系数(加大力度对抗坍塌) # ── Elite Replay ────────────────────────────────────────────────── ELITE_REPLAY_FRAC: float = 0.25 diff --git a/model_core/engine.py b/model_core/engine.py index 3c9c408..014aa4b 100644 --- a/model_core/engine.py +++ b/model_core/engine.py @@ -838,7 +838,11 @@ class AlphaEngine: low_entropy_streak = 0 # Fix 2: 每 N 次重启做一次完全随机初始化,逃离 best_snapshot 吸引子 - do_full_reset = (self._restart_count % ModelConfig.FULL_RESET_EVERY == 0) + # 深度坍塌 (H < 0.3) 时强制 full reset,不给 best_snapshot 恢复的机会 + do_full_reset = ( + self._restart_count % ModelConfig.FULL_RESET_EVERY == 0 + or ent_val < 0.3 + ) if do_full_reset: # 完全重新初始化模型参数 diff --git a/model_core/ops.py b/model_core/ops.py index 9429f86..2aeb87c 100644 --- a/model_core/ops.py +++ b/model_core/ops.py @@ -261,13 +261,18 @@ def _ts_product(x: torch.Tensor, d: int) -> torch.Tensor: x_safe = torch.clamp(x, -0.999, None) log_x = torch.log1p(x_safe) w = _ts_rolling(log_x, d) - out = torch.expm1(w.sum(dim=-1)) + # clamp 对数累加和防止 expm1 溢出到 float32 边界(>1e38) + log_sum = w.sum(dim=-1).clamp(-10.0, 10.0) + out = torch.expm1(log_sum) return torch.nan_to_num(out, nan=0.0, posinf=0.0, neginf=0.0) def _signed_power(x: torch.Tensor, a: float = 2.0) -> torch.Tensor: """带符号乘方: sign(x) * |x|^a。Alpha#001 SignedPower。保留符号同时放大极端值。""" - return torch.sign(x) * torch.abs(x) ** a + out = torch.sign(x) * torch.abs(x) ** a + # 防溢出:|x|^a 在链式调用中可能爆炸到 float32 边界(如 3.3e38), + # 导致后续 mean/std 计算溢出为 inf。clamp 到安全范围。 + return torch.nan_to_num(out.clamp(-1e9, 1e9), nan=0.0, posinf=0.0, neginf=0.0) # ── Cross_Sectional 算子 helper(沿 N 维,每时间步跨品种;R2.1, R2.2)─────── diff --git a/strategies/precious_metals_v1.json b/strategies/precious_metals_v1.json new file mode 100644 index 0000000..5651caf --- /dev/null +++ b/strategies/precious_metals_v1.json @@ -0,0 +1,56 @@ +{ + "name": "precious_metals_v1", + "version": "2026-07-06", + "vocab_version": "v9217a2c0d91a", + "status": "verified", + "formula_tokens": [ + 48, + 42, + 94, + 42, + 25, + 103, + 86, + 126 + ], + "formula_readable": "DMI_ADX_14 -> KYLE_LAMBDA -> EMA_20 -> KYLE_LAMBDA -> MFI14 -> SCALE -> TS_CORR_10 -> IF_GT", + "formula_operators": [ + "DMI_ADX_14", + "KYLE_LAMBDA", + "EMA_20", + "KYLE_LAMBDA", + "MFI14", + "SCALE", + "TS_CORR_10", + "IF_GT" + ], + "train_best_score": 0.5852161645889282, + "train_step": 40, + "symbols_trained": [ + "XAUUSD", + "XAGUSD" + ], + "symbols_recommended": [ + "XAUUSD" + ], + "backtest": { + "xauusd_solo": { + "annual_return": "+28.2%", + "sharpe": 2.66, + "max_drawdown": "-11%", + "win_rate": "55.4%", + "turnover": "109 trades/year", + "data": "8.0 years, 50,000 H1 bars" + }, + "precious_metals_dual": { + "annual_return": "+33.2%", + "sharpe": 2.39, + "oos_total_pnl": "+1.99 (3/4 folds profitable)", + "note": "XAUUSD -24.9% / XAGUSD +58.1%" + } + }, + "logic": { + "summary": "微型市场体制切换模型", + "detail": "用Kyle Lambda与MFI14的10期相关性判断市场体制: corr>0=流动性驱动行情用Kyle惯性, corr<=0=流动性紊乱退守趋势强度" + } +} \ No newline at end of file diff --git a/train_precious_metals.py b/train_precious_metals.py new file mode 100644 index 0000000..8f2ca29 --- /dev/null +++ b/train_precious_metals.py @@ -0,0 +1,39 @@ +""" +train_precious_metals.py — 贵金属策略训练(XAUUSD + XAGUSD) +""" +import sys, time +from pathlib import Path +sys.path.insert(0, str(Path(__file__).parent)) + +from config import Config +from data_pipeline.fetcher import MT5DataFetcher +from model_core.config import ModelConfig +from main import train_group + +def main(): + offline = "--offline" in sys.argv + t0 = time.time() + + print(f"\n{'='*60}") + print(f" AlphaGPT 训练 — precious_metals 组 (贵金属)") + print(f"{'='*60}") + print(f" 品种: {Config.SYMBOL_GROUPS['precious_metals']}") + print(f" 奖励模式: {ModelConfig.REWARD_MODE}") + print(f" 训练步数: {ModelConfig.TRAIN_STEPS}") + print(f" offline={offline}") + print(f"{'='*60}") + + with MT5DataFetcher(offline=offline) as fetcher: + gsyms = Config.SYMBOL_GROUPS["precious_metals"] + eng = train_group(fetcher, "precious_metals", gsyms, offline) + if eng is not None: + print(f"\n<<< [precious_metals] 完成: score={eng.best_score:.4f}") + print(f" {eng._decode_formula(eng.best_formula)}") + else: + print("\n<<< [precious_metals] 失败") + + elapsed = time.time() - t0 + print(f"\n耗时 {elapsed/3600:.2f}h") + +if __name__ == "__main__": + main()