use crate::{
core::{
collateral::Discountable,
instrument::{AssetClass, Instrument},
trade::{Side, Trade},
},
currencies::currency::Currency,
indices::{fxpair::FxPair, marketindex::MarketIndex},
instruments::cashflows::payoffops::PayoffOps,
time::{date::Date, daycounter::DayCounter},
utils::errors::Result,
volatility::volatilityindexing::Strike,
xva::{
claimevaluationstrategy::ClaimEvaluationStrategy, contigentclaim::ContingentClaim,
makecontigentclaim::MakeContingentClaim,
},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FxOptionType {
Call,
Put,
}
#[derive(Clone)]
pub struct FxOption {
identifier: String,
expiry_date: Date,
strike: Strike,
option_type: FxOptionType,
base_currency: Currency,
quote_currency: Currency,
day_counter: DayCounter,
pair: FxPair,
}
impl FxOption {
#[must_use]
#[allow(clippy::too_many_arguments)]
pub const fn new(
identifier: String,
expiry_date: Date,
strike: Strike,
option_type: FxOptionType,
base_currency: Currency,
quote_currency: Currency,
day_counter: DayCounter,
pair: FxPair,
) -> Self {
Self {
identifier,
expiry_date,
strike,
option_type,
base_currency,
quote_currency,
day_counter,
pair,
}
}
#[must_use]
pub const fn expiry_date(&self) -> Date {
self.expiry_date
}
#[must_use]
pub const fn strike(&self) -> Strike {
self.strike
}
#[must_use]
pub const fn option_type(&self) -> FxOptionType {
self.option_type
}
#[must_use]
pub const fn base_currency(&self) -> Currency {
self.base_currency
}
#[must_use]
pub const fn quote_currency(&self) -> Currency {
self.quote_currency
}
#[must_use]
pub const fn day_counter(&self) -> &DayCounter {
&self.day_counter
}
#[must_use]
pub const fn pair(&self) -> &FxPair {
&self.pair
}
#[must_use]
pub const fn underlying_index(&self) -> MarketIndex {
MarketIndex::FxPair(self.pair)
}
}
impl Instrument for FxOption {
fn identifier(&self) -> String {
self.identifier.clone()
}
}
impl Discountable for FxOption {
fn currency(&self) -> Currency {
self.quote_currency
}
fn asset_class(&self) -> AssetClass {
AssetClass::Fx
}
}
pub struct FxOptionTrade {
instrument: FxOption,
trade_date: Date,
notional: f64,
side: Side,
}
impl FxOptionTrade {
#[must_use]
pub const fn new(instrument: FxOption, trade_date: Date, notional: f64, side: Side) -> Self {
Self {
instrument,
trade_date,
notional,
side,
}
}
#[must_use]
pub const fn notional(&self) -> f64 {
self.notional
}
pub fn into_contingent_claims(&self) -> Result<Vec<ContingentClaim>> {
let opt = self.instrument();
let trade_id = opt.identifier();
let expiry = opt.expiry_date();
let strike = opt.strike().resolve(0.0);
let payoff = match opt.option_type() {
FxOptionType::Call => PayoffOps::Max(
Box::new(PayoffOps::Minus(
Box::new(PayoffOps::Index),
Box::new(PayoffOps::Const(strike)),
)),
Box::new(PayoffOps::Const(0.0)),
),
FxOptionType::Put => PayoffOps::Max(
Box::new(PayoffOps::Minus(
Box::new(PayoffOps::Const(strike)),
Box::new(PayoffOps::Index),
)),
Box::new(PayoffOps::Const(0.0)),
),
};
let claim = MakeContingentClaim::default()
.with_trade_id(trade_id)
.with_leg_id(0)
.with_payment_date(expiry)
.with_currency(opt.quote_currency())
.with_notional(self.notional)
.with_side(self.side)
.with_index(opt.underlying_index())
.with_evaluation_strategy(ClaimEvaluationStrategy::SpotPayoff {
payoff_ops: payoff,
strike,
observation_date: expiry,
})
.build()?;
Ok(vec![claim])
}
}
impl Trade<FxOption> for FxOptionTrade {
fn instrument(&self) -> &FxOption {
&self.instrument
}
fn trade_date(&self) -> Date {
self.trade_date
}
fn side(&self) -> Side {
self.side
}
}