""" Strategy configuration — YAML-based parameter management. Each strategy gets a YAML file in config/ with its parameters for backtest, paper, and live environments. The StrategyConfig class loads and validates these configs. """ from __future__ import annotations import yaml from dataclasses import dataclass, field from pathlib import Path from typing import Any CONFIG_DIR = Path(__file__).resolve().parent.parent / "config" @dataclass class StrategyConfig: """Unified strategy configuration across backtest / paper / live.""" name: str instrument: str asset: str # Base currency (BTC, ETH, etc.) allocation: float = 10000.0 # Capital allocated order_size: float = 0.001 # Default order size (in base units) maker_fee: float = 0.0002 taker_fee: float = 0.0005 slippage_bps: float = 1.0 testnet: bool = True # Signal parameters (strategy-specific) params: dict[str, Any] = field(default_factory=dict) # Risk max_position: float = 0.0 # 0 = based on allocation / price max_drawdown: float = 0.10 stop_loss_pct: float = 0.0 # 0 = no stop # Derived fee_model: str = "taker" # taker or maker @classmethod def from_yaml(cls, path: str | Path) -> StrategyConfig: with open(path) as f: data = yaml.safe_load(f) return cls(**data) def to_yaml(self, path: str | Path) -> None: with open(path, "w") as f: yaml.safe_dump(self.__dict__, f, default_flow_style=False) def effective_fee(self) -> float: return self.maker_fee if self.fee_model == "maker" else self.taker_fee @classmethod def load_by_name(cls, name: str, env: str = "paper") -> StrategyConfig: """Load a strategy config from config/{name}.yaml.""" config_path = CONFIG_DIR / f"{name}.yaml" if not config_path.exists(): raise FileNotFoundError(f"Config not found: {config_path}") cfg = cls.from_yaml(config_path) if env == "testnet": cfg.testnet = True elif env in ("mainnet", "live"): cfg.testnet = False return cfg