f5ffe4baee
Add complete framework for testing and deploying quant strategies: Framework (framework/): - HyperliquidInstrumentCatalog: loads perps as NT CryptoPerpetual - HyperliquidDataProvider: real candle/orderbook/mark-price data - HyperliquidExecutionProvider: live + PaperExecutionProvider: simulated - BaseHlStrategy: shared NT strategy lifecycle with signal library - StrategyConfig: YAML-based parameter management - DeployOrchestrator: CLI for backtest -> paper -> live pipeline Backtesting (backtests/): - VBTBacktestRunner: VectorBT vectorized backtests on real HL candles - NTBacktestRunner: NautilusTrader event-driven backtest engine NT Strategy ports (strategies/nt/): - PairsTradingNT: BTC/ETH ratio Z-score mean reversion - HurstVPINNT: Hurst exponent regime + VPIN flow imbalance - ASMarketMakingNT: Avellaneda-Stoikov stochastic control MM E2E verified: real HL candles fetch, VectorBT backtest (Sharpe 5.2 on Hurst/VPIN), instrument catalog, deploy CLI --list, strategy signals. Existing live/node.py and paper_trader.py unchanged.
69 lines
2.2 KiB
Python
69 lines
2.2 KiB
Python
"""
|
|
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
|