yieldcurveml.utils

1from .utils import get_swap_rates, SwapRatesData, swap_cashflows_matrix, SwapCashflows, regression_report
2
3__all__ = [
4    'get_swap_rates',
5    'SwapRatesData',
6    'swap_cashflows_matrix',
7    'SwapCashflows',
8    'regression_report'
9]
def get_swap_rates( dataset: Literal['ap10', 'and07', 'ab13e6m', 'ab13ois', 'negativerates']) -> SwapRatesData:
105def get_swap_rates(dataset: Literal["ap10", "and07", "ab13e6m", "ab13ois", "negativerates"]) -> SwapRatesData:
106    """
107    Get example swap rates datasets.
108
109    Args:
110        dataset: String specifying the dataset to use,
111                one of "ap10", "and07", "ab13e6m", "ab13ois", "negativerates"
112
113    Returns:
114        SwapRatesData containing maturity and rate vectors
115
116    Examples:
117        >>> print(get_swap_rates("ap10"))
118        SwapRatesData(maturity=array([ 1,  2,  3,  5,  7, 10, 12, 15, 20, 25]), 
119                     rate=array([0.042, 0.043, 0.047, 0.054, 0.057, 0.06 , 0.061, 0.059, 0.056, 0.0555]))
120    """
121    # Define all datasets
122    swap_rates: Dict[str, SwapRatesData] = {
123        "ap10": SwapRatesData(
124            maturity=np.array([1, 2, 3, 5, 7, 10, 12, 15, 20, 25]),
125            rate=np.array([4.2, 4.3, 4.7, 5.4, 5.7, 6, 6.1, 5.9, 5.6, 5.55]) / 100
126        ),
127        
128        "and07": SwapRatesData(
129            maturity=np.array([0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 7, 10, 12, 15, 20, 30]),
130            rate=np.array([2.75, 3.10, 3.30, 3.43, 3.53, 3.30, 3.78, 3.95, 
131                          4.25, 4.50, 4.65, 4.78, 4.88, 4.85]) / 100
132        ),
133        
134        "ab13e6m": SwapRatesData(
135            maturity=np.array(list(range(1, 31)) + [35, 40, 50, 60]),
136            rate=np.array([
137                0.286, 0.324, 0.424, 0.576, 0.762, 0.954, 1.135, 1.303, 1.452, 1.584,
138                1.703, 1.809, 1.901, 1.976, 2.037, 2.086, 2.123, 2.150, 2.171, 2.187,
139                2.200, 2.211, 2.220, 2.228, 2.234, 2.239, 2.243, 2.247, 2.251, 2.256,
140                2.295, 2.348, 2.421, 2.463
141            ]) / 100
142        ),
143        
144        "ab13ois": SwapRatesData(
145            maturity=np.array(list(range(1, 13)) + [15, 20, 25, 30]),
146            rate=np.array([
147                0.000, 0.036, 0.127, 0.274, 0.456, 0.647, 0.827, 0.996, 1.147, 1.280,
148                1.404, 1.516, 1.764, 1.939, 2.003, 2.038
149            ]) / 100
150        ),
151        
152        "negativerates": SwapRatesData(
153            maturity=np.array([0.5, 1, 2, 3, 5]),
154            rate=np.array([-0.00337, -0.003610, -0.003647, -0.003413, -0.003047])
155        )
156    }
157    
158    if dataset not in swap_rates:
159        raise ValueError(
160            f"Dataset {dataset} not found. Available datasets: {', '.join(swap_rates.keys())}"
161        )
162    
163    return swap_rates[dataset]

Get example swap rates datasets.

Args: dataset: String specifying the dataset to use, one of "ap10", "and07", "ab13e6m", "ab13ois", "negativerates"

Returns: SwapRatesData containing maturity and rate vectors

Examples:

print(get_swap_rates("ap10")) SwapRatesData(maturity=array([ 1, 2, 3, 5, 7, 10, 12, 15, 20, 25]), rate=array([0.042, 0.043, 0.047, 0.054, 0.057, 0.06 , 0.061, 0.059, 0.056, 0.0555]))

class SwapRatesData(typing.NamedTuple):
17class SwapRatesData(NamedTuple):
18    maturity: np.ndarray
19    rate: np.ndarray

SwapRatesData(maturity, rate)

def swap_cashflows_matrix( swap_rates: Union[List[float], numpy.ndarray], maturities: Union[List[float], numpy.ndarray], tenor_swaps: Literal['1m', '3m', '6m', '1y'] = '6m') -> SwapCashflows:
 21def swap_cashflows_matrix(
 22    swap_rates: Union[List[float], np.ndarray],
 23    maturities: Union[List[float], np.ndarray],
 24    tenor_swaps: Literal["1m", "3m", "6m", "1y"] = "6m"
 25) -> SwapCashflows:
 26    """
 27    Creates a matrix of swap cashflows.
 28
 29    Args:
 30        swap_rates: Vector of swap rates
 31        maturities: Vector of maturities (in years)
 32        tenor_swaps: Tenor for the swaps, one of "1m", "3m", "6m", "1y"
 33
 34    Returns:
 35        SwapCashflows object containing:
 36            - nb_swaps: number of swaps
 37            - swaps_maturities: original maturities
 38            - nb_swap_dates: number of cashflow dates per swap
 39            - swap_rates: original swap rates
 40            - cashflow_dates: matrix of cashflow dates
 41            - cashflow_matrix: matrix of cashflows
 42
 43    Example:
 44        >>> rates = [0.02, 0.025, 0.03]
 45        >>> maturities = [1, 2, 3]
 46        >>> result = swap_cashflows_matrix(rates, maturities, "6m")
 47    """
 48    # Convert inputs to numpy arrays if they aren't already
 49    swap_rates = np.array(swap_rates)
 50    maturities = np.array(maturities)
 51    
 52    nb_swaps = len(swap_rates)
 53    if nb_swaps != len(maturities):
 54        raise ValueError("There must be as many swap rates as maturities")
 55    
 56    # Define frequency mapping
 57    freq_map = {
 58        "1m": 1/12,
 59        "3m": 1/4,
 60        "6m": 1/2,
 61        "1y": 1
 62    }
 63    
 64    if tenor_swaps not in freq_map:
 65        raise ValueError("tenor_swaps must be one of: '1m', '3m', '6m', '1y'")
 66    
 67    freq = freq_map[tenor_swaps]
 68    
 69    # Create cashflow dates
 70    cashflow_dates = np.arange(freq, max(maturities) + freq, freq)
 71    nb_cashflow_dates = len(cashflow_dates)
 72    nb_cashflow_dates_swaps = (1 / freq) * maturities
 73    
 74    # Initialize matrices
 75    swap_cashflows_matrix = np.zeros((nb_swaps, nb_cashflow_dates))
 76    cashflow_dates_matrix = np.zeros((nb_swaps, nb_cashflow_dates))
 77    nb_swap_dates = np.zeros(nb_swaps)
 78    
 79    # Fill matrices
 80    for i in range(nb_swaps):
 81        nb_cashflow_dates_swaps_i = int(nb_cashflow_dates_swaps[i])
 82        swap_rate_i_times_freq = swap_rates[i] * freq
 83        
 84        # Set regular cashflows
 85        swap_cashflows_matrix[i, :nb_cashflow_dates_swaps_i] = swap_rate_i_times_freq
 86        # Add principal repayment at maturity
 87        swap_cashflows_matrix[i, nb_cashflow_dates_swaps_i - 1] += 1
 88        
 89        nb_swap_dates[i] = np.sum(swap_cashflows_matrix[i, :] > 0)
 90        cashflow_dates_matrix[i, :nb_cashflow_dates_swaps_i] = cashflow_dates[:nb_cashflow_dates_swaps_i]
 91    
 92    # Add row and column names
 93    swap_names = [f"swap{i+1}" for i in range(nb_swaps)]
 94    date_names = [f"{d}y" for d in cashflow_dates]
 95    
 96    return SwapCashflows(
 97        nb_swaps=nb_swaps,
 98        swaps_maturities=maturities,
 99        nb_swap_dates=nb_swap_dates,
100        swap_rates=swap_rates,
101        cashflow_dates=cashflow_dates_matrix,
102        cashflow_matrix=swap_cashflows_matrix
103    )

Creates a matrix of swap cashflows.

Args: swap_rates: Vector of swap rates maturities: Vector of maturities (in years) tenor_swaps: Tenor for the swaps, one of "1m", "3m", "6m", "1y"

Returns: SwapCashflows object containing: - nb_swaps: number of swaps - swaps_maturities: original maturities - nb_swap_dates: number of cashflow dates per swap - swap_rates: original swap rates - cashflow_dates: matrix of cashflow dates - cashflow_matrix: matrix of cashflows

Example:

rates = [0.02, 0.025, 0.03] maturities = [1, 2, 3] result = swap_cashflows_matrix(rates, maturities, "6m")

@dataclass
class SwapCashflows:
 8@dataclass
 9class SwapCashflows:
10    nb_swaps: int
11    swaps_maturities: np.ndarray
12    nb_swap_dates: np.ndarray
13    swap_rates: np.ndarray
14    cashflow_dates: np.ndarray
15    cashflow_matrix: np.ndarray
def regression_report(model: 'CurveStripper', name: str = 'Model') -> str:
165def regression_report(
166    model: 'CurveStripper',
167    name: str = "Model"
168) -> str:
169    """Create a formatted report of regression diagnostics for a fitted CurveStripper model.
170    
171    Parameters
172    ----------
173    model : CurveStripper
174        Fitted CurveStripper model
175    name : str, default="Model"
176        Name to use for the model in the report
177        
178    Returns
179    -------
180    str
181        Formatted report string
182        
183    Examples
184    --------
185    >>> from yieldcurveml.stripcurve import CurveStripper
186    >>> from sklearn.ensemble import RandomForestRegressor
187    >>> model = CurveStripper(RandomForestRegressor())
188    >>> model.fit(X, y)
189    >>> print(regression_report(model, "RandomForest"))
190    """
191    if not hasattr(model, 'curve_rates_'):
192        raise ValueError("Model must be fitted before generating report")
193        
194    diagnostics = model.get_diagnostics()
195    
196    metrics_data = {
197        'Metric': ['Samples', 'R²', 'RMSE', 'MAE', 'Min Error', 'Max Error'],
198        name: [
199            diagnostics.n_samples,
200            f"{diagnostics.r2_score:.4f}",
201            f"{diagnostics.rmse:.4f}",
202            f"{diagnostics.mae:.4f}",
203            f"{diagnostics.min_error:.4f}",
204            f"{diagnostics.max_error:.4f}"
205        ]
206    }
207    
208    summary_data = {
209        'Statistic': ['Mean', 'Std Dev', 'Median', 'MAD', 'Skewness', 'Kurtosis'],
210        name: [
211            f"{diagnostics.residuals_summary['mean']:.4f}",
212            f"{diagnostics.residuals_summary['std']:.4f}",
213            f"{diagnostics.residuals_summary['median']:.4f}",
214            f"{diagnostics.residuals_summary['mad']:.4f}",
215            f"{diagnostics.residuals_summary['skewness']:.4f}",
216            f"{diagnostics.residuals_summary['kurtosis']:.4f}"
217        ]
218    }
219    
220    percentiles_data = {
221        'Percentile': ['1%', '5%', '25%', '75%', '95%', '99%'],
222        name: [
223            f"{diagnostics.residuals_summary['percentiles']['1%']:.4f}",
224            f"{diagnostics.residuals_summary['percentiles']['5%']:.4f}",
225            f"{diagnostics.residuals_summary['percentiles']['25%']:.4f}",
226            f"{diagnostics.residuals_summary['percentiles']['75%']:.4f}",
227            f"{diagnostics.residuals_summary['percentiles']['95%']:.4f}",
228            f"{diagnostics.residuals_summary['percentiles']['99%']:.4f}"
229        ]
230    }
231    
232    metrics_df = pd.DataFrame(metrics_data)
233    summary_df = pd.DataFrame(summary_data)
234    percentiles_df = pd.DataFrame(percentiles_data)
235    
236    report = (
237        f"\nModel Performance Metrics:\n"
238        f"{tabulate(metrics_df, headers='keys', tablefmt='pipe', showindex=False)}\n\n"
239        f"Residuals Summary Statistics:\n"
240        f"{tabulate(summary_df, headers='keys', tablefmt='pipe', showindex=False)}\n\n"
241        f"Residuals Percentiles:\n"
242        f"{tabulate(percentiles_df, headers='keys', tablefmt='pipe', showindex=False)}"
243    )
244    
245    return report

Create a formatted report of regression diagnostics for a fitted CurveStripper model.

Parameters

model : CurveStripper Fitted CurveStripper model name : str, default="Model" Name to use for the model in the report

Returns

str Formatted report string

Examples

>>> from yieldcurveml.stripcurve import CurveStripper
>>> from sklearn.ensemble import RandomForestRegressor
>>> model = CurveStripper(RandomForestRegressor())
>>> model.fit(X, y)
>>> print(regression_report(model, "RandomForest"))