apple_quant_algorithmic/order_manager/
client_pending.rs1use smallvec::SmallVec;
2use thiserror::Error;
3use tracing::instrument;
4
5#[allow(unused_imports)]
6use tracing::trace;
7
8use crate::{
9 backend::{LocalOrderId, OrderIdError, OrdersBackend},
10 order::{
11 dependency::client::ClientDeployedRemoteDependency, ClientOrder,
12 ClientOrderTracker, ClientTimingCondition, DeferredOrderActions,
13 DesiredVolumeOrder, DesiredVolumeOrderError, MatchableOrder, OrderAction,
14 TriggerError,
15 },
16 timestamp::{TickTimestamp, Timestamp, Timestamped},
17 volume::{DirectionalExposure, ZeroableVolume},
18 aggregation::TradeTradeTimestamp, instrument::InstrumentSpec,
19 liquidity::LiquidityEstimation,
20};
21
22use super::{OrdersCapacitySpec, PendingRemoteSubmitError};
23
24#[derive(Debug, Error)]
25pub enum PendingClientCancelError {
26 #[error("{0:?}")]
27 OrderId(#[from] OrderIdError),
28
29 #[error("{0:?}")]
30 DesiredVolumeOrder(#[from] DesiredVolumeOrderError),
31}
32
33#[derive(Debug, Error)]
34pub enum PendingClientActivateError {
35 #[error("{0:?}")]
36 PendingClientCancel(#[from] PendingClientCancelError),
37
38 #[error("{0:?}")]
39 PendingRemoteSubmit(#[from] PendingRemoteSubmitError),
40
41 #[error("{0:?}")]
42 Trigger(#[from] TriggerError),
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Hash)]
46pub struct PendingClientOrder<IS: InstrumentSpec> {
47 pub(crate) submission_timestamp: Timestamp,
48 pub(crate) local_order_id: LocalOrderId,
49 pub(crate) client_deployed_remote_dependency: ClientDeployedRemoteDependency<IS>,
50 pub(crate) client_order: ClientOrder<IS>,
51 pub(crate) liquidity_estimation: LiquidityEstimation<IS>,
52
53 pub(crate) dependencies: Vec<(
54 ClientTimingCondition,
55 OrderAction<IS>,
56 )>,
57}
58
59impl<IS: InstrumentSpec> PendingClientOrder<IS> {
60 pub(crate) fn new(
61 submission_timestamp: Timestamp,
62 local_order_id: LocalOrderId,
63 client_deployed_remote_dependency: ClientDeployedRemoteDependency<IS>,
64 client_order: ClientOrder<IS>,
65 dependencies: Vec<(
66 ClientTimingCondition,
67 OrderAction<IS>,
68 )>,
69 ) -> Self {
70 Self {
71 submission_timestamp,
72 local_order_id,
73 client_deployed_remote_dependency,
74 client_order,
75 liquidity_estimation: LiquidityEstimation::default(),
76 dependencies,
77 }
78 }
79}
80
81pub struct PendingClientOrders<
83 IS: InstrumentSpec,
84 CS: OrdersCapacitySpec,
85>(SmallVec<[PendingClientOrder<IS>; core::direct_const_arg!(CS::PENDING_CLIENT)]>);
86
87impl<
88 IS: InstrumentSpec,
89 CS: OrdersCapacitySpec,
90> PendingClientOrders<IS, CS> {
91 #[instrument(skip_all)]
92 pub fn submit<OB: OrdersBackend<IS>>(
93 &mut self,
94 tick_timestamp: &TickTimestamp,
95 directional_exposure: &mut DirectionalExposure<IS>,
96 client_order_tracker: &ClientOrderTracker,
97 client_order: ClientOrder<IS>,
98 client_deployed_remote_dependency: ClientDeployedRemoteDependency<IS>,
99 dependencies: Vec<(
100 ClientTimingCondition,
101 OrderAction<IS>,
102 )>,
103 ) -> Result<(), DesiredVolumeOrderError>
104 where
105 IS: Send,
106 {
107 let submission_timestamp = tick_timestamp.timestamp();
108 let local_order_id = *client_order_tracker.as_local_order_id();
109
110 #[cfg(feature = "log-trace-order-manager")]
111 trace!(
112 "Adding id: `{:?}` order: `{:?}`.",
113 local_order_id,
114 client_order
115 );
116
117 let desired_directional_intent_volume = client_deployed_remote_dependency.remote_order.desired_directional_intent_volume()?;
118
119 let pending_client_order = PendingClientOrder::new(
120 submission_timestamp,
121 local_order_id,
122 client_deployed_remote_dependency,
123 client_order,
124 dependencies,
125 );
126
127 self.0.push(pending_client_order);
128
129 directional_exposure.change_armed_directional_exposure(
130 &desired_directional_intent_volume.as_zeroable(),
131 );
132
133 Ok(())
134 }
135
136 #[instrument(skip_all)]
137 pub fn get(
138 &self,
139 local_order_id: &LocalOrderId,
140 ) -> Result<&PendingClientOrder<IS>, OrderIdError> {
141 let Some(
142 pending_client_order,
143 ) = self.0.iter().find(|
144 pending_client_order,
145 | &pending_client_order.local_order_id == local_order_id) else {
146 return local_order_id.err_invalid();
147 };
148
149 Ok(pending_client_order)
150 }
151
152 #[instrument(skip_all)]
153 pub(crate) fn get_mut(
154 &mut self,
155 local_order_id: &LocalOrderId,
156 ) -> Result<&mut PendingClientOrder<IS>, OrderIdError> {
157 let Some(
158 pending_client_order,
159 ) = self.0.iter_mut().find(|
160 pending_client_order,
161 | &pending_client_order.local_order_id == local_order_id) else {
162 return local_order_id.err_invalid();
163 };
164
165 Ok(pending_client_order)
166 }
167
168 #[instrument(skip_all)]
169 pub(crate) fn remove(
170 &mut self,
171 directional_exposure: &mut DirectionalExposure<IS>,
172 local_order_id: &LocalOrderId,
173 ) -> Result<PendingClientOrder<IS>, PendingClientCancelError> {
174 #[cfg(feature = "log-trace-order-manager")]
175 trace!("Removing id: `{:?}`.", local_order_id);
176
177 let Some(
178 idx,
179 ) = self.0.iter().position(|
180 pending_client_order,
181 | &pending_client_order.local_order_id == local_order_id) else {
182 return local_order_id.err_invalid();
183 };
184
185 let pending_client_order = self.0.swap_remove(idx);
186
187 let desired_directional_intent_volume = pending_client_order.client_deployed_remote_dependency.remote_order.desired_directional_intent_volume()?;
188
189 directional_exposure.change_armed_directional_exposure(
190 &desired_directional_intent_volume.as_flipped().as_zeroable(),
191 );
192
193 Ok(pending_client_order)
194 }
195
196 #[instrument(skip_all)]
197 pub(crate) async fn activate<OB: OrdersBackend<IS>>(
198 &mut self,
199 tick_timestamp: &TickTimestamp,
200 directional_exposure: &mut DirectionalExposure<IS>,
201 deferred_order_actions: &mut DeferredOrderActions<IS>,
202 local_order_id: &LocalOrderId,
203 ) -> Result<(), PendingClientActivateError>
204 where
205 IS: Send,
206 {
207 #[cfg(feature = "log-trace-order-manager")]
208 trace!("Activating id: `{:?}`.", local_order_id);
209
210 let pending_client_order = self.remove(directional_exposure, local_order_id)?;
211
212 for (
213 client_timing_condition,
214 order_action,
215 ) in pending_client_order.dependencies.iter() {
216 if client_timing_condition != &ClientTimingCondition::Activated {
217 continue;
218 }
219
220 deferred_order_actions.push(order_action.clone());
221 }
222
223 deferred_order_actions.push(
224 pending_client_order.client_deployed_remote_dependency.into_order_action(),
225 );
226
227 Ok(())
228 }
229
230 #[instrument(skip_all)]
231 pub fn tick_client_orders<'a>(
232 &mut self,
233 deferred_order_actions: &mut DeferredOrderActions<IS>,
234 directional_exposure: &mut DirectionalExposure<IS>,
235 just_added: impl ExactSizeIterator<Item = &'a TradeTradeTimestamp<IS>> + Clone,
236 )
237 where
238 IS: 'a,
239 {
240 let pending_client_orders = self.0.drain_filter(|
241 pending_client_order,
242 | {
243 trades_stream_drain_filter(
244 pending_client_order,
245 directional_exposure,
246 just_added.clone(),
247 )
248 });
249
250 for pending_client_order in pending_client_orders {
251 for (
252 client_timing_condition,
253 order_action,
254 ) in pending_client_order.dependencies.iter() {
255 if client_timing_condition != &ClientTimingCondition::Activated {
256 continue;
257 }
258
259 deferred_order_actions.push(order_action.clone());
260 }
261
262 deferred_order_actions.push(
263 pending_client_order.client_deployed_remote_dependency.into_order_action(),
264 );
265 }
266 }
267}
268
269impl<
270 IS: InstrumentSpec,
271 CS: OrdersCapacitySpec,
272> Default for PendingClientOrders<IS, CS> {
273 fn default() -> Self {
274 Self(SmallVec::default())
275 }
276}
277
278impl<
279 IS: InstrumentSpec,
280 CS: OrdersCapacitySpec,
281> std::fmt::Debug for PendingClientOrders<IS, CS> {
282 fn fmt(
283 &self,
284 f: &mut std::fmt::Formatter<'_>,
285 ) -> std::fmt::Result {
286 self.0.fmt(f)
287 }
288}
289
290#[instrument(skip_all)]
291fn trades_stream_drain_filter<
292 'a,
293 IS: InstrumentSpec,
294>(
295 pending_client_order: &mut PendingClientOrder<IS>,
296 directional_exposure: &mut DirectionalExposure<IS>,
297 just_added: impl ExactSizeIterator<Item = &'a TradeTradeTimestamp<IS>> + Clone,
298) -> bool
299where
300 IS: 'a,
301{
302 pending_client_order.liquidity_estimation.walk_trades(just_added);
303
304 let is_liquidable = pending_client_order.client_order.is_liquidable(
305 &pending_client_order.liquidity_estimation,
306 ).is_some();
307
308 if is_liquidable {
309 #[cfg(feature = "log-trace-order-manager")]
310 trace!(
311 "Activating client order `{:?}`",
312 pending_client_order.local_order_id
313 );
314
315 let delta_directional_exposure = pending_client_order
316 .client_deployed_remote_dependency
317 .remote_order
318 .desired_directional_intent_volume()
319 .unwrap()
320 .as_flipped().as_zeroable();
321
322 directional_exposure.change_armed_directional_exposure(
323 &delta_directional_exposure,
324 );
325 }
326
327 is_liquidable
328}