use super::{formulas, types::BookOrder};
use crate::{
book::protocol::{
command::{ReduceOrderCondition, ReduceOrderTarget},
reject::RejectReason,
},
types::{Money, OddsX10000},
};
pub(crate) fn validate_reduce_order_condition(
old: &BookOrder,
condition: Option<&ReduceOrderCondition>,
) -> Result<(), RejectReason> {
let Some(condition) = condition else {
return Ok(());
};
if condition
.expected_odds
.is_some_and(|odds| odds != old.price)
|| condition
.expected_stake
.is_some_and(|stake| stake != old.stake)
|| condition
.expected_matched_stake
.is_some_and(|matched| matched != old.matched)
|| condition
.expected_remaining_stake
.is_some_and(|remaining| remaining != old.remaining())
{
return Err(RejectReason::OrderStateChanged);
}
Ok(())
}
pub(crate) fn reduce_order_target_remaining(
old: &BookOrder,
target: Option<ReduceOrderTarget>,
) -> Result<Money, RejectReason> {
match target {
None => Ok(old.remaining()),
Some(ReduceOrderTarget::TotalStake(total_stake)) => {
if total_stake.0 < 0 {
return Err(RejectReason::InvalidStake);
}
if total_stake > old.stake {
return Err(RejectReason::ExposureIncreaseNotAllowed);
}
Ok(total_stake.saturating_sub(old.matched).clamp_non_negative())
}
Some(ReduceOrderTarget::RemainingStake(remaining_stake)) => {
if remaining_stake.0 < 0 {
return Err(RejectReason::InvalidStake);
}
Ok(remaining_stake)
}
}
}
pub(crate) fn ensure_reduce_only(
old: &BookOrder,
new_price: OddsX10000,
next_stake: Money,
) -> Result<(), RejectReason> {
let old_remaining = old.remaining();
if next_stake > old_remaining {
return Err(RejectReason::ExposureIncreaseNotAllowed);
}
let current_reserve = formulas::exchange_order_reserve(old.info.side, old.price, old_remaining);
let next_reserve = formulas::exchange_order_reserve(old.info.side, new_price, next_stake);
if next_reserve > current_reserve {
return Err(RejectReason::ExposureIncreaseNotAllowed);
}
Ok(())
}