Coverage for src / qsmile / data / meta.py: 100%
28 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 22:47 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 22:47 +0000
1"""Smile metadata for coordinate transforms."""
3from __future__ import annotations
5from dataclasses import dataclass
7import pandas as pd
9from qsmile.core.daycount import DayCount
12@dataclass(frozen=True)
13class SmileMetadata:
14 """Parameters needed by coordinate transforms.
16 Parameters
17 ----------
18 date : pd.Timestamp
19 Valuation / pricing date.
20 expiry : pd.Timestamp
21 Expiry date. Must be strictly after ``date``.
22 daycount : DayCount
23 Day-count convention for computing the year fraction.
24 Defaults to ``DayCount.ACT365``.
25 forward : float | None
26 Forward price. Must be positive when provided.
27 discount_factor : float | None
28 Discount factor. Must be positive when provided.
29 sigma_atm : float | None
30 ATM implied volatility. Must be positive when provided.
31 Required for StandardisedStrike transforms.
32 """
34 date: pd.Timestamp
35 expiry: pd.Timestamp
36 daycount: DayCount = DayCount.ACT365
37 forward: float | None = None
38 discount_factor: float | None = None
39 sigma_atm: float | None = None
41 @property
42 def texpiry(self) -> float:
43 """Year fraction derived from (date, expiry, daycount)."""
44 return self.daycount.year_fraction(self.date, self.expiry)
46 def __post_init__(self) -> None:
47 """Validate inputs."""
48 if self.expiry <= self.date:
49 msg = f"expiry must be after date, got date={self.date}, expiry={self.expiry}"
50 raise ValueError(msg)
51 if self.forward is not None and self.forward <= 0:
52 msg = f"forward must be positive, got {self.forward}"
53 raise ValueError(msg)
54 if self.discount_factor is not None and self.discount_factor <= 0:
55 msg = f"discount_factor must be positive, got {self.discount_factor}"
56 raise ValueError(msg)
57 if self.sigma_atm is not None and self.sigma_atm <= 0:
58 msg = f"sigma_atm must be positive, got {self.sigma_atm}"
59 raise ValueError(msg)