pub struct Quote {
pub value: f64,
pub bid: f64,
pub ask: f64,
pub mid: f64,
}Fields§
§value: f64§bid: f64§ask: f64§mid: f64Implementations§
Source§impl Quote
impl Quote
Sourcepub fn new(value: f64) -> Self
pub fn new(value: f64) -> Self
Examples found in repository?
examples/local_vol_calibration.rs (line 60)
34fn main() {
35 common::title("LOCAL VOLATILITY — quotes -> implied surface -> Dupire -> reprice");
36
37 let maturities = [
38 (NaiveDate::from_ymd_opt(2026, 7, 2).unwrap(), 0.23),
39 (NaiveDate::from_ymd_opt(2027, 1, 1).unwrap(), 0.25),
40 ];
41
42 common::section("Step 1: generate market quotes from a known skew");
43 println!(" sigma(K, T) = base(T) - 0.001 * (K - 100)");
44 let mut quotes = Vec::new();
45 for (maturity, base_vol) in maturities {
46 let t = (maturity - asof()).num_days() as f64 / 365.0;
47 for i in 0..13 {
48 let strike = 70.0 + 5.0 * i as f64;
49 let vol = true_vol(strike, base_vol);
50 let price = bs_price(SPOT, strike, RATE, 0.0, vol, t, PutOrCall::Call);
51 let mut option = EquityOptionBuilder::new()
52 .spot(SPOT)
53 .strike(strike)
54 .flat_vol(0.2) // placeholder: the solve does not use it
55 .flat_rate(RATE)
56 .valuation_date(asof())
57 .maturity_date(maturity)
58 .vanilla(PutOrCall::Call)
59 .build();
60 option.base.current_price = Quote::new(price);
61 quotes.push(Box::new(option));
62 }
63 }
64 println!(" {} quotes across {} expiries", quotes.len(), maturities.len());
65
66 common::section("Step 2: back out implied vols and build the surface");
67 let surface = build_implied_vol_surface("es).expect("calibration failed");
68 println!("{surface}");
69
70 common::section("Step 3: check the surface recovers the input smile");
71 for (t, base_vol) in [(182.0 / 365.0, 0.23), (1.0, 0.25)] {
72 for strike in [70.0, 85.0, 100.0, 115.0, 130.0] {
73 let recovered = surface.vol(strike, SPOT, t);
74 common::check(
75 &format!("T={t:.3} K={strike}"),
76 recovered,
77 true_vol(strike, base_vol),
78 1e-6,
79 );
80 }
81 }
82
83 common::section("Step 4: Dupire local volatility from that surface");
84 let curve =
85 YieldCurve::flat(RATE, asof(), DayCountConvention::Act365, Compounding::Continuous).unwrap();
86 let lv = LocalVol::new(&surface, &curve, SPOT, 0.0, 0.0);
87 println!(" {:>8} {:>12} {:>12} {:>12}", "level", "t=0.25", "t=0.50", "t=1.00");
88 for level in [70.0, 85.0, 100.0, 115.0, 130.0] {
89 println!(
90 " {level:>8.1} {:>12.4} {:>12.4} {:>12.4}",
91 lv.vol(level, 0.25),
92 lv.vol(level, 0.50),
93 lv.vol(level, 1.00)
94 );
95 }
96 common::note("local vol is steeper in strike than implied vol (the 'twice the slope' rule)");
97 common::note("the far wings are noisy: Dupire takes numerical derivatives of a");
98 common::note("piecewise-linear surface with flat extrapolation — trust the interior.");
99
100 common::section("Step 5: reprice the calibrating vanillas through local vol MC");
101 common::table_header();
102 for strike in [90.0, 100.0, 110.0] {
103 let expected = bs_price(SPOT, strike, RATE, 0.0, true_vol(strike, 0.25), 1.0, PutOrCall::Call);
104 common::row(
105 &format!("local vol MC, K={strike}"),
106 &EquityOptionBuilder::new()
107 .spot(SPOT)
108 .strike(strike)
109 .vol_surface(surface.clone())
110 .flat_rate(RATE)
111 .valuation_date(asof())
112 .maturity_date(NaiveDate::from_ymd_opt(2027, 1, 1).unwrap())
113 .vanilla(PutOrCall::Call)
114 .engine(Engine::MonteCarlo)
115 .model(McModel::LocalVol)
116 .paths(50_000)
117 .build(),
118 );
119 println!("{:<34} {expected:>12.6} <- Black-Scholes target at the quoted smile vol", "");
120 }
121
122 common::section("Local vol on the finite difference engine (no sampling noise)");
123 common::table_header();
124 for strike in [90.0, 100.0, 110.0] {
125 common::row(
126 &format!("local vol FD, K={strike}"),
127 &EquityOptionBuilder::new()
128 .spot(SPOT)
129 .strike(strike)
130 .vol_surface(surface.clone())
131 .flat_rate(RATE)
132 .valuation_date(asof())
133 .maturity_date(NaiveDate::from_ymd_opt(2027, 1, 1).unwrap())
134 .vanilla(PutOrCall::Call)
135 .engine(Engine::FiniteDifference)
136 .model(McModel::LocalVol)
137 .build(),
138 );
139 }
140
141 common::section("Sanity: a flat surface must give flat local vol");
142 let flat = VolSurface::flat(0.25, asof(), DayCountConvention::Act365).unwrap();
143 let flat_lv = LocalVol::new(&flat, &curve, SPOT, 0.0, 0.0);
144 for (level, t) in [(70.0, 0.25), (100.0, 1.0), (130.0, 2.0)] {
145 common::check(&format!("sigma_loc({level}, {t})"), flat_lv.vol(level, t), 0.25, 1e-6);
146 }
147
148 common::section("Term structure: local vol is the forward variance");
149 let term = VolSurface::from_strike_smiles(
150 &[Tenor::YearFraction(0.5), Tenor::YearFraction(1.0)],
151 &[vec![(100.0, 0.20)], vec![(100.0, 0.25)]],
152 asof(),
153 DayCountConvention::Act365,
154 )
155 .unwrap();
156 let term_lv = LocalVol::new(&term, &curve, SPOT, 0.0, 0.0);
157 // (0.25^2 * 1 - 0.20^2 * 0.5) / 0.5 = 0.085
158 common::check(
159 "sigma_loc between pillars = sqrt(fwd variance)",
160 term_lv.vol(100.0, 0.75),
161 0.085_f64.sqrt(),
162 1e-3,
163 );
164 println!();
165}pub fn value(&self) -> f64
pub fn valid_value(&self) -> bool
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Quote
impl RefUnwindSafe for Quote
impl Send for Quote
impl Sync for Quote
impl Unpin for Quote
impl UnsafeUnpin for Quote
impl UnwindSafe for Quote
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more