1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! One-factor affine short-rate models.
//!
//! Port of the closed-form parts of `ql/models/shortrate/onefactormodel.hpp`:
//! the affine `discountBond` payoff. A single-factor affine model prices a
//! zero-coupon bond in closed form as
//! `P(t, T, r_t) = A(t,T) e^{-B(t,T) r_t}` (`onefactormodel.hpp:136`), with
//! `A` and `B` supplied by the concrete model (Vasicek in #378).
//!
//! ## Trait surface (deferral by omission)
//!
//! Rust controls the trait surface, so the deferred machinery is omitted rather
//! than stubbed to a panic. The only affine method here is
//! [`discount_bond`](OneFactorAffineModel::discount_bond) plus the factor
//! overload; three C++ pure-virtuals are intentionally absent:
//!
//! - `AffineModel::discount(Time)` (`onefactormodel.cpp:97`) routes through the
//! deferred `ShortRateDynamics`; `discountBond` takes an explicit rate and
//! does not call it, and the #378 oracle calls `discountBond` directly.
//! - `AffineModel::discountBondOption` (`model.hpp:54`) is left pure-virtual by
//! `OneFactorAffineModel` in C++ and needs the cumulative normal.
//! - `ShortRateModel::tree(const TimeGrid&)` (`model.hpp:144`) is the numerical
//! lattice path.
//!
//! ## Collapsed intermediates
//!
//! C++'s `ShortRateModel` (`model.hpp:141`) and `OneFactorModel`
//! (`onefactormodel.hpp:38`) sit between `CalibratedModel` and this trait. Each
//! adds only a constructor forwarding the argument count (subsumed by
//! [`CalibratedModel::new`](crate::models::CalibratedModel::new)) plus deferred
//! virtuals: `ShortRateModel` adds `tree()`; `OneFactorModel` adds `dynamics()`
//! and its `tree()` implementation, both numerical-tree machinery. With their
//! only non-deferred content subsumed, they carry no Rust surface this slice; a
//! concrete affine model embeds a `CalibratedModel` and implements
//! [`OneFactorAffineModel`] directly.
use Cell;
use crateArray;
use crateTimeGrid;
use crateTree;
use crateTreeLatticeImpl;
use crateTrinomialTree;
use crateShared;
use crateStochasticProcess1D;
use crate;
/// Analytically tractable model (`ql/models/model.hpp:45`), reduced to its one
/// non-deferred method: the zero-coupon bond price as a function of the state
/// factors.
/// Single-factor affine base (`onefactormodel.hpp:126`).
///
/// A concrete model supplies `A(t,T)` and `B(t,T)`; the closed-form
/// [`discount_bond`](Self::discount_bond) and, through the [`AffineModel`]
/// blanket impl, the factor overload follow.
///
/// C++'s `A`/`B` are distinct from a model's scalar parameter inspectors
/// `a()`/`b()` only by case; lowercased in Rust they share a name. The two
/// coexist on a concrete model (the inherent 0-argument inspector wins
/// method-syntax resolution, and [`discount_bond`](Self::discount_bond)
/// resolves against the trait bound), but where a model's [`a`](Self::a)
/// cross-references its [`b`](Self::b) it must qualify the call as
/// `OneFactorAffineModel::b(self, t, maturity)`.
/// `OneFactorAffineModel::discountBond(Array)` (`onefactormodel.hpp:132`)
/// delegates to the rate overload on the first factor.
/// Base description of a one-factor short-rate's dynamics
/// (`OneFactorModel::ShortRateDynamics`, `onefactormodel.hpp:54`).
///
/// A model maps between its Markov state variable `x` and the short rate `r`, and
/// exposes the risk-neutral process the state follows. The numerical tree
/// discretizes [`process`](Self::process) and reads [`short_rate`](Self::short_rate)
/// off each node to discount.
/// Recombining trinomial tree discretizing a short-rate model's state variable
/// (`OneFactorModel::ShortRateTree`, `onefactormodel.hpp:75`).
///
/// C++'s `ShortRateTree` *is-a* `TreeLattice1D<ShortRateTree>` (CRTP self-
/// inheritance) and supplies the per-node discount off the model's dynamics.
/// Here it is instead the [`TreeLatticeImpl`] callback surface a
/// [`TreeLattice1D`](crate::methods::lattices::TreeLattice1D) induces over: it
/// embeds the [`TrinomialTree`] (which serves `size`/`descendant`/`probability`/
/// `underlying`) and the [`ShortRateDynamics`], and computes
/// [`discount`](TreeLatticeImpl::discount)`(i, index) =
/// exp(-(short_rate(t_i, x) + spread) * dt_i)` (`onefactormodel.hpp:91`). It
/// carries its own [`TimeGrid`] clone because the discount callback needs
/// `t_i`/`dt_i`, and the impl is nested inside - not above - the lattice that
/// owns the grid (the [`TreeLattice1D`](crate::methods::lattices::TreeLattice1D)
/// test fixtures do the same).
///
/// Only the plain build-up is ported; the Brent-fitting ctor and its `Helper`
/// (`onefactormodel.cpp:28,56`) belong to the deferred generic `tree()` path
/// (Hull-White's `tree()` fits `phi` in closed form instead).