Skip to main content

apple_quant_algorithmic/order_manager/
client_pending.rs

1use smallvec::SmallVec;
2use thiserror::Error;
3
4use crate::{
5	aggregation::TradeTradeTimestamp,
6	backend::{LocalOrderId, OrderIdError, OrdersBackend},
7	instrument::InstrumentSpec,
8	liquidity::LiquidityEstimation,
9	order::{
10		ClientOrder, ClientOrderTracker, DeferredOrderActions, DesiredVolumeOrder,
11		DesiredVolumeOrderError, MatchableOrder, TriggerError,
12		dependency::client::ClientDeployedRemoteDependency,
13	},
14	timestamp::{TickTimestamp, Timestamp},
15	volume::DirectionalExposure,
16};
17
18use super::{OrdersCapacitySpec, PendingRemoteSubmitError};
19
20#[derive(Debug, Error)]
21pub enum PendingClientCancelError {
22	#[error("{0:?}")]
23	OrderId(#[from] OrderIdError),
24
25	#[error("{0:?}")]
26	DesiredVolumeOrder(#[from] DesiredVolumeOrderError),
27}
28
29#[derive(Debug, Error)]
30pub enum PendingClientActivateError {
31	#[error("{0:?}")]
32	PendingClientCancel(#[from] PendingClientCancelError),
33
34	#[error("{0:?}")]
35	PendingRemoteSubmit(#[from] PendingRemoteSubmitError),
36
37	#[error("{0:?}")]
38	Trigger(#[from] TriggerError),
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42pub struct PendingClientOrder<IS: InstrumentSpec> {
43	pub(crate) submission_timestamp: Timestamp,
44	pub(crate) local_order_id: LocalOrderId,
45	pub(crate) client_deployed_remote_dependency: ClientDeployedRemoteDependency<IS>,
46	pub(crate) client_order: ClientOrder<IS>,
47	pub(crate) liquidity_estimation: LiquidityEstimation<IS>,
48}
49
50impl<IS: InstrumentSpec> PendingClientOrder<IS> {
51	pub(crate) fn new(
52		submission_timestamp: Timestamp,
53		local_order_id: LocalOrderId,
54		client_deployed_remote_dependency: ClientDeployedRemoteDependency<IS>,
55		client_order: ClientOrder<IS>,
56	) -> Self {
57		Self {
58			submission_timestamp,
59			local_order_id,
60			client_deployed_remote_dependency,
61			client_order,
62			liquidity_estimation: LiquidityEstimation::default(),
63		}
64	}
65}
66
67/// Unordered list of pending client orders (eg. stop orders), where only this client knows the order and its intent. When the condition is tripped, it will be converted into a pending remote order, the state transition will take place, and the [`OrdersBackend`] used will become aware of the order.
68#[derive(Debug)]
69pub struct PendingClientOrders<IS: InstrumentSpec, CS: OrdersCapacitySpec>(
70	SmallVec<[PendingClientOrder<IS>; CS::PENDING_CLIENT]>,
71);
72
73impl<IS: InstrumentSpec, CS: OrdersCapacitySpec> PendingClientOrders<IS, CS> {
74	pub fn submit<OB: OrdersBackend<IS>>(
75		&mut self,
76		tick_timestamp: &TickTimestamp,
77		directional_exposure: &mut DirectionalExposure<IS>,
78		client_order_tracker: &ClientOrderTracker,
79		client_order: ClientOrder<IS>,
80		client_deployed_remote_dependency: ClientDeployedRemoteDependency<IS>,
81	) -> Result<(), DesiredVolumeOrderError>
82	where
83		IS: Send,
84	{
85		let submission_timestamp = tick_timestamp.timestamp();
86		let local_order_id = *client_order_tracker.as_local_order_id();
87
88		let desired_directional_intent_volume = client_deployed_remote_dependency
89			.remote_order
90			.desired_directional_intent_volume()?;
91
92		let pending_client_order = PendingClientOrder::new(
93			submission_timestamp,
94			local_order_id,
95			client_deployed_remote_dependency,
96			client_order,
97		);
98
99		self.0
100			.push(pending_client_order);
101
102		directional_exposure
103			.change_armed_directional_exposure(&desired_directional_intent_volume.as_zeroable());
104
105		Ok(())
106	}
107
108	pub fn get(
109		&self,
110		local_order_id: &LocalOrderId,
111	) -> Result<&PendingClientOrder<IS>, OrderIdError> {
112		let Some(pending_client_order) = self
113			.0
114			.iter()
115			.find(|pending_client_order| &pending_client_order.local_order_id == local_order_id)
116		else {
117			return local_order_id.err_invalid();
118		};
119
120		Ok(pending_client_order)
121	}
122
123	pub(crate) fn get_mut(
124		&mut self,
125		local_order_id: &LocalOrderId,
126	) -> Result<&mut PendingClientOrder<IS>, OrderIdError> {
127		let Some(pending_client_order) = self
128			.0
129			.iter_mut()
130			.find(|pending_client_order| &pending_client_order.local_order_id == local_order_id)
131		else {
132			return local_order_id.err_invalid();
133		};
134
135		Ok(pending_client_order)
136	}
137
138	pub(crate) fn remove(
139		&mut self,
140		directional_exposure: &mut DirectionalExposure<IS>,
141		local_order_id: &LocalOrderId,
142	) -> Result<PendingClientOrder<IS>, PendingClientCancelError> {
143		let Some(idx) = self
144			.0
145			.iter()
146			.position(|pending_client_order| {
147				&pending_client_order.local_order_id == local_order_id
148			})
149		else {
150			return local_order_id.err_invalid();
151		};
152
153		let pending_client_order = self.0.swap_remove(idx);
154
155		let desired_directional_intent_volume = pending_client_order
156			.client_deployed_remote_dependency
157			.remote_order
158			.desired_directional_intent_volume()?;
159
160		directional_exposure.change_armed_directional_exposure(
161			&desired_directional_intent_volume
162				.as_zeroable()
163				.as_flipped(),
164		);
165
166		Ok(pending_client_order)
167	}
168
169	pub(crate) async fn activate<OB: OrdersBackend<IS>>(
170		&mut self,
171		tick_timestamp: &TickTimestamp,
172		directional_exposure: &mut DirectionalExposure<IS>,
173		deferred_order_actions: &mut DeferredOrderActions<IS>,
174		local_order_id: &LocalOrderId,
175	) -> Result<(), PendingClientActivateError>
176	where
177		IS: Send,
178	{
179		let PendingClientOrder {
180			submission_timestamp,
181			local_order_id,
182			client_deployed_remote_dependency,
183			client_order,
184			liquidity_estimation,
185		} = self.remove(
186			directional_exposure,
187			local_order_id,
188		)?;
189
190		deferred_order_actions.push(client_deployed_remote_dependency.into_order_action());
191
192		Ok(())
193	}
194
195	pub fn tick_client_orders<'a>(
196		&mut self,
197		deferred_order_actions: &mut DeferredOrderActions<IS>,
198		directional_exposure: &mut DirectionalExposure<IS>,
199		just_added: impl ExactSizeIterator<Item = &'a TradeTradeTimestamp<IS>> + Clone,
200	) where
201		IS: 'a,
202	{
203		let pending_client_orders = self
204			.0
205			.drain_filter(|pending_client_order| {
206				trades_stream_drain_filter(
207					pending_client_order,
208					directional_exposure,
209					just_added.clone(),
210				)
211			});
212
213		for pending_client_order in pending_client_orders {
214			deferred_order_actions.push(
215				pending_client_order
216					.client_deployed_remote_dependency
217					.into_order_action(),
218			);
219		}
220	}
221}
222
223impl<IS: InstrumentSpec, CS: OrdersCapacitySpec> Default for PendingClientOrders<IS, CS> {
224	fn default() -> Self {
225		Self(SmallVec::default())
226	}
227}
228
229// impl<IS: InstrumentSpec, CS: OrdersCapacitySpec> MarketDataStream<IS>
230// 	for PendingClientOrders<IS, CS>
231// {
232// }
233
234fn trades_stream_drain_filter<'a, IS: InstrumentSpec>(
235	pending_client_order: &mut PendingClientOrder<IS>,
236	directional_exposure: &mut DirectionalExposure<IS>,
237	just_added: impl ExactSizeIterator<Item = &'a TradeTradeTimestamp<IS>> + Clone,
238) -> bool
239where
240	IS: 'a,
241{
242	pending_client_order
243		.liquidity_estimation
244		.walk_trades(just_added);
245
246	let is_liquidable = pending_client_order
247		.client_order
248		.is_liquidable(&pending_client_order.liquidity_estimation)
249		.is_some();
250
251	if is_liquidable {
252		let delta_directional_exposure = pending_client_order
253			.client_deployed_remote_dependency
254			.remote_order
255			.desired_directional_intent_volume()
256			.unwrap()
257			.as_zeroable()
258			.as_flipped();
259
260		directional_exposure.change_armed_directional_exposure(&delta_directional_exposure);
261	}
262
263	is_liquidable
264}