Coverage for src/stable_yield_lab/core/models.py: 100%

29 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-04 20:38 +0000

1"""Immutable data models used throughout StableYieldLab.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import asdict, dataclass 

6from datetime import UTC, datetime 

7from typing import Any 

8 

9import pandas as pd 

10 

11 

12@dataclass(frozen=True) 

13class Pool: 

14 """Snapshot description of a yield-bearing pool.""" 

15 

16 name: str 

17 chain: str 

18 stablecoin: str 

19 tvl_usd: float 

20 base_apy: float # decimal fraction, e.g. 0.08 for 8% 

21 reward_apy: float = 0.0 # optional extra rewards (auto-compounded by meta protocols) 

22 is_auto: bool = True # True if fully automated (no manual boosts/claims) 

23 source: str = "custom" 

24 risk_score: float = 2.0 # 1=low, 3=high (subjective / model-derived) 

25 timestamp: float = 0.0 # unix epoch; 0 means unknown 

26 

27 def to_dict(self) -> dict[str, Any]: 

28 """Serialise the pool to a dictionary suitable for DataFrame creation.""" 

29 

30 data = asdict(self) 

31 # for readability in CSV outputs 

32 data["timestamp_iso"] = ( 

33 datetime.fromtimestamp(self.timestamp or 0, tz=UTC).isoformat() 

34 if self.timestamp 

35 else "" 

36 ) 

37 return data 

38 

39 

40@dataclass(frozen=True) 

41class PoolReturn: 

42 """Periodic return observation for a given pool.""" 

43 

44 name: str 

45 timestamp: pd.Timestamp 

46 period_return: float 

47 

48 def to_dict(self) -> dict[str, Any]: 

49 return { 

50 "name": self.name, 

51 "timestamp": self.timestamp, 

52 "period_return": self.period_return, 

53 } 

54 

55 

56__all__ = ["Pool", "PoolReturn"]