添加后缀映射功能
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
# Real backtest smoke test for the symbol alias resolver.
|
||||
# Runs two backtests via the project's pipeline (INIGenerator + MT5AutoRunner):
|
||||
# 1) XAUUSD (no alias) -> INI Symbol=XAUUSD
|
||||
# 2) XAUUSD (alias -> XAUUSDc) -> INI Symbol=XAUUSDc
|
||||
# Success: two .htm reports in C:\Users\Administrator\Desktop\mt5-backtest\reports,
|
||||
# one with Symbol=XAUUSD, one with Symbol=XAUUSDc.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import shutil
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
PROJECT_ROOT = os.path.normpath(os.path.join(HERE, ".."))
|
||||
SCRIPTS = os.path.join(PROJECT_ROOT, "scripts")
|
||||
EXAMPLES_ROOT = os.path.normpath(os.path.join(PROJECT_ROOT, "..", "examples"))
|
||||
|
||||
TARGET_REPORTS = r"C:\Users\Administrator\Desktop\mt5-backtest\reports"
|
||||
MT5_PATH = r"C:\Program Files\MetaTrader 5 IC Markets Global\terminal64.exe"
|
||||
MT5_DATA_DIR = r"C:\Users\Administrator\AppData\Roaming\MetaQuotes\Terminal\010E047102812FC0C18890992854220E"
|
||||
MT5_TESTER_DIR = os.path.join(MT5_DATA_DIR, "MQL5", "Profiles", "Tester")
|
||||
|
||||
EA_NAME = "MACD_MA_XAUUSD_5M"
|
||||
EA_BASENAME = f"{EA_NAME}.ex5"
|
||||
EA_FULL_PATH = os.path.join(MT5_DATA_DIR, "MQL5", "Experts", EA_BASENAME)
|
||||
SET_SRC = os.path.join(EXAMPLES_ROOT, "configs", f"{EA_NAME}.set")
|
||||
SET_DEST_NAME = "MACD_MA_XAUUSD_5M_smoke.set"
|
||||
SET_DEST_PATH = os.path.join(MT5_TESTER_DIR, SET_DEST_NAME)
|
||||
|
||||
os.makedirs(TARGET_REPORTS, exist_ok=True)
|
||||
shutil.copy2(SET_SRC, SET_DEST_PATH)
|
||||
|
||||
sys.path.insert(0, SCRIPTS)
|
||||
from ini_generator import INIGenerator
|
||||
from mt5_auto_runner import MT5AutoRunner
|
||||
|
||||
|
||||
def build_cfg(aliases, out_ini_dir):
|
||||
return {
|
||||
"backtest_settings": {
|
||||
"currency": "USD",
|
||||
"date_range": {"from": "2026.05.01", "to": "2026.06.05"},
|
||||
"deposit": 10000,
|
||||
"execution_delay": 0,
|
||||
"forward_date": "",
|
||||
"forward_mode": 0,
|
||||
"leverage": "1:100",
|
||||
"model": 0,
|
||||
"optimization": 0,
|
||||
"replace_report": True,
|
||||
"shutdown_terminal": True,
|
||||
"symbols": ["XAUUSD"],
|
||||
"timeframes": ["H1"],
|
||||
"visual": 0,
|
||||
"symbol_aliases": aliases or {},
|
||||
},
|
||||
"eas": [
|
||||
{"name": EA_NAME, "filename": EA_FULL_PATH, "set_file": SET_DEST_NAME},
|
||||
],
|
||||
"mt5_settings": {
|
||||
"terminal_path": MT5_PATH,
|
||||
"data_dir": MT5_DATA_DIR,
|
||||
"ini_dir": out_ini_dir,
|
||||
"reports_dir": TARGET_REPORTS,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def patch_ini_to_inline(ini_path):
|
||||
"""Patch the INI to use relative Expert + inline [TesterInputs]
|
||||
(the format MT5 actually auto-starts with on this terminal)."""
|
||||
with open(ini_path, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
text = text.replace(f"Expert={EA_FULL_PATH}", f"Expert={EA_BASENAME}")
|
||||
text = text.replace(f"ExpertParameters={SET_DEST_NAME}", "ExpertParameters=")
|
||||
with open(SET_DEST_PATH, encoding="utf-8", errors="ignore") as f:
|
||||
set_text = f.read()
|
||||
if not text.rstrip().endswith("[TesterInputs]"):
|
||||
text = text.rstrip() + "\n\n[TesterInputs]\n" + set_text
|
||||
with open(ini_path, "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
|
||||
|
||||
def run_one(label, aliases, out_dir):
|
||||
cfg = build_cfg(aliases=aliases, out_ini_dir=out_dir)
|
||||
gen = INIGenerator(cfg)
|
||||
ini_files = gen.generate_ini_files()
|
||||
assert len(ini_files) == 1, f"{label}: expected 1 INI, got {len(ini_files)}"
|
||||
ini = ini_files[0]
|
||||
patch_ini_to_inline(ini)
|
||||
with open(ini, encoding="utf-8") as f:
|
||||
sym_line = next(l for l in f if l.startswith("Symbol="))
|
||||
print(f"[{label}] INI Symbol line: {sym_line.strip()}")
|
||||
|
||||
runner = MT5AutoRunner(cfg)
|
||||
res = runner._execute_single_ini(ini, timeout_min=15)
|
||||
print(f"[{label}] runner status: {res['status']}, report_path: {res.get('report_path')}")
|
||||
return ini, res
|
||||
|
||||
|
||||
def main():
|
||||
tmp_root = os.path.join(PROJECT_ROOT, "config", "generated_smoke")
|
||||
if os.path.exists(tmp_root):
|
||||
shutil.rmtree(tmp_root)
|
||||
os.makedirs(tmp_root, exist_ok=True)
|
||||
out1 = os.path.join(tmp_root, "no_alias"); os.makedirs(out1, exist_ok=True)
|
||||
out2 = os.path.join(tmp_root, "with_alias"); os.makedirs(out2, exist_ok=True)
|
||||
|
||||
print("=== run 1: no alias (should pick XAUUSD) ===")
|
||||
ini1, r1 = run_one("no_alias", aliases=None, out_dir=out1)
|
||||
print()
|
||||
print("=== run 2: alias XAUUSD -> XAUUSDc (should pick XAUUSDc) ===")
|
||||
ini2, r2 = run_one("with_alias", aliases={"XAUUSD": "XAUUSDc"}, out_dir=out2)
|
||||
print()
|
||||
|
||||
print("=== summary ===")
|
||||
print(f" INI1 ({os.path.basename(ini1)}) -> {r1['status']}, {r1.get('report_path')}")
|
||||
print(f" INI2 ({os.path.basename(ini2)}) -> {r2['status']}, {r2.get('report_path')}")
|
||||
|
||||
reports = sorted([f for f in os.listdir(TARGET_REPORTS)
|
||||
if f.startswith('MACD_MA_XAUUSD_5M_') and f.endswith('.htm')])
|
||||
print(f"\nReports in {TARGET_REPORTS}:")
|
||||
found = {"XAUUSD": [], "XAUUSDc": []}
|
||||
for r in reports:
|
||||
path = os.path.join(TARGET_REPORTS, r)
|
||||
size = os.path.getsize(path)
|
||||
# MT5 saves reports as UTF-16 LE with BOM
|
||||
text = open(path, encoding='utf-16', errors='ignore').read()
|
||||
m = re.search(r'交易品种:</td>\s*<td[^>]*><b>([^<]+)</b>', text)
|
||||
sym = m.group(1) if m else "?"
|
||||
found[sym].append(r)
|
||||
print(f" - {r} ({size} bytes) symbol={sym}")
|
||||
|
||||
print(f"\nDistinct symbols found: {sorted({k for k, v in found.items() if v})}")
|
||||
if not found["XAUUSD"]:
|
||||
raise AssertionError("No report with Symbol=XAUUSD")
|
||||
if not found["XAUUSDc"]:
|
||||
raise AssertionError("No report with Symbol=XAUUSDc")
|
||||
print("SUCCESS: both XAUUSD and XAUUSDc reports present in target dir.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user