Files
mymt5opp/shared/mt5_pipeline/set_gen.py
T
2026-06-26 20:50:07 +08:00

72 lines
2.5 KiB
Python

""".set file generator (doc 07 §2a).
A ``.set`` is the EA's inputs serialized as ``key=value`` lines. **MT5 writes
``.set`` files as UTF-16-LE** — generate yours in the same encoding or the
tester silently ignores them.
The bridge's set generator takes the *same* parameter dict you backtested in
Python and writes the matching ``.set``. The mapping from Python parameter
names to EA input names is strategy-specific — keep a small mapping table
next to the strategy so the two tiers always agree. Watch the enum-valued
inputs (mode flags, timeframe codes): MT5 inputs are often integers.
"""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Mapping
SET_FILE_ENCODING = "utf-16-le"
@dataclass
class ParamMapping:
"""Maps a Python param name to an EA input name (+ enum cast if needed).
``cast`` converts a Python value to the EA input's wire form (e.g. a
timeframe string to its ``ENUM_TIMEFRAMES`` integer, a bool to ``true``/
``false``). Defaults to identity.
"""
py_name: str
ea_name: str
cast: Any = None # callable(value) -> str, optional
def to_wire(self, value: Any) -> str:
v = self.cast(value) if self.cast is not None else value
if isinstance(v, bool):
return "true" if v else "false"
if isinstance(v, float) and v.is_integer():
return str(int(v))
return str(v)
def write_set_file(
params: Mapping[str, Any],
mappings: list[ParamMapping],
path: str | Path,
*,
extra_lines: list[str] | None = None,
) -> None:
"""Write a ``.set`` file (UTF-16-LE) from a Python params dict.
Only parameters with a mapping are written — frozen baseline values that
match the EA's compiled-in defaults can be omitted. ``extra_lines`` lets a
strategy inject raw ``key=value`` lines that don't have a Python
counterpart (e.g. EA constants).
**Lot-mode guard (doc 05 §4, doc 07 §2a):** make sure the fixed-lot input
is ``0`` if you intend money mode — a mismatched ``.set`` is the #1 reason
a verified MT5 number disagrees with Python.
"""
out = Path(path)
out.parent.mkdir(parents=True, exist_ok=True)
lines: list[str] = ["; Generated by the MT5 bridge (UTF-16-LE)"]
for m in mappings:
if m.py_name in params:
lines.append(f"{m.ea_name}={m.to_wire(params[m.py_name])}")
if extra_lines:
lines.extend(extra_lines)
text = "\n".join(lines) + "\n"
out.write_bytes(text.encode(SET_FILE_ENCODING))