Coverage for src/finbot/backtest/experiment.py: 96%
41 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 17:12 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 17:12 +0000
1from dataclasses import dataclass, field
2from datetime import datetime
3from typing import Any
4from uuid import UUID
6from finbot.backtest.models import BacktestResult
9@dataclass(frozen=True)
10class ExperimentConfig:
11 strategy: str
12 symbols: tuple[str, ...]
13 start: datetime
14 end: datetime
15 dataset: str
16 initial_cash: float
17 allocation: float = 0.999
18 fee_rate: float = 0.0
19 slippage_rate: float = 0.0
20 strategy_params: dict[str, Any] = field(default_factory=dict)
22 def __post_init__(self) -> None:
23 if not self.strategy: 23 ↛ 24line 23 didn't jump to line 24 because the condition on line 23 was never true
24 raise ValueError("strategy must not be empty")
25 if not self.symbols:
26 raise ValueError("symbols must not be empty")
27 if self.start >= self.end:
28 raise ValueError("start must be before end")
29 if self.initial_cash <= 0:
30 raise ValueError("initial_cash must be greater than 0")
31 if not 0 < self.allocation <= 1:
32 raise ValueError("allocation must be greater than 0 and at most 1")
33 if self.fee_rate < 0:
34 raise ValueError("fee_rate must not be negative")
35 if self.slippage_rate < 0:
36 raise ValueError("slippage_rate must not be negative")
39@dataclass(frozen=True)
40class ExperimentMetadata:
41 run_id: UUID
42 created_at: datetime
43 config: ExperimentConfig
46@dataclass(frozen=True)
47class ExperimentRun:
48 metadata: ExperimentMetadata
49 result: BacktestResult