74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
"""run_v1_backtest.py — 用 v1 公式对所有品种回测(单公式对比)"""
|
||
import sys, json, math
|
||
from pathlib import Path
|
||
import numpy as np
|
||
sys.path.insert(0, str(Path(__file__).parent))
|
||
|
||
from config import Config
|
||
from data_pipeline.data_manager import MT5DataManager
|
||
from data_pipeline.fetcher import MT5DataFetcher
|
||
from backtest_viz import BacktestEngine
|
||
from model_core.features import MT5FeatureEngineer
|
||
from model_core.vocab import FORMULA_VOCAB
|
||
|
||
# v1 公式(策略库 strategy_v1_20260702.json)
|
||
V1_FORMULA = [6, 44, 3, 39, 4, 29, 34, 27]
|
||
names = FORMULA_VOCAB.token_names
|
||
readable = ' -> '.join(names[t] for t in V1_FORMULA)
|
||
|
||
_H1_PER_YEAR = 6240
|
||
|
||
def sharpe(p): m=p.mean(); s=p.std(); return float(m/s*math.sqrt(_H1_PER_YEAR)) if s>1e-10 else 0
|
||
def sortino(p):
|
||
m=p.mean(); d=p[p<0]
|
||
ds=d.std() if len(d)>0 else 1e-10; ds=max(ds,abs(m),1e-10)
|
||
return float(np.clip(m/ds*math.sqrt(_H1_PER_YEAR),-20,20))
|
||
def mdd(c): return float((np.maximum.accumulate(c)-c).max())
|
||
|
||
# 真实点差
|
||
import MetaTrader5 as mt5
|
||
mt5.initialize()
|
||
costs = {}
|
||
for sym in Config.SYMBOLS:
|
||
tick = mt5.symbol_info_tick(sym)
|
||
if tick and tick.ask > 0:
|
||
mid = (tick.ask+tick.bid)/2
|
||
costs[sym] = (tick.ask-tick.bid)/mid/2
|
||
else:
|
||
costs[sym] = 0.0001
|
||
mt5.shutdown()
|
||
|
||
print(f"\nV1 公式(单公式,所有品种共用)")
|
||
print(f" {readable}\n")
|
||
|
||
with MT5DataFetcher() as f:
|
||
mgr = MT5DataManager(f)
|
||
mgr.load()
|
||
raw = mgr.raw_dict
|
||
syms = mgr.symbols
|
||
feat = MT5FeatureEngineer.compute_features(raw)
|
||
T = feat.shape[2]
|
||
|
||
print(f"{'品种':12s} {'PnL':>8} {'Sharpe':>8} {'Sortino':>8} {'MaxDD':>8} {'Trades':>7} {'AvgH':>6}")
|
||
print('─'*65)
|
||
|
||
all_pnls = []
|
||
for i, sym in enumerate(syms):
|
||
cr = costs.get(sym, 0.0001)
|
||
engine = BacktestEngine(formula=V1_FORMULA, cost_rate=cr)
|
||
res = engine.run({k: v[i:i+1] for k,v in raw.items()}, feat[i:i+1], [sym])
|
||
r = res[0]
|
||
pnl = r.pnl
|
||
cum = r.cum_pnl
|
||
all_pnls.append(pnl)
|
||
print(f"{sym:12s} {r.total_return:+8.3f} {sharpe(pnl):+8.3f} {sortino(pnl):+8.3f}"
|
||
f" {mdd(cum):8.3f} {r.n_trades:7d} {r.avg_hold_bars:6.1f}h")
|
||
|
||
# 组合
|
||
port = np.stack(all_pnls).mean(0)
|
||
cum_p = np.cumsum(port)
|
||
print('─'*65)
|
||
print(f"{'Portfolio':12s} {cum_p[-1]:+8.3f} {sharpe(port):+8.3f} {sortino(port):+8.3f}"
|
||
f" {mdd(cum_p):8.3f}")
|
||
print(f"\n 正收益品种: {sum(1 for p in all_pnls if np.cumsum(p)[-1]>0)}/{len(syms)}")
|