Coverage for rivapy / marketdata / fixing_table.py: 54%
48 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-27 14:36 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-27 14:36 +0000
1# 2025.07.17 Hans Nguyen
2# Fixing table class for Interest Rate Swap Pricer for IR bootstrapping implementation
5# Modules
6from typing import List as _List, Union as _Union, Tuple, Dict, Any
7from datetime import datetime, date
8import bisect
11# Class
12class FixingTable:
13 """Container for historical fixings."""
15 def __init__(self, id: str = None, fixings: Dict[str, Tuple[_List[datetime], _List[float]]] = None):
16 # id: Optional[str] = None,
17 # fixings: Optional[Dict[str, Tuple[List[datetime], List[float]]]] = None):
18 """Constructor for FixingTable. Creates 'empty' instance if no parameters passed.
20 Args:
21 id (str): identifier for the table. Defaults to None.
22 fixings (Dict[str, Tuple[List[datetime], List[float]]]): A dictionary mapping underlying IDs to
23 pairs of fixing dates and values. Defaults to None.
24 """
26 self.id = id
27 self.fixings: Dict[str, Tuple[_List[datetime], _List[float]]] = fixings if fixings else {}
29 if fixings:
30 expected_len = None
31 for udl, (dates, values) in fixings.items():
32 if expected_len is None:
33 expected_len = len(dates)
34 if len(dates) != expected_len:
35 raise ValueError(f"Inconsistent number of dates for {udl}: {len(dates)} != {expected_len}")
36 if len(values) != expected_len:
37 raise ValueError(f"Inconsistent number of fixings for {udl}: {len(values)} != {expected_len}")
39 def get_object_type(self) -> str:
40 return "FIXING_TABLE"
42 def get_fixing(self, udl_id: str, fixing_date: datetime) -> float:
43 """
44 Return fixing for a given underlying and date. Raises error if not found.
47 Args:
48 udl_id (str): The underlying ID.
49 fixing_date (datetime): The date of the fixing
51 Raises:
52 ValueError: no fixings for underlying
53 ValueError: no fixing for given date
55 Returns:
56 float: The fixing value if found, otherwise raise error
57 """
59 if udl_id not in self.fixings:
60 raise ValueError(f"No fixings for underlying '{udl_id}'")
62 dates, values = self.fixings[udl_id]
63 for i, date in enumerate(dates):
64 if date == fixing_date:
65 return values[i]
67 raise ValueError(f"No fixing found for '{udl_id}' on {fixing_date.isoformat()}")
69 def get_num_underlyings(self) -> int:
70 """Return number of underlyings
72 Returns:
73 int: _description_
74 """
75 return len(self.fixings)
77 def add(self, key: str, fixing_date: datetime, value: float) -> None:
78 """
79 Add a fixing and keep entries sorted by date.
81 Args:
82 key (str): The underlying ID key to map to fixing.
83 fixing_date (datetime): The date of the fixing.
84 value (float): The fixing value.
85 """
87 if key not in self.fixings:
88 self.fixings[key] = ([], [])
90 dates, values = self.fixings[key]
92 # Insert while keeping both lists sorted to preserve date order
93 index = bisect.bisect_left(dates, fixing_date)
94 dates.insert(index, fixing_date)
95 values.insert(index, value)
97 def get(self, udl_id: str) -> Tuple[_List[datetime], _List[float]]:
98 """
99 Get Tuple of all fixings for a given underlying.
101 Args:
102 udl_id (str): key of underlying ID
104 Returns:
105 Tuple[List[datetime], List[float]]: Tuple of dates and fixings.
106 """
107 if udl_id not in self.fixings:
108 return [], [] # TODO or riase an error?
109 dates, values = self.fixings[udl_id]
111 return dates[:], values[:]
113 def _to_dict(self):
114 # Serializes the fixings dictionary: keys are underlying IDs,
115 # values are tuples of (list of dates, list of floats)
116 result = {}
117 for udl_id, (dates, values) in self.fixings.items():
118 # Convert all dates to ISO strings
119 dates_serialized = [d.isoformat() if hasattr(d, "isoformat") else str(d) for d in dates]
120 result[udl_id] = {"dates": dates_serialized, "values": values}
121 return {"id": self.id, "fixings": result}
124# Functions
127if __name__ == "__main__":
128 pass