quant_opts/core/
mod.rs

1//! Core domain types for `quant-opts`.
2//!
3//! These types describe *what* is being priced (vanilla options) and the
4//! surrounding market data, independently of any particular pricing model
5//! (Black–Scholes, SABR, etc.).
6
7use std::{
8    fmt::{Display, Formatter, Result as FmtResult},
9    ops::Neg,
10};
11
12use num_traits::ConstZero;
13
14/// The type of option to be priced (call or put).
15#[derive(Debug, Clone, Eq, PartialEq, Copy)]
16#[repr(i8)]
17pub enum OptionType {
18    Call = 1,
19    Put = -1,
20}
21
22impl Neg for OptionType {
23    type Output = Self;
24
25    #[inline]
26    fn neg(self) -> Self::Output {
27        match self {
28            OptionType::Call => OptionType::Put,
29            OptionType::Put => OptionType::Call,
30        }
31    }
32}
33
34impl Display for OptionType {
35    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
36        match self {
37            OptionType::Call => write!(f, "Call"),
38            OptionType::Put => write!(f, "Put"),
39        }
40    }
41}
42
43macro_rules! impl_option_type {
44    ($type:ty) => {
45        impl From<OptionType> for $type {
46            #[inline]
47            fn from(val: OptionType) -> Self {
48                <$type>::from(val as i8)
49            }
50        }
51
52        impl From<$type> for OptionType {
53            #[inline]
54            fn from(value: $type) -> Self {
55                if value >= <$type>::ZERO {
56                    OptionType::Call
57                } else {
58                    OptionType::Put
59                }
60            }
61        }
62
63        impl std::ops::Mul<OptionType> for $type {
64            type Output = $type;
65
66            #[inline]
67            fn mul(self, rhs: OptionType) -> Self::Output {
68                match rhs {
69                    OptionType::Call => self,
70                    OptionType::Put => -self,
71                }
72            }
73        }
74
75        impl std::ops::Mul<$type> for OptionType {
76            type Output = $type;
77
78            #[inline]
79            fn mul(self, rhs: $type) -> Self::Output {
80                match self {
81                    OptionType::Call => rhs,
82                    OptionType::Put => -rhs,
83                }
84            }
85        }
86    };
87}
88
89impl_option_type!(f32);
90impl_option_type!(f64);
91impl_option_type!(i8);
92impl_option_type!(i16);
93impl_option_type!(i32);
94impl_option_type!(i64);
95impl_option_type!(i128);
96impl_option_type!(isize);
97
98/// Exercise style of a vanilla option.
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub enum OptionStyle {
101    /// European-style option (exercise only at maturity).
102    European,
103    /// American-style option (exercise any time up to maturity).
104    American,
105}
106
107/// Specification of a single vanilla option contract.
108///
109/// This struct is model-agnostic; pricing models interpret the fields
110/// according to their own assumptions.
111#[derive(Debug, Clone, Copy, PartialEq)]
112pub struct VanillaOption {
113    /// Exercise style (European or American).
114    pub style: OptionStyle,
115    /// Payoff direction (call or put).
116    pub kind: OptionType,
117    /// Strike price of the option.
118    pub strike: f64,
119    /// Time to maturity in years.
120    ///
121    /// The exact day-count convention (e.g. ACT/365 or ACT/365.25) is
122    /// determined by how `t` is computed by the caller. The current
123    /// library typically uses 365.25 days per year.
124    pub maturity: f64,
125}
126
127impl VanillaOption {
128    /// Creates a new vanilla option from its components.
129    #[inline]
130    pub fn new(style: OptionStyle, kind: OptionType, strike: f64, maturity: f64) -> Self {
131        Self {
132            style,
133            kind,
134            strike,
135            maturity,
136        }
137    }
138
139    /// Convenience constructor for a European call.
140    #[inline]
141    pub fn european_call(strike: f64, maturity: f64) -> Self {
142        Self::new(OptionStyle::European, OptionType::Call, strike, maturity)
143    }
144
145    /// Convenience constructor for a European put.
146    #[inline]
147    pub fn european_put(strike: f64, maturity: f64) -> Self {
148        Self::new(OptionStyle::European, OptionType::Put, strike, maturity)
149    }
150
151    /// Convenience constructor for an American call.
152    #[inline]
153    pub fn american_call(strike: f64, maturity: f64) -> Self {
154        Self::new(OptionStyle::American, OptionType::Call, strike, maturity)
155    }
156
157    /// Convenience constructor for an American put.
158    #[inline]
159    pub fn american_put(strike: f64, maturity: f64) -> Self {
160        Self::new(OptionStyle::American, OptionType::Put, strike, maturity)
161    }
162}
163
164/// Market data required for pricing a single underlying.
165#[derive(Debug, Clone, Copy, PartialEq)]
166pub struct MarketData {
167    /// Current spot price of the underlying.
168    pub spot: f64,
169    /// Continuously-compounded risk-free interest rate.
170    pub rate: f64,
171    /// Continuously-compounded dividend yield (or cost of carry).
172    pub dividend_yield: f64,
173}
174
175impl MarketData {
176    /// Creates a new `MarketData` instance.
177    #[inline]
178    pub fn new(spot: f64, rate: f64, dividend_yield: f64) -> Self {
179        Self {
180            spot,
181            rate,
182            dividend_yield,
183        }
184    }
185}
186
187/// Collection of Greeks for a vanilla option.
188///
189/// All fields are optional from a modeling perspective; a particular model
190/// may choose to populate only a subset. By default they are initialized
191/// to zero.
192#[allow(clippy::struct_excessive_bools)]
193#[derive(Debug, Clone, Copy, Default, PartialEq)]
194pub struct Greeks {
195    pub delta: f64,
196    pub gamma: f64,
197    pub theta: f64,
198    pub vega: f64,
199    pub rho: f64,
200    pub epsilon: f64,
201    pub lambda: f64,
202    pub vanna: f64,
203    pub charm: f64,
204    pub veta: f64,
205    pub vomma: f64,
206    pub speed: f64,
207    pub zomma: f64,
208    pub color: f64,
209    pub ultima: f64,
210    pub dual_delta: f64,
211    pub dual_gamma: f64,
212}