use std::any::Any;
use crate::errors::QlResult;
use crate::event::event_has_occurred;
use crate::instrument::{Instrument, InstrumentBase, InstrumentResults};
use crate::option::OptionType;
use crate::position::Position;
use crate::pricingengine::{Arguments, GenericEngine, Results};
use crate::settings::Settings;
use crate::shared::Shared;
use crate::time::date::Date;
use crate::{fail, require};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct VarianceSwapOptionWeight {
pub option_type: OptionType,
pub strike: f64,
pub weight: f64,
}
#[derive(Default)]
pub struct VarianceSwapArguments {
pub position: Option<Position>,
pub strike: Option<f64>,
pub notional: Option<f64>,
pub start_date: Option<Date>,
pub maturity_date: Option<Date>,
pub settings: Option<Shared<Settings<Date>>>,
}
impl Arguments for VarianceSwapArguments {
fn validate(&self) -> QlResult<()> {
require!(self.position.is_some(), "variance swap position missing");
require!(
self.strike.is_some_and(|v| v.is_finite() && v > 0.0),
"variance strike must be finite and positive"
);
require!(
self.notional.is_some_and(|v| v.is_finite() && v > 0.0),
"variance notional must be finite and positive"
);
let Some((start, maturity)) = self.start_date.zip(self.maturity_date) else {
fail!("variance swap dates missing");
};
require!(
start != Date::default() && maturity != Date::default() && start < maturity,
"variance swap requires valid start before maturity"
);
require!(self.settings.is_some(), "variance swap settings missing");
Ok(())
}
}
#[derive(Default)]
pub struct VarianceSwapResults {
pub instrument: InstrumentResults,
pub variance: Option<f64>,
pub variance_error: Option<f64>,
pub samples: Option<usize>,
pub option_weights: Vec<VarianceSwapOptionWeight>,
}
impl Results for VarianceSwapResults {
fn reset(&mut self) {
self.instrument.reset();
self.variance = None;
self.variance_error = None;
self.samples = None;
self.option_weights.clear();
}
fn as_instrument_results(&self) -> Option<&InstrumentResults> {
Some(&self.instrument)
}
}
pub type VarianceSwapEngine = GenericEngine<VarianceSwapArguments, VarianceSwapResults>;
pub struct VarianceSwap {
base: InstrumentBase,
position: Position,
strike: f64,
notional: f64,
start_date: Date,
maturity_date: Date,
settings: Shared<Settings<Date>>,
variance: Option<f64>,
variance_error: Option<f64>,
samples: Option<usize>,
option_weights: Vec<VarianceSwapOptionWeight>,
}
impl VarianceSwap {
pub fn new(
position: Position,
strike: f64,
notional: f64,
start_date: Date,
maturity_date: Date,
settings: Shared<Settings<Date>>,
) -> QlResult<Self> {
let arguments = VarianceSwapArguments {
position: Some(position),
strike: Some(strike),
notional: Some(notional),
start_date: Some(start_date),
maturity_date: Some(maturity_date),
settings: Some(settings.clone()),
};
arguments.validate()?;
let base = InstrumentBase::new();
settings.register_eval_date_observer(&base.observer());
Ok(Self {
base,
position,
strike,
notional,
start_date,
maturity_date,
settings,
variance: None,
variance_error: None,
samples: None,
option_weights: Vec::new(),
})
}
pub fn position(&self) -> Position {
self.position
}
pub fn strike(&self) -> f64 {
self.strike
}
pub fn notional(&self) -> f64 {
self.notional
}
pub fn start_date(&self) -> Date {
self.start_date
}
pub fn maturity_date(&self) -> Date {
self.maturity_date
}
pub fn variance(&mut self) -> QlResult<f64> {
self.calculate()?;
let Some(variance) = self.variance else {
fail!("variance not provided");
};
Ok(variance)
}
pub fn variance_error(&mut self) -> QlResult<f64> {
self.calculate()?;
let Some(error) = self.variance_error else {
fail!("variance standard error not provided");
};
Ok(error)
}
pub fn samples(&mut self) -> QlResult<usize> {
self.calculate()?;
let Some(samples) = self.samples else {
fail!("variance sample count not provided");
};
Ok(samples)
}
pub fn option_weights(&mut self) -> QlResult<Vec<VarianceSwapOptionWeight>> {
self.calculate()?;
require!(
self.variance.is_some(),
"variance option weights not provided"
);
Ok(self.option_weights.clone())
}
}
impl Instrument for VarianceSwap {
fn base(&self) -> &InstrumentBase {
&self.base
}
fn base_mut(&mut self) -> &mut InstrumentBase {
&mut self.base
}
fn is_expired(&self) -> QlResult<bool> {
event_has_occurred(self.maturity_date, &self.settings, None, None)
}
fn setup_arguments(&self, arguments: &mut dyn Arguments) -> QlResult<()> {
let Some(arguments) = (arguments as &mut dyn Any).downcast_mut::<VarianceSwapArguments>()
else {
fail!("wrong variance swap arguments");
};
*arguments = VarianceSwapArguments {
position: Some(self.position),
strike: Some(self.strike),
notional: Some(self.notional),
start_date: Some(self.start_date),
maturity_date: Some(self.maturity_date),
settings: Some(self.settings.clone()),
};
Ok(())
}
fn fetch_results(&mut self, results: &dyn Results) -> QlResult<()> {
let Some(results) = (results as &dyn Any).downcast_ref::<VarianceSwapResults>() else {
fail!("wrong variance swap results");
};
require!(
results.variance.is_some_and(f64::is_finite)
&& results.instrument.value.is_some_and(f64::is_finite),
"invalid variance swap results"
);
require!(
results
.variance_error
.is_none_or(|v| v.is_finite() && v >= 0.0)
&& results.samples.is_none_or(|v| v >= 2)
&& results.variance_error.is_some() == results.samples.is_some()
&& results.instrument.error_estimate.is_none_or(f64::is_finite),
"invalid variance sampling results"
);
self.base.store_results(&results.instrument);
self.variance = results.variance;
self.variance_error = results.variance_error;
self.samples = results.samples;
self.option_weights.clone_from(&results.option_weights);
Ok(())
}
fn setup_expired(&mut self) {
let results = InstrumentResults {
value: Some(0.0),
error_estimate: Some(0.0),
..InstrumentResults::default()
};
self.base.store_results(&results);
self.variance = None;
self.variance_error = None;
self.samples = None;
self.option_weights.clear();
}
}
#[cfg(test)]
#[path = "varianceswap_tests.rs"]
mod tests;