Files
2026-06-26 20:50:07 +08:00

83 lines
2.9 KiB
Python

"""tester.ini generator (doc 07 §2b).
A small INI tells the tester *what* to run: expert, symbol, period, tick
model, date range, deposit, leverage, report name, and ``ShutdownTerminal=1``
so the terminal closes itself when done (the bridge then knows it's finished).
"""
from __future__ import annotations
import configparser
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
DEFAULT_MT5_INSTALL = r"C:\Program Files\MetaTrader 5 IC Markets Global"
# Tick models (doc 07 §2b):
# 0 = Every tick based on real ticks (highest accuracy, slowest)
# 1 = Every tick (generated from M1)
# 2 = 1-minute OHLC (good for non-tick-sensitive, fast) — routine verification
# 4 = Open prices only (rough, fastest)
MODEL_OHLC = 2
MODEL_EVERY_TICK = 1
MODEL_REAL_TICKS = 0
MODEL_OPEN_PRICES = 4
@dataclass
class TesterConfig:
"""Inputs for one tester run (doc 07 §2b).
``FromDate`` / ``ToDate`` are formatted ``YYYY.MM.DD`` for MT5. The login
section lives separately (kept out of code via the env file, doc 07 §5).
"""
expert: str # e.g. "Experts\\MyEA.ex5"
symbol: str
period: str = "H1" # chart timeframe (≤ the EA's signal timeframe)
model: int = MODEL_OHLC # tick model
from_date: str = "2024.01.01" # YYYY.MM.DD
to_date: str = "2026.01.01"
deposit: float = 10000.0
leverage: int = 100
report: str = "report_myrun" # output HTML name (no extension)
shutdown_terminal: bool = True
set_file: Optional[str] = None # path to the .set, or None to inline
login: Optional[int] = None # from env, never hard-coded
password: Optional[str] = None # from env, never hard-coded
server: Optional[str] = None # broker server
def write_tester_ini(cfg: TesterConfig, path: str | Path) -> str:
"""Write a ``tester.ini`` for ``terminal64.exe /config:`` and return its path."""
p = Path(path)
p.parent.mkdir(parents=True, exist_ok=True)
cp = configparser.ConfigParser()
cp.optionxform = str # preserve case (MT5 keys are case-sensitive)
cp["Tester"] = {
"Expert": cfg.expert,
"Symbol": cfg.symbol,
"Period": cfg.period,
"Model": str(cfg.model),
"FromDate": cfg.from_date,
"ToDate": cfg.to_date,
"Deposit": str(cfg.deposit),
"Leverage": str(cfg.leverage),
"Report": cfg.report,
"ShutdownTerminal": "1" if cfg.shutdown_terminal else "0",
}
if cfg.set_file:
cp["Tester"]["TestReplaceExpert"] = "0"
common: dict[str, str] = {}
if cfg.login is not None:
common["Login"] = str(cfg.login)
if cfg.password is not None:
common["Password"] = cfg.password
if cfg.server is not None:
common["Server"] = cfg.server
if common:
cp["Common"] = common
with p.open("w", encoding="utf-8") as f:
cp.write(f)
return str(p)