use crate::{
book::protocol::command::Side,
types::{Money, OddsX10000},
};
#[inline]
fn clamp_i128_to_i64(v: i128) -> i64 {
v.clamp(i64::MIN as i128, i64::MAX as i128) as i64
}
#[inline]
fn floor_to_step(v: i128, step: i128) -> i128 {
if v <= 0 || step <= 0 {
0
} else {
(v / step) * step
}
}
#[inline]
fn ceil_div(v: i128, denom: i128) -> i128 {
if v <= 0 || denom <= 0 {
0
} else {
(v + denom - 1) / denom
}
}
#[inline]
fn cent_step() -> i128 {
Money::MONEY_SCALE_PER_CENT as i128
}
#[inline]
pub fn floor_to_cent(stake: Money) -> Money {
Money(clamp_i128_to_i64(floor_to_step(
stake.0 as i128,
cent_step(),
)))
}
#[inline]
pub fn ceil_to_cent(stake: Money) -> Money {
if stake.0 <= 0 {
Money::zero()
} else {
Money(clamp_i128_to_i64(
ceil_div(stake.0 as i128, cent_step()) * cent_step(),
))
}
}
#[inline]
fn derived_odds_raw(odds: OddsX10000) -> OddsX10000 {
let o = odds.0 as u64;
if o <= 10_000 {
return OddsX10000(OddsX10000::MAX);
}
let derived = (o * 10_000) / (o - 10_000);
OddsX10000(derived.min(u32::MAX as u64) as u32)
}
#[inline]
pub fn backer_profit(stake: Money, odds: OddsX10000) -> Money {
let stake_i = stake.0 as i128;
let odds_i = odds.0 as i128;
let profit = (stake_i * (odds_i - 10_000)) / 10_000;
Money(clamp_i128_to_i64(profit))
}
#[inline]
pub fn exchange_order_reserve(side: Side, odds: OddsX10000, stake: Money) -> Money {
let stake = stake.clamp_non_negative();
match side {
Side::Yes => stake,
Side::No => backer_profit(stake, odds).clamp_non_negative(),
}
}
#[inline]
pub fn binary_order_reserve(
max_price_ticks: u16,
side: Side,
price_ticks: u16,
qty_shares: u64,
) -> u128 {
let per_share = match side {
Side::Yes => u128::from(price_ticks),
Side::No => u128::from(max_price_ticks.saturating_sub(price_ticks)),
};
per_share.saturating_mul(u128::from(qty_shares))
}
#[inline]
pub fn back_to_lay_odds(back_odds: OddsX10000) -> OddsX10000 {
let raw = derived_odds_raw(back_odds);
raw.ceil_tick().unwrap_or(OddsX10000(OddsX10000::MAX))
}
#[inline]
pub fn lay_to_back_odds(lay_odds: OddsX10000) -> OddsX10000 {
let raw = derived_odds_raw(lay_odds);
raw.floor_tick().unwrap_or(OddsX10000(OddsX10000::MAX))
}
#[inline]
pub fn max_opposite_back_odds_for_taker_back(target_back_odds: OddsX10000) -> OddsX10000 {
if target_back_odds.0 == OddsX10000::MIN {
return OddsX10000(OddsX10000::MAX);
}
let inclusive_floor = target_back_odds.tick_down().unwrap_or(target_back_odds);
lay_to_back_odds(inclusive_floor)
}
#[inline]
pub fn min_opposite_lay_odds_for_taker_lay(target_lay_odds: OddsX10000) -> OddsX10000 {
let inclusive_ceil = target_lay_odds.tick_up().unwrap_or(target_lay_odds);
back_to_lay_odds(inclusive_ceil)
}
#[inline]
pub fn implied_maker_stake_from_taker(taker_stake: Money, trade_price: OddsX10000) -> Money {
let denom = trade_price.0 as i128 - 10_000;
if taker_stake.0 <= 0 || denom <= 0 {
return Money::zero();
}
let numerator = taker_stake.0 as i128 * denom;
ceil_to_cent(Money(clamp_i128_to_i64(numerator / 10_000)))
}
#[inline]
pub fn implied_taker_capacity_from_maker(maker_stake: Money, trade_price: OddsX10000) -> Money {
let denom = trade_price.0 as i128 - 10_000;
if maker_stake.0 <= 0 || denom <= 0 {
return Money::zero();
}
let numerator = maker_stake.0 as i128 * 10_000;
floor_to_cent(Money(clamp_i128_to_i64(numerator / denom)))
}