use crate::options::Direction;
use crate::options::OptionType;
use bitcoin::Amount;
use ddk_manager::payout_curve::HyperbolaPayoutCurvePiece;
use ddk_manager::payout_curve::PayoutFunction;
use ddk_manager::payout_curve::PayoutFunctionPiece;
use ddk_manager::payout_curve::PayoutPoint;
use ddk_manager::payout_curve::PolynomialPayoutCurvePiece;
pub struct OptionBuilder;
impl OptionBuilder {
pub fn build_covered_call_payout(
strike_price: u64,
contract_size: Amount,
oracle_base: u32,
oracle_digits: u32,
) -> anyhow::Result<PayoutFunction> {
let max_outcome = (oracle_base as u64).pow(oracle_digits) - 1;
let total_collateral = contract_size;
let below_strike = PayoutFunctionPiece::PolynomialPayoutCurvePiece(
PolynomialPayoutCurvePiece::new(vec![
PayoutPoint {
event_outcome: 0,
outcome_payout: total_collateral,
extra_precision: 0,
},
PayoutPoint {
event_outcome: strike_price,
outcome_payout: total_collateral,
extra_precision: 0,
},
])?,
);
let above_strike =
PayoutFunctionPiece::HyperbolaPayoutCurvePiece(HyperbolaPayoutCurvePiece::new(
PayoutPoint {
event_outcome: strike_price,
outcome_payout: total_collateral,
extra_precision: 0,
},
PayoutPoint {
event_outcome: max_outcome,
outcome_payout: Amount::ZERO,
extra_precision: 0,
},
true,
strike_price as f64,
total_collateral.to_sat() as f64,
1.0,
0.0,
-1.0,
strike_price as f64,
)?);
let payout_function = PayoutFunction::new(vec![below_strike, above_strike])?;
payout_function.validate(max_outcome)?;
Ok(payout_function)
}
pub fn build_short_put_payout(
strike_price: u64,
total_collateral: Amount,
oracle_base: u32,
oracle_digits: u32,
) -> anyhow::Result<PayoutFunction> {
let max_outcome = (oracle_base as u64).pow(oracle_digits) - 1;
let below_strike =
PayoutFunctionPiece::HyperbolaPayoutCurvePiece(HyperbolaPayoutCurvePiece::new(
PayoutPoint {
event_outcome: 0,
outcome_payout: Amount::ZERO,
extra_precision: 0,
},
PayoutPoint {
event_outcome: strike_price,
outcome_payout: total_collateral,
extra_precision: 0,
},
true,
0.0,
0.0,
1.0,
0.0,
1.0,
strike_price as f64,
)?);
let above_strike = PayoutFunctionPiece::PolynomialPayoutCurvePiece(
PolynomialPayoutCurvePiece::new(vec![
PayoutPoint {
event_outcome: strike_price,
outcome_payout: total_collateral,
extra_precision: 0,
},
PayoutPoint {
event_outcome: max_outcome,
outcome_payout: total_collateral,
extra_precision: 0,
},
])?,
);
let payout_function = PayoutFunction::new(vec![below_strike, above_strike])?;
payout_function.validate(max_outcome)?;
Ok(payout_function)
}
pub fn build_long_call_payout(
strike_price: u64,
total_collateral: Amount,
oracle_base: u32,
oracle_digits: u32,
) -> anyhow::Result<PayoutFunction> {
let max_outcome = (oracle_base as u64).pow(oracle_digits) - 1;
let below_strike = PayoutFunctionPiece::PolynomialPayoutCurvePiece(
PolynomialPayoutCurvePiece::new(vec![
PayoutPoint {
event_outcome: 0,
outcome_payout: Amount::ZERO,
extra_precision: 0,
},
PayoutPoint {
event_outcome: strike_price,
outcome_payout: Amount::ZERO,
extra_precision: 0,
},
])?,
);
let above_strike =
PayoutFunctionPiece::HyperbolaPayoutCurvePiece(HyperbolaPayoutCurvePiece::new(
PayoutPoint {
event_outcome: strike_price,
outcome_payout: Amount::ZERO,
extra_precision: 0,
},
PayoutPoint {
event_outcome: max_outcome,
outcome_payout: total_collateral,
extra_precision: 0,
},
true,
strike_price as f64,
0.0,
1.0,
0.0,
1.0,
strike_price as f64,
)?);
let payout_function = PayoutFunction::new(vec![below_strike, above_strike])?;
payout_function.validate(max_outcome)?;
Ok(payout_function)
}
pub fn build_long_put_payout(
strike_price: u64,
total_collateral: Amount,
oracle_base: u32,
oracle_digits: u32,
) -> anyhow::Result<PayoutFunction> {
let max_outcome = (oracle_base as u64).pow(oracle_digits) - 1;
let below_strike =
PayoutFunctionPiece::HyperbolaPayoutCurvePiece(HyperbolaPayoutCurvePiece::new(
PayoutPoint {
event_outcome: 0,
outcome_payout: total_collateral,
extra_precision: 0,
},
PayoutPoint {
event_outcome: strike_price,
outcome_payout: Amount::ZERO,
extra_precision: 0,
},
true,
0.0,
total_collateral.to_sat() as f64,
1.0,
0.0,
-1.0,
strike_price as f64,
)?);
let above_strike = PayoutFunctionPiece::PolynomialPayoutCurvePiece(
PolynomialPayoutCurvePiece::new(vec![
PayoutPoint {
event_outcome: strike_price,
outcome_payout: Amount::ZERO,
extra_precision: 0,
},
PayoutPoint {
event_outcome: max_outcome,
outcome_payout: Amount::ZERO,
extra_precision: 0,
},
])?,
);
let payout_function = PayoutFunction::new(vec![below_strike, above_strike])?;
payout_function.validate(max_outcome)?;
Ok(payout_function)
}
pub fn build_option_payout(
direction: Direction,
option_type: OptionType,
strike_price: u64,
contract_size: Amount,
total_collateral: Amount,
oracle_base: u32,
oracle_digits: u32,
) -> anyhow::Result<PayoutFunction> {
match (direction, option_type) {
(Direction::Short, OptionType::Call) => Self::build_covered_call_payout(
strike_price,
contract_size,
oracle_base,
oracle_digits,
),
(Direction::Short, OptionType::Put) => Self::build_short_put_payout(
strike_price,
total_collateral,
oracle_base,
oracle_digits,
),
(Direction::Long, OptionType::Call) => Self::build_long_call_payout(
strike_price,
total_collateral,
oracle_base,
oracle_digits,
),
(Direction::Long, OptionType::Put) => Self::build_long_put_payout(
strike_price,
total_collateral,
oracle_base,
oracle_digits,
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all_option_types() {
let strike_price = 50000; let contract_size = Amount::ONE_BTC; let oracle_base = 10;
let oracle_digits = 5;
OptionBuilder::build_option_payout(
Direction::Short,
OptionType::Call,
strike_price,
contract_size,
Amount::from_sat(1_000_000),
oracle_base,
oracle_digits,
)
.unwrap();
OptionBuilder::build_option_payout(
Direction::Short,
OptionType::Put,
strike_price,
contract_size,
Amount::from_sat(1_000_000),
oracle_base,
oracle_digits,
)
.unwrap();
OptionBuilder::build_option_payout(
Direction::Long,
OptionType::Call,
strike_price,
contract_size,
Amount::from_sat(1_000_000),
oracle_base,
oracle_digits,
)
.unwrap();
OptionBuilder::build_option_payout(
Direction::Long,
OptionType::Put,
strike_price,
contract_size,
Amount::from_sat(1_000_000),
oracle_base,
oracle_digits,
)
.unwrap();
}
}