convex_math/extrapolation/flat.rs
1//! Flat (constant) extrapolation.
2
3use super::Extrapolator;
4
5/// Flat extrapolation - constant value from the last point.
6///
7/// This is the simplest extrapolation method, maintaining a constant value
8/// equal to the last observed point. It implies a flat forward rate curve
9/// beyond the last liquid point.
10///
11/// # Properties
12///
13/// - **Simple**: No parameters to configure
14/// - **Conservative**: No trend assumptions
15/// - **Discontinuous derivative**: Slope becomes zero at boundary
16///
17/// # Use Cases
18///
19/// - Short-term extrapolation where trend continuation is not desired
20/// - Conservative scenarios
21/// - Default fallback when no better method is available
22///
23/// # Example
24///
25/// ```rust
26/// use convex_math::extrapolation::{FlatExtrapolator, Extrapolator};
27///
28/// let extrap = FlatExtrapolator;
29///
30/// // Last observed: 5% at 10 years with 0.1% slope
31/// let rate = extrap.extrapolate(15.0, 10.0, 0.05, 0.001);
32/// assert_eq!(rate, 0.05); // Same as last value
33/// ```
34#[derive(Debug, Clone, Copy, Default)]
35pub struct FlatExtrapolator;
36
37impl FlatExtrapolator {
38 /// Creates a new flat extrapolator.
39 #[must_use]
40 pub fn new() -> Self {
41 Self
42 }
43}
44
45impl Extrapolator for FlatExtrapolator {
46 fn extrapolate(&self, _t: f64, _last_t: f64, last_value: f64, _last_derivative: f64) -> f64 {
47 // Simply return the last known value
48 last_value
49 }
50
51 fn name(&self) -> &'static str {
52 "Flat"
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use approx::assert_relative_eq;
60
61 #[test]
62 fn test_flat_returns_last_value() {
63 let extrap = FlatExtrapolator::new();
64
65 let last_t = 20.0;
66 let last_value = 0.045;
67 let last_deriv = 0.002;
68
69 // Should always return last value regardless of t
70 for t in [21.0, 30.0, 50.0, 100.0, 1000.0] {
71 let value = extrap.extrapolate(t, last_t, last_value, last_deriv);
72 assert_relative_eq!(value, last_value, epsilon = 1e-15);
73 }
74 }
75
76 #[test]
77 fn test_flat_ignores_derivative() {
78 let extrap = FlatExtrapolator;
79
80 let last_t = 10.0;
81 let last_value = 0.03;
82
83 // Different derivatives should give same result
84 for deriv in [-0.01, 0.0, 0.001, 0.01, 0.1] {
85 let value = extrap.extrapolate(20.0, last_t, last_value, deriv);
86 assert_relative_eq!(value, last_value, epsilon = 1e-15);
87 }
88 }
89
90 #[test]
91 fn test_flat_name() {
92 let extrap = FlatExtrapolator;
93 assert_eq!(extrap.name(), "Flat");
94 }
95}