回测基本一致
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"""Compare the dry-run MT5 report against Python on the SAME window + deposit.
|
||||
|
||||
The dry-run MT5 report shows the tester was run on 2026.04.16 - 2026.05.08
|
||||
with initial deposit 1000 USD. To make a fair Python-vs-MT5 comparison we
|
||||
re-slice the bars to that exact window and re-run the engine with the same
|
||||
deposit. Then we print the side-by-side table.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(PROJECT))
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from shared.core.engine import SizingInputs
|
||||
from shared.core.metrics import compute_metrics
|
||||
from shared.data.loaders import load_bars
|
||||
from shared.data.mt5_report import parse_mt5_report
|
||||
from shared.mt5_pipeline.compare import build_comparison_table
|
||||
from strategies.gold_scalper_pro.instruments import XAUUSD_REAL
|
||||
from strategies.gold_scalper_pro.scalper_engine import (
|
||||
ScalperEngine,
|
||||
engine_kwargs_from_params,
|
||||
)
|
||||
from strategies.gold_scalper_pro.search_space import FROZEN_BASELINE
|
||||
from strategies.gold_scalper_pro.signals import build_signals
|
||||
|
||||
|
||||
def main() -> int:
|
||||
# ── Parse the MT5 report ──────────────────────────────────────────────
|
||||
report = PROJECT / "reports" / "ReportTester-52845377.html"
|
||||
mt5 = parse_mt5_report(report)
|
||||
print("=== MT5 report (dry run) ===")
|
||||
for k, v in mt5.items():
|
||||
if k.startswith("_"):
|
||||
continue
|
||||
print(f" {k:24s}: {v}")
|
||||
# The MT5 window + deposit (parsed from the report's _all dict).
|
||||
all_fields = mt5["_all"]
|
||||
window_label = all_fields.get("期间:", "")
|
||||
print(f" window (raw) : {window_label}")
|
||||
deposit = mt5.get("Initial Deposit") or 1000
|
||||
print(f" initial deposit: {deposit}")
|
||||
|
||||
# ── Slice Python bars to the same window ─────────────────────────────
|
||||
bars = load_bars(PROJECT / "data" / "XAUUSD_M5_2024-06-26_2026-06-26.parquet")
|
||||
# MT5 tester's ToDate is exclusive of the day (uses 00:00 of that day),
|
||||
# so the actual data ends at 2026.05.08 00:00 (not 23:59). Match it exactly.
|
||||
start = pd.Timestamp("2026-04-16 00:00:00")
|
||||
end = pd.Timestamp("2026-05-08 00:00:00")
|
||||
window = bars[(bars["timestamp"] >= start) & (bars["timestamp"] < end)].reset_index(drop=True)
|
||||
print(f"\n=== Python (matched window) ===")
|
||||
print(f" bars : {len(window):,}")
|
||||
print(f" window : {window['timestamp'].iloc[0]} → {window['timestamp'].iloc[-1]}")
|
||||
print(f" deposit : {deposit}")
|
||||
|
||||
# ── Run the engine on the matched window ─────────────────────────────
|
||||
# Override InpAtrPeriod to 15 to match the MT5 manual run (user changed
|
||||
# it from the .set's 14 to 15 in the tester UI). RSI stays at 14.
|
||||
params = dict(FROZEN_BASELINE)
|
||||
params["InpAtrPeriod"] = 15
|
||||
pack = build_signals(params, window, XAUUSD_REAL)
|
||||
# Load M1 data for tick-level exit simulation (closes the bar-level
|
||||
# optimism gap on BE/trailing — doc 03 §7).
|
||||
m1_path = PROJECT / "data" / "XAUUSD_M1_2024-06-26_2026-06-26.parquet"
|
||||
m1_bars = load_bars(m1_path) if m1_path.exists() else None
|
||||
if m1_bars is not None:
|
||||
# Slice M1 to the same window as M5 (exclusive end, matching MT5).
|
||||
m1_bars = m1_bars[
|
||||
(m1_bars["timestamp"] >= start) & (m1_bars["timestamp"] < end)
|
||||
].reset_index(drop=True)
|
||||
print(f" m1 bars : {len(m1_bars):,} (tick-level exit simulation ON)")
|
||||
engine = ScalperEngine()
|
||||
result = engine.run(
|
||||
window, pack.signals_long, pack.signals_short,
|
||||
pack.sl_prices, pack.tp_prices,
|
||||
XAUUSD_REAL, SizingInputs(), float(deposit),
|
||||
m1_bars=m1_bars,
|
||||
**engine_kwargs_from_params(params),
|
||||
)
|
||||
py = compute_metrics(result)
|
||||
print(f" trades : {py.total_trades}")
|
||||
print(f" net : {py.net_profit:+.2f}")
|
||||
print(f" PF : {py.profit_factor:.2f}")
|
||||
print(f" DD : {py.max_equity_dd:.2f} ({py.max_equity_dd_pct:.2%})")
|
||||
|
||||
# ── Build the comparison table ───────────────────────────────────────
|
||||
# Map Python Metrics → keys the compare table expects.
|
||||
py_mapped = {
|
||||
"net_profit": py.net_profit,
|
||||
"profit_factor": py.profit_factor,
|
||||
"total_trades": py.total_trades,
|
||||
"max_equity_dd": py.max_equity_dd,
|
||||
"win_rate": py.win_rate,
|
||||
"sharpe": py.sharpe,
|
||||
}
|
||||
mt5_mapped = {
|
||||
"net_profit": mt5.get("Total Net Profit"),
|
||||
"profit_factor": mt5.get("Profit Factor"),
|
||||
"total_trades": mt5.get("Total Trades"),
|
||||
"max_equity_dd": mt5.get("Equity Drawdown Maximal"),
|
||||
"win_rate": None,
|
||||
"sharpe": mt5.get("Sharpe Ratio"),
|
||||
}
|
||||
print(f"\n=== Python vs MT5 (matched window {start.date()} → {end.date()}) ===")
|
||||
print(build_comparison_table(py_mapped, mt5_mapped))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user