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
use num_complex::Complex;

/// Returns log of Gaussian characteristic function
/// # Examples
///
/// ```
/// extern crate num_complex;
/// use num_complex::Complex;
/// extern crate cf_functions;
/// # fn main() {
/// let u = Complex::new(1.0, 1.0);
/// let mu = 0.5;
/// let sigma = 0.3;
/// let log_cf = cf_functions::gauss::gauss_log_cf(
///     &u, mu, sigma
/// );
/// # }
/// ```
pub fn gauss_log_cf(u: &Complex<f64>, mu: f64, sigma: f64) -> Complex<f64> {
    u * mu + u * u * 0.5 * sigma.powi(2)
}

/// Returns log of Gaussian characteristic function with complex mu
/// # Examples
///
/// ```
/// extern crate num_complex;
/// use num_complex::Complex;
/// extern crate cf_functions;
/// # fn main() {
/// let u = Complex::new(1.0, 1.0);
/// let mu = Complex::new(1.0, 1.0);
/// let sigma = 0.3;
/// let log_cf = cf_functions::gauss::gauss_log_cf_cmp(
///     &u, &mu, sigma
/// );
/// # }
/// ```
pub fn gauss_log_cf_cmp(u: &Complex<f64>, mu: &Complex<f64>, sigma: f64) -> Complex<f64> {
    u * mu + u * u * 0.5 * sigma.powi(2)
}
/// Returns Gaussian characteristic function
/// # Examples
///
/// ```
/// extern crate num_complex;
/// use num_complex::Complex;
/// extern crate cf_functions;
/// # fn main() {
/// let u = Complex::new(1.0, 1.0);
/// let mu = 0.5;
/// let sigma = 0.3;
/// let cf = cf_functions::gauss::gauss_cf(
///     &u, mu, sigma
/// );
/// # }
/// ```
pub fn gauss_cf(u: &Complex<f64>, mu: f64, sigma: f64) -> Complex<f64> {
    gauss_log_cf(u, mu, sigma).exp()
}

/// Returns Heston model log CF.  Heston is simply a time changed gaussian model.
///
/// # Remarks
/// The time change is assumed to be a CIR process.
///
/// # Examples
///
/// ```
/// extern crate num_complex;
/// use num_complex::Complex;
/// extern crate cf_functions;
/// # fn main() {
/// let u = Complex::new(1.0, 1.0);
/// let sigma = 0.3; //square root of long run average
/// let t = 0.5; //time horizon
/// let speed = 0.5; //speed of mear reversion CIR process
/// let v0 = 0.25; //initial value of CIR process
/// let eta_v = 0.3; //volatility of CIR process (vol of vol)
/// let rho = -0.5; //correlation
/// let log_cf = cf_functions::gauss::heston_log_cf(
///     &u, t, sigma, v0,
///     speed, eta_v, rho
/// );
/// # }
/// ```
pub fn heston_log_cf(
    u: &Complex<f64>,
    t: f64,
    sigma: f64,
    v0: f64,
    speed: f64,
    eta_v: f64,
    rho: f64,
) -> Complex<f64> {
    let sigma_sq = sigma.powi(2);
    crate::cir::generic_leverage_diffusion(
        u,
        &|u| gauss_log_cf(u, -0.5 * sigma_sq, sigma),
        t,
        sigma,
        v0 / sigma_sq,
        speed,
        eta_v / sigma,
        rho,
    )
}

/// Returns Heston model log CF.  Heston is simply a time changed gaussian model.
///
/// # Remarks
/// The time change is assumed to be a CIR process
///
/// # Examples
///
/// ```
/// extern crate num_complex;
/// use num_complex::Complex;
/// extern crate cf_functions;
/// # fn main() {
/// let u = Complex::new(1.0, 1.0);
/// let sigma = 0.3; //square root of long run average
/// let t = 0.5; //time horizon
/// let rate = 0.05;
/// let speed = 0.5; //speed of mean reversion of CIR process
/// let v0 = 0.29; //initial value of CIR process
/// let eta_v = 0.3; //volatility of CIR process (vol of vol)
/// let rho = -0.5; //correlation between diffusions
/// let cf = cf_functions::gauss::heston_cf(
///     t, rate,
///     sigma, v0, speed, eta_v, rho
/// );
/// let value_of_cf=cf(&Complex::new(0.05, -0.5));
/// # }
/// ```
pub fn heston_cf(
    t: f64,
    rate: f64,
    sigma: f64,
    v0: f64,
    speed: f64,
    eta_v: f64,
    rho: f64,
) -> impl Fn(&Complex<f64>) -> Complex<f64> {
    move |u| (rate * t * u + heston_log_cf(u, t, sigma, v0, speed, eta_v, rho)).exp()
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn cir_heston_2() {
        let t = 0.25;
        let k = 0.2;
        let v0 = 0.98;
        let sig = 0.2;
        let rho = -0.3;
        let sig_tot = 0.3;
        let u = Complex::new(0.5, 0.5);
        let neg_psi = 0.5 * sig_tot * sig_tot * (u - u * u);
        let k_star = k - u * rho * sig * sig_tot;
        let ada = (k_star * k_star + 2.0 * sig * sig * neg_psi as Complex<f64>).sqrt();
        let b_t = 2.0 * neg_psi * (1.0 - (-ada * t).exp())
            / (2.0 * ada - (ada - k_star) * (1.0 - (-ada * t).exp()));
        let c_t = (k / (sig * sig))
            * (2.0 * (1.0 - (1.0 - (-ada * t).exp()) * (ada - k_star) / (2.0 * ada)).ln()
                + (ada - k_star) * t);
        let cf_heston = (-b_t * v0 - c_t).exp().re;
        let approx_heston_cf =
            heston_cf(t, 0.0, sig_tot, v0 * sig_tot.powi(2), k, sig * sig_tot, rho)(&u).re;
        assert_eq!(cf_heston, approx_heston_cf);
    }
}