use crate::error::SolMathError;
use crate::SCALE;
#[cfg(feature = "bs")]
use crate::bs::{black_scholes_price, bs_delta, bs_full, bs_gamma, bs_rho, bs_theta, bs_vega};
#[cfg(feature = "bs")]
use crate::constants::BsFull;
#[cfg(any(feature = "bs", feature = "barrier", feature = "asian"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Price(u128);
#[cfg(any(feature = "bs", feature = "barrier", feature = "asian"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Rate(u128);
#[cfg(any(feature = "bs", feature = "barrier", feature = "asian"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Vol(u128);
#[cfg(any(feature = "bs", feature = "barrier", feature = "asian"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Time(u128);
#[cfg(any(feature = "bs", feature = "barrier", feature = "asian"))]
impl Price {
pub const MAX: u128 = 100_000 * SCALE;
#[inline]
pub const fn new(raw: u128) -> Result<Self, SolMathError> {
if raw > Self::MAX {
return Err(SolMathError::DomainError);
}
Ok(Self(raw))
}
#[inline]
pub const fn get(self) -> u128 {
self.0
}
}
#[cfg(any(feature = "bs", feature = "barrier", feature = "asian"))]
impl Rate {
pub const MAX: u128 = 10 * SCALE;
#[inline]
pub const fn new(raw: u128) -> Result<Self, SolMathError> {
if raw > Self::MAX {
return Err(SolMathError::DomainError);
}
Ok(Self(raw))
}
#[inline]
pub const fn get(self) -> u128 {
self.0
}
}
#[cfg(any(feature = "bs", feature = "barrier", feature = "asian"))]
impl Vol {
pub const MAX: u128 = 100 * SCALE;
#[inline]
pub const fn new(raw: u128) -> Result<Self, SolMathError> {
if raw == 0 || raw > Self::MAX {
return Err(SolMathError::DomainError);
}
Ok(Self(raw))
}
#[inline]
pub const fn get(self) -> u128 {
self.0
}
}
#[cfg(any(feature = "bs", feature = "barrier", feature = "asian"))]
impl Time {
pub const MAX: u128 = 100 * SCALE;
#[inline]
pub const fn new(raw: u128) -> Result<Self, SolMathError> {
if raw == 0 || raw > Self::MAX {
return Err(SolMathError::DomainError);
}
Ok(Self(raw))
}
#[inline]
pub const fn get(self) -> u128 {
self.0
}
}
#[cfg(feature = "bs")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EuropeanInputs {
s: Price,
k: Price,
r: Rate,
sigma: Vol,
t: Time,
}
#[cfg(feature = "bs")]
impl EuropeanInputs {
#[inline]
pub const fn new(s: Price, k: Price, r: Rate, sigma: Vol, t: Time) -> Self {
Self { s, k, r, sigma, t }
}
#[inline]
pub const fn from_raw(
s: u128,
k: u128,
r: u128,
sigma: u128,
t: u128,
) -> Result<Self, SolMathError> {
let s = match Price::new(s) {
Ok(v) => v,
Err(e) => return Err(e),
};
let k = match Price::new(k) {
Ok(v) => v,
Err(e) => return Err(e),
};
let r = match Rate::new(r) {
Ok(v) => v,
Err(e) => return Err(e),
};
let sigma = match Vol::new(sigma) {
Ok(v) => v,
Err(e) => return Err(e),
};
let t = match Time::new(t) {
Ok(v) => v,
Err(e) => return Err(e),
};
Ok(Self { s, k, r, sigma, t })
}
#[inline]
pub fn price(&self) -> Result<(u128, u128), SolMathError> {
black_scholes_price(self.s.0, self.k.0, self.r.0, self.sigma.0, self.t.0)
}
#[inline]
pub fn full(&self) -> Result<BsFull, SolMathError> {
bs_full(self.s.0, self.k.0, self.r.0, self.sigma.0, self.t.0)
}
#[cfg(feature = "transcendental")]
#[inline]
pub fn full_hp(&self) -> Result<BsFull, SolMathError> {
crate::hp::bs_full_hp(self.s.0, self.k.0, self.r.0, self.sigma.0, self.t.0)
}
#[inline]
pub fn delta(&self) -> Result<(i128, i128), SolMathError> {
bs_delta(self.s.0, self.k.0, self.r.0, self.sigma.0, self.t.0)
}
#[inline]
pub fn gamma(&self) -> Result<i128, SolMathError> {
bs_gamma(self.s.0, self.k.0, self.r.0, self.sigma.0, self.t.0)
}
#[inline]
pub fn vega(&self) -> Result<i128, SolMathError> {
bs_vega(self.s.0, self.k.0, self.r.0, self.sigma.0, self.t.0)
}
#[inline]
pub fn theta(&self) -> Result<(i128, i128), SolMathError> {
bs_theta(self.s.0, self.k.0, self.r.0, self.sigma.0, self.t.0)
}
#[inline]
pub fn rho(&self) -> Result<(i128, i128), SolMathError> {
bs_rho(self.s.0, self.k.0, self.r.0, self.sigma.0, self.t.0)
}
#[inline]
pub const fn spot(&self) -> Price {
self.s
}
#[inline]
pub const fn strike(&self) -> Price {
self.k
}
}
#[cfg(feature = "iv")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ImpliedVolInputs {
market_price: Price,
s: Price,
k: Price,
r: Rate,
t: Time,
}
#[cfg(feature = "iv")]
impl ImpliedVolInputs {
#[inline]
pub const fn new(market_price: Price, s: Price, k: Price, r: Rate, t: Time) -> Self {
Self {
market_price,
s,
k,
r,
t,
}
}
#[inline]
pub const fn from_raw(
market_price: u128,
s: u128,
k: u128,
r: u128,
t: u128,
) -> Result<Self, SolMathError> {
let market_price = match Price::new(market_price) {
Ok(v) => v,
Err(e) => return Err(e),
};
let s = match Price::new(s) {
Ok(v) => v,
Err(e) => return Err(e),
};
let k = match Price::new(k) {
Ok(v) => v,
Err(e) => return Err(e),
};
let r = match Rate::new(r) {
Ok(v) => v,
Err(e) => return Err(e),
};
let t = match Time::new(t) {
Ok(v) => v,
Err(e) => return Err(e),
};
Ok(Self {
market_price,
s,
k,
r,
t,
})
}
#[inline]
pub fn solve(&self) -> Result<u128, SolMathError> {
crate::iv::implied_vol(self.market_price.0, self.s.0, self.k.0, self.r.0, self.t.0)
}
}
#[cfg(feature = "barrier")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BarrierInputs {
s: Price,
k: Price,
h: Price,
r: Rate,
sigma: Vol,
t: Time,
}
#[cfg(feature = "barrier")]
impl BarrierInputs {
#[inline]
pub const fn new(s: Price, k: Price, h: Price, r: Rate, sigma: Vol, t: Time) -> Self {
Self {
s,
k,
h,
r,
sigma,
t,
}
}
#[inline]
pub const fn from_raw(
s: u128,
k: u128,
h: u128,
r: u128,
sigma: u128,
t: u128,
) -> Result<Self, SolMathError> {
let s = match Price::new(s) {
Ok(v) => v,
Err(e) => return Err(e),
};
let k = match Price::new(k) {
Ok(v) => v,
Err(e) => return Err(e),
};
let h = match Price::new(h) {
Ok(v) => v,
Err(e) => return Err(e),
};
let r = match Rate::new(r) {
Ok(v) => v,
Err(e) => return Err(e),
};
let sigma = match Vol::new(sigma) {
Ok(v) => v,
Err(e) => return Err(e),
};
let t = match Time::new(t) {
Ok(v) => v,
Err(e) => return Err(e),
};
Ok(Self {
s,
k,
h,
r,
sigma,
t,
})
}
#[inline]
pub fn price(
&self,
is_call: bool,
barrier_type: crate::barrier::BarrierType,
) -> Result<crate::barrier::BarrierResult, SolMathError> {
crate::barrier::barrier_option(
self.s.0,
self.k.0,
self.h.0,
self.r.0,
self.sigma.0,
self.t.0,
is_call,
barrier_type,
)
}
#[inline]
pub fn price_with_state(
&self,
is_call: bool,
barrier_type: crate::barrier::BarrierType,
barrier_was_breached: bool,
) -> Result<crate::barrier::BarrierResult, SolMathError> {
crate::barrier::barrier_option_with_state(
self.s.0,
self.k.0,
self.h.0,
self.r.0,
self.sigma.0,
self.t.0,
is_call,
barrier_type,
barrier_was_breached,
)
}
}
#[cfg(feature = "asian")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TwapInputs {
s: Price,
k: Price,
r: Rate,
q: Rate,
sigma: Vol,
t: Time,
averaging_time: u128,
fixed_average: Price,
fixed_weight: u128,
}
#[cfg(feature = "asian")]
impl TwapInputs {
#[allow(clippy::too_many_arguments)]
pub const fn from_raw(
s: u128,
k: u128,
r: u128,
q: u128,
sigma: u128,
t: u128,
averaging_time: u128,
fixed_average: u128,
fixed_weight: u128,
) -> Result<Self, SolMathError> {
let s = match Price::new(s) {
Ok(v) if v.get() > 0 => v,
Ok(_) => return Err(SolMathError::DomainError),
Err(e) => return Err(e),
};
let k = match Price::new(k) {
Ok(v) if v.get() > 0 => v,
Ok(_) => return Err(SolMathError::DomainError),
Err(e) => return Err(e),
};
let r = match Rate::new(r) {
Ok(v) => v,
Err(e) => return Err(e),
};
let q = match Rate::new(q) {
Ok(v) => v,
Err(e) => return Err(e),
};
let sigma = match Vol::new(sigma) {
Ok(v) => v,
Err(e) => return Err(e),
};
let t = match Time::new(t) {
Ok(v) => v,
Err(e) => return Err(e),
};
let fixed_average = match Price::new(fixed_average) {
Ok(v) => v,
Err(e) => return Err(e),
};
if averaging_time > t.get() || fixed_weight > SCALE {
return Err(SolMathError::DomainError);
}
if fixed_weight < SCALE && averaging_time == 0 {
return Err(SolMathError::DomainError);
}
if fixed_weight == SCALE && averaging_time != 0 {
return Err(SolMathError::DomainError);
}
if fixed_weight == 0 {
if fixed_average.get() != 0 {
return Err(SolMathError::DomainError);
}
} else if fixed_average.get() == 0 {
return Err(SolMathError::DomainError);
}
Ok(Self {
s,
k,
r,
q,
sigma,
t,
averaging_time,
fixed_average,
fixed_weight,
})
}
#[inline]
pub fn price(&self) -> Result<crate::asian::AsianOptionResult, SolMathError> {
crate::asian::twap_option_price(
self.s.0,
self.k.0,
self.r.0,
self.q.0,
self.sigma.0,
self.t.0,
self.averaging_time,
self.fixed_average.0,
self.fixed_weight,
)
}
#[inline]
pub const fn averaging_time(&self) -> u128 {
self.averaging_time
}
#[inline]
pub const fn fixed_weight(&self) -> u128 {
self.fixed_weight
}
#[inline]
pub const fn fixed_average(&self) -> Price {
self.fixed_average
}
}
#[cfg(feature = "pool")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PoolSwapInputs {
balance_in: u128,
balance_out: u128,
weight_in: u128,
weight_out: u128,
amount_in: u128,
fee_rate: u128,
}
#[cfg(feature = "pool")]
impl PoolSwapInputs {
pub fn from_raw(
balance_in: u128,
balance_out: u128,
weight_in: u128,
weight_out: u128,
amount_in: u128,
fee_rate: u128,
) -> Result<Self, SolMathError> {
if weight_out == 0 || weight_in == 0 || balance_in == 0 || balance_out == 0 {
return Err(SolMathError::DomainError);
}
if fee_rate > SCALE {
return Err(SolMathError::DomainError);
}
let weight_q = weight_in / weight_out;
if weight_q > 20 || (weight_q == 20 && weight_in % weight_out != 0) {
return Err(SolMathError::DomainError);
}
if amount_in != 0 {
let denominator = balance_in
.checked_add(amount_in)
.ok_or(SolMathError::Overflow)?;
let min_balance = denominator / 100 + u128::from(denominator % 100 != 0);
if balance_in < min_balance {
return Err(SolMathError::DomainError);
}
}
Ok(Self {
balance_in,
balance_out,
weight_in,
weight_out,
amount_in,
fee_rate,
})
}
#[inline]
pub fn quote(&self) -> Result<(u128, u128), SolMathError> {
crate::pool::weighted_pool_swap(
self.balance_in,
self.balance_out,
self.weight_in,
self.weight_out,
self.amount_in,
self.fee_rate,
)
}
}