nautilus_model/python/data/
greeks.rs1use nautilus_core::{UnixNanos, python::to_pyvalue_err};
17use pyo3::{prelude::*, types::PyType};
18
19use crate::data::greeks::{
20 BlackScholesGreeksResult, GreeksData, OptionGreekValues, PortfolioGreeks, YieldCurve,
21 black_scholes_greeks_checked, imply_vol_and_greeks_checked, imply_vol_checked,
22};
23
24#[cfg(feature = "python")]
25#[pymethods]
26#[pyo3_stub_gen::derive::gen_stub_pymethods]
27impl OptionGreekValues {
28 #[getter]
29 fn delta(&self) -> f64 {
30 self.delta
31 }
32
33 #[getter]
34 fn gamma(&self) -> f64 {
35 self.gamma
36 }
37
38 #[getter]
39 fn vega(&self) -> f64 {
40 self.vega
41 }
42
43 #[getter]
44 fn theta(&self) -> f64 {
45 self.theta
46 }
47
48 #[getter]
49 fn rho(&self) -> f64 {
50 self.rho
51 }
52}
53
54#[cfg(feature = "python")]
55#[pymethods]
56#[pyo3_stub_gen::derive::gen_stub_pymethods]
57impl GreeksData {
58 #[classmethod]
59 #[pyo3(name = "from_delta", signature = (instrument_id, delta, multiplier, ts_event=0))]
60 fn py_from_delta(
61 _cls: &Bound<'_, PyType>,
62 instrument_id: crate::identifiers::InstrumentId,
63 delta: f64,
64 multiplier: f64,
65 ts_event: u64,
66 ) -> Self {
67 Self::from_delta(instrument_id, delta, multiplier, UnixNanos::from(ts_event))
68 }
69
70 #[pyo3(name = "to_portfolio_greeks")]
74 fn py_to_portfolio_greeks(&self) -> PortfolioGreeks {
75 self.to_portfolio_greeks()
76 }
77
78 fn __rmul__(&self, scalar: f64) -> PortfolioGreeks {
79 scalar * &self.to_portfolio_greeks()
80 }
81
82 #[getter]
83 fn instrument_id(&self) -> crate::identifiers::InstrumentId {
84 self.instrument_id
85 }
86
87 #[getter]
88 fn is_call(&self) -> bool {
89 self.is_call
90 }
91
92 #[getter]
93 fn strike(&self) -> f64 {
94 self.strike
95 }
96
97 #[getter]
98 fn expiry(&self) -> i32 {
99 self.expiry
100 }
101
102 #[getter]
103 fn expiry_in_days(&self) -> i32 {
104 self.expiry_in_days
105 }
106
107 #[getter]
108 fn expiry_in_years(&self) -> f64 {
109 self.expiry_in_years
110 }
111
112 #[getter]
113 fn multiplier(&self) -> f64 {
114 self.multiplier
115 }
116
117 #[getter]
118 fn quantity(&self) -> f64 {
119 self.quantity
120 }
121
122 #[getter]
123 fn underlying_price(&self) -> f64 {
124 self.underlying_price
125 }
126
127 #[getter]
128 fn interest_rate(&self) -> f64 {
129 self.interest_rate
130 }
131
132 #[getter]
133 fn cost_of_carry(&self) -> f64 {
134 self.cost_of_carry
135 }
136
137 #[getter]
138 fn vol(&self) -> f64 {
139 self.vol
140 }
141
142 #[getter]
143 fn pnl(&self) -> f64 {
144 self.pnl
145 }
146
147 #[getter]
148 fn price(&self) -> f64 {
149 self.price
150 }
151
152 #[getter]
153 fn delta(&self) -> f64 {
154 self.greeks.delta
155 }
156
157 #[getter]
158 fn gamma(&self) -> f64 {
159 self.greeks.gamma
160 }
161
162 #[getter]
163 fn vega(&self) -> f64 {
164 self.greeks.vega
165 }
166
167 #[getter]
168 fn theta(&self) -> f64 {
169 self.greeks.theta
170 }
171
172 #[getter]
173 fn rho(&self) -> f64 {
174 self.greeks.rho
175 }
176
177 #[getter]
178 fn itm_prob(&self) -> f64 {
179 self.itm_prob
180 }
181
182 #[getter]
183 fn ts_event(&self) -> u64 {
184 self.ts_event.as_u64()
185 }
186
187 #[getter]
188 fn ts_init(&self) -> u64 {
189 self.ts_init.as_u64()
190 }
191}
192
193#[cfg(feature = "python")]
194#[pymethods]
195#[pyo3_stub_gen::derive::gen_stub_pymethods]
196impl PortfolioGreeks {
197 fn __rmul__(&self, scalar: f64) -> Self {
198 scalar * self
199 }
200
201 fn __add__(&self, other: &Self) -> Self {
202 self.clone() + other.clone()
203 }
204
205 #[getter]
206 fn pnl(&self) -> f64 {
207 self.pnl
208 }
209
210 #[getter]
211 fn price(&self) -> f64 {
212 self.price
213 }
214
215 #[getter]
216 fn delta(&self) -> f64 {
217 self.greeks.delta
218 }
219
220 #[getter]
221 fn gamma(&self) -> f64 {
222 self.greeks.gamma
223 }
224
225 #[getter]
226 fn vega(&self) -> f64 {
227 self.greeks.vega
228 }
229
230 #[getter]
231 fn theta(&self) -> f64 {
232 self.greeks.theta
233 }
234
235 #[getter]
236 fn rho(&self) -> f64 {
237 self.greeks.rho
238 }
239
240 #[getter]
241 fn ts_event(&self) -> u64 {
242 self.ts_event.as_u64()
243 }
244
245 #[getter]
246 fn ts_init(&self) -> u64 {
247 self.ts_init.as_u64()
248 }
249}
250
251#[cfg(feature = "python")]
252#[pymethods]
253#[pyo3_stub_gen::derive::gen_stub_pymethods]
254impl YieldCurve {
255 #[new]
256 fn py_new(
257 curve_name: String,
258 tenors: Vec<f64>,
259 interest_rates: Vec<f64>,
260 ts_event: u64,
261 ts_init: u64,
262 ) -> PyResult<Self> {
263 let curve = Self::new(
264 curve_name,
265 tenors,
266 interest_rates,
267 ts_event.into(),
268 ts_init.into(),
269 );
270 curve.validate().map_err(to_pyvalue_err)?;
271 Ok(curve)
272 }
273
274 fn __repr__(&self) -> String {
275 self.to_string()
276 }
277
278 #[getter]
279 fn curve_name(&self) -> &str {
280 &self.curve_name
281 }
282
283 #[getter]
284 fn tenors(&self) -> Vec<f64> {
285 self.tenors.clone()
286 }
287
288 #[getter]
289 fn interest_rates(&self) -> Vec<f64> {
290 self.interest_rates.clone()
291 }
292
293 #[getter]
294 fn ts_event(&self) -> u64 {
295 self.ts_event.as_u64()
296 }
297
298 #[getter]
299 fn ts_init(&self) -> u64 {
300 self.ts_init.as_u64()
301 }
302}
303
304#[cfg(feature = "python")]
305#[pymethods]
306#[pyo3_stub_gen::derive::gen_stub_pymethods]
307impl BlackScholesGreeksResult {
308 #[getter]
309 fn price(&self) -> f64 {
310 self.price
311 }
312
313 #[getter]
314 fn vol(&self) -> f64 {
315 self.vol
316 }
317
318 #[getter]
319 fn delta(&self) -> f64 {
320 self.delta
321 }
322
323 #[getter]
324 fn gamma(&self) -> f64 {
325 self.gamma
326 }
327
328 #[getter]
329 fn vega(&self) -> f64 {
330 self.vega
331 }
332
333 #[getter]
334 fn theta(&self) -> f64 {
335 self.theta
336 }
337
338 #[getter]
339 fn itm_prob(&self) -> f64 {
340 self.itm_prob
341 }
342
343 #[getter]
344 fn rho(&self) -> f64 {
345 self.rho
346 }
347}
348
349#[doc = "
352
353`db_dr` is zero for fixed carry and one for carry defined as `r - q`, including `r == q`.
354Rho is per percentage point.
355The exact implementation is used when the fast result is nonfinite or inconsistent.
356
357# Errors
358
359Returns an error if:
360- Model inputs are invalid.
361- Valuation results are nonfinite or inconsistent."]
362#[pyfunction]
363#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.model")]
364#[pyo3(name = "black_scholes_greeks", signature = (s, r, b, vol, is_call, k, t, db_dr=0.0))]
365#[expect(clippy::too_many_arguments)]
366pub fn py_black_scholes_greeks(
367 s: f64,
368 r: f64,
369 b: f64,
370 vol: f64,
371 is_call: bool,
372 k: f64,
373 t: f64,
374 db_dr: f64,
375) -> PyResult<BlackScholesGreeksResult> {
376 black_scholes_greeks_checked(s, r, b, vol, is_call, k, t, db_dr).map_err(to_pyvalue_err)
377}
378
379#[pyfunction]
385#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.model")]
386#[pyo3(name = "imply_vol")]
387pub fn py_imply_vol(
388 s: f64,
389 r: f64,
390 b: f64,
391 is_call: bool,
392 k: f64,
393 t: f64,
394 price: f64,
395) -> PyResult<f64> {
396 imply_vol_checked(s, r, b, is_call, k, t, price).map_err(to_pyvalue_err)
397}
398
399#[doc = "
402
403`db_dr` is zero for fixed carry and one for carry defined as `r - q`, including `r == q`.
404Rho is per percentage point.
405The exact implementation is used when the fast kernel cannot reprice the solved premium
406within 0.01 percent.
407
408# Errors
409
410Returns an error if:
411- Model inputs or the premium are invalid.
412- The premium has no finite positive implied volatility.
413- Results are nonfinite, inconsistent, or fail the 0.01 percent repricing tolerance."]
414#[pyfunction]
415#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.model")]
416#[pyo3(name = "imply_vol_and_greeks", signature = (s, r, b, is_call, k, t, price, db_dr=0.0))]
417#[expect(clippy::too_many_arguments)]
418pub fn py_imply_vol_and_greeks(
419 s: f64,
420 r: f64,
421 b: f64,
422 is_call: bool,
423 k: f64,
424 t: f64,
425 price: f64,
426 db_dr: f64,
427) -> PyResult<BlackScholesGreeksResult> {
428 imply_vol_and_greeks_checked(s, r, b, is_call, k, t, price, db_dr, None).map_err(to_pyvalue_err)
429}
430
431#[doc = "
435
436`db_dr` is zero for fixed carry and one for carry defined as `r - q`, including `r == q`.
437Rho is per percentage point.
438
439Refinement falls back to the full implied-volatility solver when its volatility reaches a
440clamp bound or it fails to reprice the premium within 0.01 percent.
441The exact implementation is used when the fast kernel cannot reprice the solved premium
442within that tolerance.
443
444# Errors
445
446Returns an error if:
447- Model inputs or the premium are invalid.
448- The premium has no finite positive implied volatility.
449- Results are nonfinite, inconsistent, or fail the 0.01 percent repricing tolerance."]
450#[pyfunction]
451#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.model")]
452#[pyo3(name = "refine_vol_and_greeks", signature = (s, r, b, is_call, k, t, target_price, initial_vol, db_dr=0.0))]
453#[expect(clippy::too_many_arguments)]
454pub fn py_refine_vol_and_greeks(
455 s: f64,
456 r: f64,
457 b: f64,
458 is_call: bool,
459 k: f64,
460 t: f64,
461 target_price: f64,
462 initial_vol: f64,
463 db_dr: f64,
464) -> PyResult<BlackScholesGreeksResult> {
465 imply_vol_and_greeks_checked(
466 s,
467 r,
468 b,
469 is_call,
470 k,
471 t,
472 target_price,
473 db_dr,
474 Some(initial_vol),
475 )
476 .map_err(to_pyvalue_err)
477}