use crate::options_builder::OptionBuilder;
use bitcoin::Amount;
use ddk_manager::{
contract::{
contract_input::{ContractInput, ContractInputInfo, OracleInput},
numerical_descriptor::NumericalDescriptor,
ContractDescriptor,
},
payout_curve::{PayoutFunction, RoundingInterval, RoundingIntervals},
};
use ddk_messages::oracle_msgs::OracleAnnouncement;
use ddk_trie::OracleNumericInfo;
#[derive(Copy, Clone)]
pub enum OptionType {
Call,
Put,
}
#[derive(Copy, Clone)]
pub enum Direction {
Long,
Short,
}
fn build_order_offer(
announcement: &OracleAnnouncement,
total_collateral: Amount,
offer_collateral: Amount,
payout_function: PayoutFunction,
rounding_intervals: RoundingIntervals,
fee_rate: u64,
) -> ContractInput {
let contract_descriptor = ContractDescriptor::Numerical(NumericalDescriptor {
payout_function,
rounding_intervals,
difference_params: None,
oracle_numeric_infos: OracleNumericInfo {
nb_digits: vec![20],
base: 2,
},
});
let oracles = OracleInput {
public_keys: vec![announcement.oracle_public_key],
event_id: announcement.oracle_event.event_id.clone(),
threshold: 1,
};
let contract_info = ContractInputInfo {
oracles,
contract_descriptor,
};
ContractInput {
contract_infos: vec![contract_info],
offer_collateral,
accept_collateral: total_collateral - offer_collateral,
fee_rate,
contract_flags: 0,
}
}
#[allow(clippy::too_many_arguments)]
pub fn build_option_order_offer(
announcement: &OracleAnnouncement,
contract_size: Amount,
strike_price: u64,
premium: Amount,
fee_per_byte: u64,
rounding: u64,
option_type: OptionType,
direction: Direction,
total_collateral: Amount,
nb_oracle_digits: u32,
) -> anyhow::Result<ContractInput> {
let payout_function = OptionBuilder::build_option_payout(
direction,
option_type,
strike_price,
contract_size,
total_collateral,
2,
nb_oracle_digits,
)?;
let rounding_mod = compute_rounding_modulus(rounding, contract_size);
let rounding_intervals =
create_rounding_intervals(strike_price, rounding_mod, option_type, direction);
let offer_collateral = match direction {
Direction::Short => total_collateral - premium,
Direction::Long => premium,
};
Ok(build_order_offer(
announcement,
total_collateral,
offer_collateral,
payout_function,
rounding_intervals,
fee_per_byte,
))
}
fn create_rounding_intervals(
strike_price: u64,
rounding_mod: u64,
option_type: OptionType,
direction: Direction,
) -> RoundingIntervals {
let intervals = match (option_type, direction) {
(OptionType::Call, Direction::Short) | (OptionType::Call, Direction::Long) => vec![
RoundingInterval {
begin_interval: 0,
rounding_mod: 1,
},
RoundingInterval {
begin_interval: strike_price,
rounding_mod,
},
],
(OptionType::Put, Direction::Short) | (OptionType::Put, Direction::Long) => vec![
RoundingInterval {
begin_interval: 0,
rounding_mod,
},
RoundingInterval {
begin_interval: strike_price,
rounding_mod: 1,
},
],
};
RoundingIntervals { intervals }
}
fn compute_rounding_modulus(rounding: u64, total_collateral: Amount) -> u64 {
(rounding * total_collateral.to_sat()) / 100_000_000
}
pub fn create_covered_call_rounding_intervals(
strike_price: u64,
rounding_mod: u64,
) -> RoundingIntervals {
RoundingIntervals {
intervals: vec![
RoundingInterval {
begin_interval: 0,
rounding_mod: 1,
},
RoundingInterval {
begin_interval: strike_price,
rounding_mod,
},
],
}
}