apple_quant_algorithmic/order/dependency/client/
cancel.rs1#[allow(unused_imports)]
2use apple_quant_core::log::trace;
3
4use apple_quant_core::log::{error, warn, CtxExt};
5
6use tracing::instrument;
7
8use crate::{
9 backend::{LocalOrderId, OrderIdGenerator, OrdersBackend},
10 order::{OrderDependency, RemoteOrderBuilder, StateGoal, TriggerError},
11 order_manager::{OrderManager, OrdersCapacitySpec, PendingClientCancelError},
12 volume::{AggressorSide, DirectionalExposure, DirectionalIntent, ZeroableExt},
13 instrument::InstrumentSpec, points::Subpoints, timestamp::TickTimestamp,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct CancelClientOrder {
18 pub local_order_id: LocalOrderId,
19 pub attempt_exposure_reversion: bool,
20}
21
22impl CancelClientOrder {
23 pub fn new(
24 local_order_id: LocalOrderId,
25 attempt_exposure_reversion: bool,
26 ) -> Self {
27 Self {
28 local_order_id,
29 attempt_exposure_reversion,
30 }
31 }
32}
33
34impl<IS: InstrumentSpec> OrderDependency<IS> for CancelClientOrder {
35 #[instrument(skip_all)]
38 async fn trigger<
39 OB: OrdersBackend<IS>,
40 CS: OrdersCapacitySpec,
41 >(
42 &self,
43 tick_timestamp: &TickTimestamp,
44 order_manager: &mut OrderManager<IS, CS>,
45 orders_backend: &mut OB,
46 order_id_generator: &mut OrderIdGenerator,
47 directional_exposure: &mut DirectionalExposure<IS>,
48 state_goal: &mut StateGoal,
49 ) -> Result<bool, TriggerError>
50 where
51 IS: Send,
52 {
53 #[cfg(feature = "log-trace-order-actions")]
54 trace!("Triggered order action: `{self:#?}`.");
55
56 match order_manager.pending_client_orders.remove(
57 directional_exposure,
58 &self.local_order_id,
59 ) {
60 Ok(_) => return Ok(false),
61 Err(
62 error,
63 ) => {
64 if !self.attempt_exposure_reversion {
65 return Err(error.into());
66 }
67
68 match error {
69 PendingClientCancelError::OrderId(_) => {},
70 _ => return Err(error.into()),
71 }
72 },
73 }
74
75 let Some(
76 effective_directional_intent_volume,
77 ) = directional_exposure.combined().into_optional_nonzero() else {
78 warn!("Exposure reversion trigger failed with 0 combined directional exposure.");
79
80 return Ok(false);
81 };
82
83 let aggressor_side = match effective_directional_intent_volume.directional_intent {
85 DirectionalIntent::Positive => AggressorSide::Bid,
86 DirectionalIntent::Negative => AggressorSide::Ask,
87 };
88
89 let directionless_volume = effective_directional_intent_volume.directionless_volume;
90
91 warn!(
92 "Exposure reversion triggered. Submitting detached market `{:?}` with `{:?}`.",
93 aggressor_side,
94 directionless_volume
95 );
96
97 let order = match RemoteOrderBuilder::<IS>::new_market_parts(
98 order_id_generator,
99 aggressor_side,
100 directionless_volume,
101 ) {
102 Ok(
103 remote_order_builder,
104 ) => remote_order_builder,
105 error_result => {
106 error!(
107 "{:?}",
108 error_result.ctx("While constructing exposure reversion market order.")
109 );
110
111 return Ok(false);
112 },
113 };
114
115 let order_action = order.into_condensed().into_order_action::<4>().0;
116
117 Box::pin(order_action.trigger(
118 tick_timestamp,
119 order_manager,
120 orders_backend,
121 order_id_generator,
122 directional_exposure,
123 state_goal,
124 )).await
125 }
126}