apple_quant_algorithmic/order/
client.rs1use 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}