Skip to main content

apple_quant_algorithmic/order/
client.rs

1use num_traits::Signed;
2
3use crate::{
4	backend::OrderIdGenerator,
5	instrument::InstrumentSpec,
6	liquidity::LiquidityEstimation,
7	price::{AbsolutePrice, BidAskPriceSpread, Price},
8	volume::DirectionalIntent,
9};
10
11use super::{ClientOrderTracker, MatchableOrder};
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub enum ClientOrder<IS: InstrumentSpec> {
15	Stop(ClientStopOrder<IS>),
16}
17
18impl<IS: InstrumentSpec> ClientOrder<IS> {
19	pub(crate) fn parent_processed(
20		&mut self,
21		bid_ask_price_spread: &BidAskPriceSpread<IS>,
22	) {
23		#[allow(irrefutable_let_patterns)]
24		let Self::Stop(client_stop_order) = self else {
25			return;
26		};
27
28		client_stop_order.parent_processed(bid_ask_price_spread);
29	}
30
31	pub fn register(
32		&self,
33		order_id_generator: &mut OrderIdGenerator,
34	) -> ClientOrderTracker
35	where
36		IS: Send,
37	{
38		let local_order_id = order_id_generator.next_local_order_id();
39
40		ClientOrderTracker::new(local_order_id)
41	}
42}
43
44impl<IS: InstrumentSpec> MatchableOrder<IS> for ClientOrder<IS> {
45	fn is_liquidable(
46		&self,
47		liquidity_estimation: &LiquidityEstimation<IS>,
48	) -> Option<AbsolutePrice<IS>> {
49		match self {
50			Self::Stop(client_stop_order) => client_stop_order.is_liquidable(liquidity_estimation),
51		}
52	}
53}
54
55impl<IS: InstrumentSpec> From<ClientStopOrder<IS>> for ClientOrder<IS> {
56	fn from(value: ClientStopOrder<IS>) -> Self {
57		Self::Stop(value)
58	}
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Hash)]
62pub struct ClientStopOrder<IS: InstrumentSpec> {
63	pub price: Price<IS>,
64	pub cross_over_directional_intent: DirectionalIntent,
65}
66
67impl<IS: InstrumentSpec> ClientStopOrder<IS> {
68	pub fn new(
69		price: Price<IS>,
70		cross_over_directional_intent: DirectionalIntent,
71	) -> Self {
72		Self {
73			price,
74			cross_over_directional_intent,
75		}
76	}
77
78	pub(crate) fn parent_processed(
79		&mut self,
80		bid_ask_price_spread: &BidAskPriceSpread<IS>,
81	) {
82		let Price::Relative(relative_price) = &mut self.price else {
83			return;
84		};
85
86		let absolute_price: AbsolutePrice<IS> = if relative_price.is_positive() {
87			AbsolutePrice::new(*bid_ask_price_spread.ask_price + **relative_price)
88		} else {
89			AbsolutePrice::new(*bid_ask_price_spread.bid_price + **relative_price)
90		};
91
92		self.price = Price::Absolute(absolute_price);
93	}
94}
95
96impl<IS: InstrumentSpec> MatchableOrder<IS> for ClientStopOrder<IS> {
97	fn is_liquidable(
98		&self,
99		liquidity_estimation: &LiquidityEstimation<IS>,
100	) -> Option<AbsolutePrice<IS>> {
101		let Some((bid_price_volume_level, ask_price_volume_level)) =
102			liquidity_estimation.furthest_bid_ask()
103		else {
104			return None;
105		};
106
107		let Price::Absolute(absolute_price) = &self.price else {
108			return None;
109		};
110
111		match self.cross_over_directional_intent {
112			DirectionalIntent::Positive => {
113				if &ask_price_volume_level.price < absolute_price {
114					return None;
115				}
116
117				Some(ask_price_volume_level.price)
118			}
119			DirectionalIntent::Negative => {
120				if &bid_price_volume_level.price > absolute_price {
121					return None;
122				}
123
124				Some(bid_price_volume_level.price)
125			}
126		}
127	}
128}