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}