Coverage for src/finbot/strategy/core.py: 100%
25 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-04 12:05 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-04 12:05 +0000
1import math
2from abc import ABC, abstractmethod
3from dataclasses import dataclass, field
4from enum import StrEnum
5from typing import Any
7from finbot.market.models import Bar
10class SignalDirection(StrEnum):
11 LONG = "long"
12 SHORT = "short"
13 FLAT = "flat"
16@dataclass(frozen=True)
17class Signal:
18 symbol: str
19 direction: SignalDirection
20 strength: float | None = None
21 metadata: dict[str, Any] = field(default_factory=dict)
23 def __post_init__(self) -> None:
24 if not isinstance(self.symbol, str) or not self.symbol.strip():
25 raise ValueError("Symbol must be a non-empty string.")
27 if self.strength is not None:
28 if not math.isfinite(self.strength):
29 raise ValueError("Signal strength must be finite.")
31 if not 0.0 <= self.strength <= 1.0:
32 raise ValueError("Signal strength must be between 0 and 1.")
35class Strategy(ABC):
37 @abstractmethod
38 def on_bar(self, bar: Bar) -> Signal | list[Signal] | None:
39 ...