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

1import math 

2from abc import ABC, abstractmethod 

3from dataclasses import dataclass, field 

4from enum import StrEnum 

5from typing import Any 

6 

7from finbot.market.models import Bar 

8 

9 

10class SignalDirection(StrEnum): 

11 LONG = "long" 

12 SHORT = "short" 

13 FLAT = "flat" 

14 

15 

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) 

22 

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.") 

26 

27 if self.strength is not None: 

28 if not math.isfinite(self.strength): 

29 raise ValueError("Signal strength must be finite.") 

30 

31 if not 0.0 <= self.strength <= 1.0: 

32 raise ValueError("Signal strength must be between 0 and 1.") 

33 

34 

35class Strategy(ABC): 

36 

37 @abstractmethod 

38 def on_bar(self, bar: Bar) -> Signal | list[Signal] | None: 

39 ...