回测基本一致
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
""".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))
|
||||
Reference in New Issue
Block a user