use num_traits::Signed;
use crate::{
backend::OrderIdGenerator,
instrument::InstrumentSpec,
liquidity::LiquidityEstimation,
price::{AbsolutePrice, BidAskPriceSpread, Price},
volume::DirectionalIntent,
};
use super::{ClientOrderTracker, MatchableOrder};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClientOrder<IS: InstrumentSpec> {
Stop(ClientStopOrder<IS>),
}
impl<IS: InstrumentSpec> ClientOrder<IS> {
pub(crate) fn parent_processed(
&mut self,
bid_ask_price_spread: &BidAskPriceSpread<IS>,
) {
#[allow(irrefutable_let_patterns)]
let Self::Stop(client_stop_order) = self else {
return;
};
client_stop_order.parent_processed(bid_ask_price_spread);
}
pub fn register(
&self,
order_id_generator: &mut OrderIdGenerator,
) -> ClientOrderTracker
where
IS: Send,
{
let local_order_id = order_id_generator.next_local_order_id();
ClientOrderTracker::new(local_order_id)
}
}
impl<IS: InstrumentSpec> MatchableOrder<IS> for ClientOrder<IS> {
fn is_liquidable(
&self,
liquidity_estimation: &LiquidityEstimation<IS>,
) -> Option<AbsolutePrice<IS>> {
match self {
Self::Stop(client_stop_order) => client_stop_order.is_liquidable(liquidity_estimation),
}
}
}
impl<IS: InstrumentSpec> From<ClientStopOrder<IS>> for ClientOrder<IS> {
fn from(value: ClientStopOrder<IS>) -> Self {
Self::Stop(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ClientStopOrder<IS: InstrumentSpec> {
pub price: Price<IS>,
pub cross_over_directional_intent: DirectionalIntent,
}
impl<IS: InstrumentSpec> ClientStopOrder<IS> {
pub fn new(
price: Price<IS>,
cross_over_directional_intent: DirectionalIntent,
) -> Self {
Self {
price,
cross_over_directional_intent,
}
}
pub(crate) fn parent_processed(
&mut self,
bid_ask_price_spread: &BidAskPriceSpread<IS>,
) {
let Price::Relative(relative_price) = &mut self.price else {
return;
};
let absolute_price: AbsolutePrice<IS> = if relative_price.is_positive() {
AbsolutePrice::new(*bid_ask_price_spread.ask_price + **relative_price)
} else {
AbsolutePrice::new(*bid_ask_price_spread.bid_price + **relative_price)
};
self.price = Price::Absolute(absolute_price);
}
}
impl<IS: InstrumentSpec> MatchableOrder<IS> for ClientStopOrder<IS> {
fn is_liquidable(
&self,
liquidity_estimation: &LiquidityEstimation<IS>,
) -> Option<AbsolutePrice<IS>> {
let Some((bid_price_volume_level, ask_price_volume_level)) =
liquidity_estimation.furthest_bid_ask()
else {
return None;
};
let Price::Absolute(absolute_price) = &self.price else {
return None;
};
match self.cross_over_directional_intent {
DirectionalIntent::Positive => {
if &ask_price_volume_level.price < absolute_price {
return None;
}
Some(ask_price_volume_level.price)
}
DirectionalIntent::Negative => {
if &bid_price_volume_level.price > absolute_price {
return None;
}
Some(bid_price_volume_level.price)
}
}
}
}