Files
mymt5opp/shared/mt5_pipeline/set_gen.py
T
gavindiaz 63a829cc46 phase 7-8 完成 + warmup 修复 + 产物结构化重组
主要内容:
- Phase 8 PROMOTE: finalist #1 (trial #324) registry 条目,自动生成
- Optuna objective warmup bug 修复 (shared/optimizer/objective.py)
- studies/ 目录按用途重组为 optuna/ + finalists/ + features/ 三层
- reports/ 加入 Optuna 中文 dashboard (5 主图 + 18 slice + 15 contour)
- 新增 PROJECT_GUIDE.md 项目说明文档
- 新增 build_registry_entry.py / build_optuna_dashboard.py / build_feature_datasets.py
- .gitignore: 允许提交 studies/*.db (Optuna DB) 和 reports/*.html (MT5 + dashboard)
2026-06-27 00:28:07 +08:00

75 lines
2.7 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"
# MT5's Strategy Tester silently ignores .set files without a UTF-16-LE BOM.
# Python's "utf-16-le" codec does NOT emit a BOM, so prepend one explicitly.
UTF16_LE_BOM = b"\xff\xfe"
@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(UTF16_LE_BOM + text.encode(SET_FILE_ENCODING))