- Bumped version numbers across Cargo.toml, Cargo.lock, pyproject.toml, and conda/meta.yaml to 1.1.3. - Added new features including American option pricing, digital options, extended Greeks, and historical volatility estimators. - Enhanced documentation and tests for new functionalities. - Updated CHANGELOG.md to reflect changes for version 1.1.3.
243 lines
7.6 KiB
Rust
243 lines
7.6 KiB
Rust
use crate::validation;
|
|
use numpy::{IntoPyArray, PyArray1, PyReadonlyArray1};
|
|
use pyo3::prelude::*;
|
|
|
|
type ExtendedGreekArrays<'py> = (
|
|
Bound<'py, PyArray1<f64>>,
|
|
Bound<'py, PyArray1<f64>>,
|
|
Bound<'py, PyArray1<f64>>,
|
|
Bound<'py, PyArray1<f64>>,
|
|
Bound<'py, PyArray1<f64>>,
|
|
);
|
|
|
|
type GreekArrays<'py> = (
|
|
Bound<'py, PyArray1<f64>>,
|
|
Bound<'py, PyArray1<f64>>,
|
|
Bound<'py, PyArray1<f64>>,
|
|
Bound<'py, PyArray1<f64>>,
|
|
Bound<'py, PyArray1<f64>>,
|
|
);
|
|
|
|
#[pyfunction]
|
|
#[pyo3(signature = (underlying, strike, rate, time_to_expiry, volatility, option_type = "call", model = "bsm", carry = 0.0))]
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn option_greeks(
|
|
underlying: f64,
|
|
strike: f64,
|
|
rate: f64,
|
|
time_to_expiry: f64,
|
|
volatility: f64,
|
|
option_type: &str,
|
|
model: &str,
|
|
carry: f64,
|
|
) -> PyResult<(f64, f64, f64, f64, f64)> {
|
|
let kind = super::parse_option_kind(option_type)?;
|
|
let model = super::parse_pricing_model(model)?;
|
|
let greeks =
|
|
ferro_ta_core::options::greeks::model_greeks(ferro_ta_core::options::OptionEvaluation {
|
|
contract: ferro_ta_core::options::OptionContract {
|
|
model,
|
|
underlying,
|
|
strike,
|
|
rate,
|
|
carry,
|
|
time_to_expiry,
|
|
kind,
|
|
},
|
|
volatility,
|
|
});
|
|
Ok((
|
|
greeks.delta,
|
|
greeks.gamma,
|
|
greeks.vega,
|
|
greeks.theta,
|
|
greeks.rho,
|
|
))
|
|
}
|
|
|
|
#[pyfunction]
|
|
#[pyo3(signature = (underlying, strike, rate, time_to_expiry, volatility, option_type = "call", model = "bsm", carry = None))]
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn option_greeks_batch<'py>(
|
|
py: Python<'py>,
|
|
underlying: PyReadonlyArray1<'py, f64>,
|
|
strike: PyReadonlyArray1<'py, f64>,
|
|
rate: PyReadonlyArray1<'py, f64>,
|
|
time_to_expiry: PyReadonlyArray1<'py, f64>,
|
|
volatility: PyReadonlyArray1<'py, f64>,
|
|
option_type: &str,
|
|
model: &str,
|
|
carry: Option<PyReadonlyArray1<'py, f64>>,
|
|
) -> PyResult<GreekArrays<'py>> {
|
|
let kind = super::parse_option_kind(option_type)?;
|
|
let model = super::parse_pricing_model(model)?;
|
|
let underlying = underlying.as_slice()?;
|
|
let strike = strike.as_slice()?;
|
|
let rate = rate.as_slice()?;
|
|
let time_to_expiry = time_to_expiry.as_slice()?;
|
|
let volatility = volatility.as_slice()?;
|
|
let carry_vec = match carry {
|
|
Some(array) => array.as_slice()?.to_vec(),
|
|
None => vec![0.0; underlying.len()],
|
|
};
|
|
validation::validate_equal_length(&[
|
|
(underlying.len(), "underlying"),
|
|
(strike.len(), "strike"),
|
|
(rate.len(), "rate"),
|
|
(time_to_expiry.len(), "time_to_expiry"),
|
|
(volatility.len(), "volatility"),
|
|
(carry_vec.len(), "carry"),
|
|
])?;
|
|
|
|
let mut delta = Vec::with_capacity(underlying.len());
|
|
let mut gamma = Vec::with_capacity(underlying.len());
|
|
let mut vega = Vec::with_capacity(underlying.len());
|
|
let mut theta = Vec::with_capacity(underlying.len());
|
|
let mut rho = Vec::with_capacity(underlying.len());
|
|
for (((((&u, &k), &r), &t), &vol), &c) in underlying
|
|
.iter()
|
|
.zip(strike.iter())
|
|
.zip(rate.iter())
|
|
.zip(time_to_expiry.iter())
|
|
.zip(volatility.iter())
|
|
.zip(carry_vec.iter())
|
|
{
|
|
let g = ferro_ta_core::options::greeks::model_greeks(
|
|
ferro_ta_core::options::OptionEvaluation {
|
|
contract: ferro_ta_core::options::OptionContract {
|
|
model,
|
|
underlying: u,
|
|
strike: k,
|
|
rate: r,
|
|
carry: c,
|
|
time_to_expiry: t,
|
|
kind,
|
|
},
|
|
volatility: vol,
|
|
},
|
|
);
|
|
delta.push(g.delta);
|
|
gamma.push(g.gamma);
|
|
vega.push(g.vega);
|
|
theta.push(g.theta);
|
|
rho.push(g.rho);
|
|
}
|
|
|
|
Ok((
|
|
delta.into_pyarray(py),
|
|
gamma.into_pyarray(py),
|
|
vega.into_pyarray(py),
|
|
theta.into_pyarray(py),
|
|
rho.into_pyarray(py),
|
|
))
|
|
}
|
|
|
|
#[pyfunction]
|
|
#[pyo3(signature = (underlying, strike, rate, time_to_expiry, volatility, option_type = "call", model = "bsm", carry = 0.0))]
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn extended_greeks(
|
|
underlying: f64,
|
|
strike: f64,
|
|
rate: f64,
|
|
time_to_expiry: f64,
|
|
volatility: f64,
|
|
option_type: &str,
|
|
model: &str,
|
|
carry: f64,
|
|
) -> PyResult<(f64, f64, f64, f64, f64)> {
|
|
let kind = super::parse_option_kind(option_type)?;
|
|
let model = super::parse_pricing_model(model)?;
|
|
let eg = ferro_ta_core::options::greeks::model_extended_greeks(
|
|
ferro_ta_core::options::OptionEvaluation {
|
|
contract: ferro_ta_core::options::OptionContract {
|
|
model,
|
|
underlying,
|
|
strike,
|
|
rate,
|
|
carry,
|
|
time_to_expiry,
|
|
kind,
|
|
},
|
|
volatility,
|
|
},
|
|
);
|
|
Ok((eg.vanna, eg.volga, eg.charm, eg.speed, eg.color))
|
|
}
|
|
|
|
#[pyfunction]
|
|
#[pyo3(signature = (underlying, strike, rate, time_to_expiry, volatility, option_type = "call", model = "bsm", carry = None))]
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn extended_greeks_batch<'py>(
|
|
py: Python<'py>,
|
|
underlying: PyReadonlyArray1<'py, f64>,
|
|
strike: PyReadonlyArray1<'py, f64>,
|
|
rate: PyReadonlyArray1<'py, f64>,
|
|
time_to_expiry: PyReadonlyArray1<'py, f64>,
|
|
volatility: PyReadonlyArray1<'py, f64>,
|
|
option_type: &str,
|
|
model: &str,
|
|
carry: Option<PyReadonlyArray1<'py, f64>>,
|
|
) -> PyResult<ExtendedGreekArrays<'py>> {
|
|
let kind = super::parse_option_kind(option_type)?;
|
|
let model = super::parse_pricing_model(model)?;
|
|
let underlying = underlying.as_slice()?;
|
|
let strike = strike.as_slice()?;
|
|
let rate = rate.as_slice()?;
|
|
let time_to_expiry = time_to_expiry.as_slice()?;
|
|
let volatility = volatility.as_slice()?;
|
|
let carry_vec = match carry {
|
|
Some(array) => array.as_slice()?.to_vec(),
|
|
None => vec![0.0; underlying.len()],
|
|
};
|
|
validation::validate_equal_length(&[
|
|
(underlying.len(), "underlying"),
|
|
(strike.len(), "strike"),
|
|
(rate.len(), "rate"),
|
|
(time_to_expiry.len(), "time_to_expiry"),
|
|
(volatility.len(), "volatility"),
|
|
(carry_vec.len(), "carry"),
|
|
])?;
|
|
|
|
let mut vanna = Vec::with_capacity(underlying.len());
|
|
let mut volga = Vec::with_capacity(underlying.len());
|
|
let mut charm = Vec::with_capacity(underlying.len());
|
|
let mut speed = Vec::with_capacity(underlying.len());
|
|
let mut color = Vec::with_capacity(underlying.len());
|
|
for (((((&u, &k), &r), &t), &vol), &c) in underlying
|
|
.iter()
|
|
.zip(strike.iter())
|
|
.zip(rate.iter())
|
|
.zip(time_to_expiry.iter())
|
|
.zip(volatility.iter())
|
|
.zip(carry_vec.iter())
|
|
{
|
|
let eg = ferro_ta_core::options::greeks::model_extended_greeks(
|
|
ferro_ta_core::options::OptionEvaluation {
|
|
contract: ferro_ta_core::options::OptionContract {
|
|
model,
|
|
underlying: u,
|
|
strike: k,
|
|
rate: r,
|
|
carry: c,
|
|
time_to_expiry: t,
|
|
kind,
|
|
},
|
|
volatility: vol,
|
|
},
|
|
);
|
|
vanna.push(eg.vanna);
|
|
volga.push(eg.volga);
|
|
charm.push(eg.charm);
|
|
speed.push(eg.speed);
|
|
color.push(eg.color);
|
|
}
|
|
|
|
Ok((
|
|
vanna.into_pyarray(py),
|
|
volga.into_pyarray(py),
|
|
charm.into_pyarray(py),
|
|
speed.into_pyarray(py),
|
|
color.into_pyarray(py),
|
|
))
|
|
}
|