Skip to main content

HestonParams

Struct HestonParams 

Source
pub struct HestonParams {
    pub v0: f64,
    pub kappa: f64,
    pub theta: f64,
    pub vol_of_vol: f64,
    pub rho: f64,
}
Expand description

Heston parameters. theta is the long-run variance, v0 the initial variance, vol_of_vol the volatility of variance (often written xi or sigma), rho the spot-variance correlation.

Fields§

§v0: f64§kappa: f64§theta: f64§vol_of_vol: f64§rho: f64

Implementations§

Source§

impl HestonParams

Source

pub fn validate(&self) -> Result<(), String>

Source

pub fn feller_condition_holds(&self) -> bool

Whether the Feller condition 2 kappa theta >= vol_of_vol^2 holds (if not, the variance process can touch zero; pricing still works).

Examples found in repository?
examples/heston_option.rs (line 48)
40fn main() {
41    let p = params();
42    common::title(&format!(
43        "HESTON — v0={} kappa={} theta={} vol-of-vol={} rho={}",
44        p.v0, p.kappa, p.theta, p.vol_of_vol, p.rho
45    ));
46    common::note(&format!(
47        "Feller condition 2*kappa*theta >= vol_of_vol^2: {}",
48        if p.feller_condition_holds() { "holds" } else { "VIOLATED (variance can touch zero)" }
49    ));
50
51    common::section("Vanilla: semi-analytic vs Monte Carlo");
52    common::table_header();
53    for pc in [PutOrCall::Call, PutOrCall::Put] {
54        common::row(
55            &format!("Analytical (char. function), {pc:?}"),
56            &base().vanilla(pc).engine(Engine::BlackScholes).build(),
57        );
58        common::row(
59            &format!("Monte Carlo (full-trunc Euler), {pc:?}"),
60            &base().vanilla(pc).engine(Engine::MonteCarlo).paths(100_000).build(),
61        );
62    }
63    common::row(
64        "Finite difference (unsupported)",
65        &base().vanilla(PutOrCall::Call).engine(Engine::FiniteDifference).build(),
66    );
67    common::note("MC vega/theta bump sqrt(v0) and sqrt(theta) in parallel");
68
69    common::section("Binaries under Heston");
70    common::table_header();
71    common::row(
72        "Cash-or-nothing call (analytic)",
73        &base()
74            .binary(PutOrCall::Call, BinaryType::CashOrNothing, 1.0)
75            .engine(Engine::BlackScholes)
76            .build(),
77    );
78    common::row(
79        "Cash-or-nothing call (MC)",
80        &base()
81            .binary(PutOrCall::Call, BinaryType::CashOrNothing, 1.0)
82            .engine(Engine::MonteCarlo)
83            .paths(100_000)
84            .build(),
85    );
86    common::row(
87        "Asset-or-nothing call (analytic)",
88        &base()
89            .binary(PutOrCall::Call, BinaryType::AssetOrNothing, 0.0)
90            .engine(Engine::BlackScholes)
91            .build(),
92    );
93
94    common::section("Path-dependent payoffs (Monte Carlo only)");
95    common::table_header();
96    common::row(
97        "Down-and-out call H=85",
98        &base()
99            .barrier(PutOrCall::Call, BarrierDirection::Down, KnockType::Out, 85.0)
100            .engine(Engine::MonteCarlo)
101            .paths(50_000)
102            .build(),
103    );
104    common::row(
105        "Down-and-in put H=85",
106        &base()
107            .barrier(PutOrCall::Put, BarrierDirection::Down, KnockType::In, 85.0)
108            .engine(Engine::MonteCarlo)
109            .paths(50_000)
110            .build(),
111    );
112
113    common::section("Identities");
114    let call = base().vanilla(PutOrCall::Call).engine(Engine::BlackScholes).build();
115    let put = base().vanilla(PutOrCall::Put).engine(Engine::BlackScholes).build();
116    let parity = SPOT * (-DIV * 1.0_f64).exp() - STRIKE * (-RATE * 1.0_f64).exp();
117    common::check("put-call parity", call.npv() - put.npv(), parity, 1e-10);
118    let asset = base()
119        .binary(PutOrCall::Call, BinaryType::AssetOrNothing, 0.0)
120        .engine(Engine::BlackScholes)
121        .build();
122    let k_cash = base()
123        .binary(PutOrCall::Call, BinaryType::CashOrNothing, STRIKE)
124        .engine(Engine::BlackScholes)
125        .build();
126    common::check("vanilla = asset digital - K cash digitals", call.npv(), asset.npv() - k_cash.npv(), 1e-10);
127    common::check(
128        "vol-of-vol -> 0 degenerates to Black-Scholes",
129        heston_price(
130            SPOT,
131            STRIKE,
132            RATE,
133            DIV,
134            1.0,
135            &HestonParams { vol_of_vol: 1e-4, ..params() },
136            PutOrCall::Call,
137        ),
138        bs_price(SPOT, STRIKE, RATE, DIV, p.v0.sqrt(), 1.0, PutOrCall::Call),
139        1e-4,
140    );
141
142    common::section("The Heston smile (implied vol backed out of Heston prices)");
143    println!("  {:>8} {:>14} {:>14}", "strike", "heston price", "implied vol");
144    for k in [70.0, 80.0, 90.0, 100.0, 110.0, 120.0, 130.0] {
145        let price = heston_price(SPOT, k, RATE, DIV, 1.0, &p, PutOrCall::Call);
146        let iv = implied_vol_from_price(SPOT, k, RATE, DIV, 1.0, price, PutOrCall::Call)
147            .unwrap_or(f64::NAN);
148        println!("  {k:>8.1} {price:>14.6} {:>13.4}%", iv * 100.0);
149    }
150    common::note("rho < 0 tilts the smile: low strikes carry higher implied vol");
151
152    common::section("Correlation and vol-of-vol control the smile shape");
153    println!("  {:>6} {:>8} {:>12} {:>12} {:>12}", "rho", "vol-of-vol", "iv(80)", "iv(100)", "iv(120)");
154    for (rho, vov) in [(-0.7, 0.4), (0.0, 0.4), (0.7, 0.4), (-0.7, 0.1), (-0.7, 0.8)] {
155        let hp = HestonParams { rho, vol_of_vol: vov, ..params() };
156        let iv = |k: f64| {
157            let price = heston_price(SPOT, k, RATE, DIV, 1.0, &hp, PutOrCall::Call);
158            implied_vol_from_price(SPOT, k, RATE, DIV, 1.0, price, PutOrCall::Call)
159                .unwrap_or(f64::NAN)
160                * 100.0
161        };
162        println!("  {rho:>6.1} {vov:>10.1} {:>11.3}% {:>11.3}% {:>11.3}%", iv(80.0), iv(100.0), iv(120.0));
163    }
164    common::note("rho controls the skew (tilt); vol-of-vol controls the smile (curvature)");
165    println!();
166}
Source

pub fn with_vol_shift(&self, shift: f64) -> HestonParams

Parameters with a parallel shift applied to the instantaneous and long-run vol (used for vega bump-and-reprice).

Trait Implementations§

Source§

impl Clone for HestonParams

Source§

fn clone(&self) -> HestonParams

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for HestonParams

Source§

impl Debug for HestonParams

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for HestonParams

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for HestonParams

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V