research/ now runs end-to-end (fetch -> features/labels -> walk-forward -> train -> OOS evaluate -> save), and backtest_strategy() replays the same signal(ctx) strategy over a historical frame with the same manage_position hook the live loop calls, simulating SL/TP, trailing, partial closes and cost per bar. --backtest wires it into the CLI. Co-Authored-By: Claude Code <noreply@anthropic.com>
61 lines
2.6 KiB
Python
61 lines
2.6 KiB
Python
"""Research pipeline configuration (environment-driven, like trading/)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
|
|
|
|
def _env_float(name: str, default: float) -> float:
|
|
raw = os.environ.get(name)
|
|
return float(raw) if raw else default
|
|
|
|
|
|
def _env_int(name: str, default: int) -> int:
|
|
raw = os.environ.get(name)
|
|
return int(raw) if raw else default
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ResearchConfig:
|
|
# --- process ------------------------------------------------------------
|
|
mtdata_cli: str = os.environ.get("MTDATA_CLI", "mtdata-cli")
|
|
cli_timeout_seconds: float = _env_float("RESEARCH_CLI_TIMEOUT", 60.0)
|
|
|
|
# --- data ---------------------------------------------------------------
|
|
data_dir: Path = Path(os.environ.get("RESEARCH_DATA_DIR", "research/data"))
|
|
storage_format: str = os.environ.get("RESEARCH_STORAGE_FORMAT", "parquet") # parquet | csv
|
|
symbols: tuple[str, ...] = field(
|
|
default_factory=lambda: tuple(
|
|
filter(None, os.environ.get("RESEARCH_SYMBOLS", "EURUSD,GBPUSD").split(","))
|
|
)
|
|
)
|
|
timeframe: str = os.environ.get("RESEARCH_TIMEFRAME", "H1")
|
|
lookback_bars: int = _env_int("RESEARCH_LOOKBACK_BARS", 3000)
|
|
indicators: tuple[str, ...] = field(
|
|
default_factory=lambda: tuple(
|
|
filter(None, os.environ.get("RESEARCH_INDICATORS", "rsi,atr,ema,bbands").split(","))
|
|
)
|
|
)
|
|
|
|
# --- labels --------------------------------------------------------------
|
|
label_tp_pct: float = _env_float("RESEARCH_LABEL_TP_PCT", 0.4) # 0.4%
|
|
label_sl_pct: float = _env_float("RESEARCH_LABEL_SL_PCT", 0.6) # 0.6%
|
|
label_horizon: int = _env_int("RESEARCH_LABEL_HORIZON", 12)
|
|
|
|
# --- walk-forward backtest ------------------------------------------------
|
|
wf_n_splits: int = _env_int("RESEARCH_WF_N_SPLITS", 5)
|
|
wf_min_train_bars: int = _env_int("RESEARCH_WF_MIN_TRAIN_BARS", 1000)
|
|
spread_bps: float = _env_float("RESEARCH_SPREAD_BPS", 1.0)
|
|
commission_bps_per_side: float = _env_float("RESEARCH_COMMISSION_BPS", 0.0)
|
|
slippage_bps: float = _env_float("RESEARCH_SLIPPAGE_BPS", 1.0)
|
|
|
|
# --- model ----------------------------------------------------------------
|
|
model_dir: Path = Path(os.environ.get("RESEARCH_MODEL_DIR", "research/models"))
|
|
model_kind: str = os.environ.get("RESEARCH_MODEL", "logistic") # logistic|rf|gboost|lightgbm
|
|
train_ratio: float = _env_float("RESEARCH_TRAIN_RATIO", 0.8) # chronological split
|
|
|
|
# --- reporting --------------------------------------------------------------
|
|
report_dir: Path = Path(os.environ.get("RESEARCH_REPORT_DIR", "research/reports"))
|