apple-quant-algorithmic 0.1.0

Apple Quant's algorithmic trading api
Documentation
use smallvec::SmallVec;
use thiserror::Error;

use crate::{
	backend::{
		LocalOrderId, OrderIdError, OrdersBackend, OrdersBackendSubmitRemoteOrderError,
		RemoteOrderId,
	},
	instrument::InstrumentSpec,
	order::{
		DeferredOrderActions, DesiredVolumeOrder, DesiredVolumeOrderError, OrderGoal, RemoteOrder,
		RemoteOrderTracker, RemoteTimingCondition, TriggerError,
		dependency::remote::RemoteOrderAction,
	},
	price::AbsolutePrice,
	timestamp::{TickTimestamp, Timestamp},
	volume::{DirectionalExposure, DirectionalIntentVolume},
};

use super::{
	OrdersCapacitySpec, PendingClientCancelError, WorkingRemoteOrderSubmitError,
	WorkingRemoteOrders,
};

#[derive(Debug, Error)]
pub enum PendingRemoteSubmitError {
	#[error("{0:?}")]
	DesiredVolumeOrder(#[from] DesiredVolumeOrderError),

	#[error("{0:?}")]
	OrdersBackendSubmitRemoteOrder(#[from] OrdersBackendSubmitRemoteOrderError),
}

#[derive(Debug, Error)]
pub enum PendingRemoteProcessedError {
	#[error("{0:?}")]
	OrderId(#[from] OrderIdError),

	#[error("{0:?}")]
	PendingClientCancel(#[from] PendingClientCancelError),

	#[error("{0:?}")]
	WorkingRemoteOrderSubmit(#[from] WorkingRemoteOrderSubmitError),

	#[error("{0:?}")]
	Trigger(#[from] TriggerError),
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PendingRemoteOrder<IS: InstrumentSpec, CS: OrdersCapacitySpec> {
	pub(crate) submission_timestamp: Timestamp,
	pub(crate) local_order_id: LocalOrderId,
	pub(crate) dependencies: SmallVec<
		[(
			RemoteTimingCondition,
			RemoteOrderAction<IS>,
		); CS::DEPENDENCY],
	>,
	pub(crate) order_goal: OrderGoal<IS>,
	pub(crate) desired_directional_intent_volume: DirectionalIntentVolume<IS>,
	pub(crate) rest_at: Option<AbsolutePrice<IS>>,
}

impl<IS: InstrumentSpec, CS: OrdersCapacitySpec> PendingRemoteOrder<IS, CS> {
	pub(crate) fn new(
		submission_timestamp: Timestamp,
		local_order_id: LocalOrderId,
		dependencies: impl IntoIterator<
			Item = (
				RemoteTimingCondition,
				RemoteOrderAction<IS>,
			),
		>,
		order_goal: OrderGoal<IS>,
		desired_directional_intent_volume: DirectionalIntentVolume<IS>,
		rest_at: Option<AbsolutePrice<IS>>,
	) -> Self {
		Self {
			submission_timestamp,
			local_order_id,
			dependencies: dependencies
				.into_iter()
				.collect(),
			order_goal,
			desired_directional_intent_volume,
			rest_at,
		}
	}
}

/// Unordered list of pending remote orders. The order will be booked into a fill, partial fill, or resting state before this client knows. Once the client recognizes the booking, the order will be moved to [`working_remote_orders`].
#[derive(Debug)]
pub struct PendingRemoteOrders<IS: InstrumentSpec, CS: OrdersCapacitySpec>(
	SmallVec<[PendingRemoteOrder<IS, CS>; CS::PENDING_REMOTE]>,
);

impl<IS: InstrumentSpec, CS: OrdersCapacitySpec> PendingRemoteOrders<IS, CS> {
	pub async fn submit<OB: OrdersBackend<IS>>(
		&mut self,
		directional_exposure: &mut DirectionalExposure<IS>,
		orders_backend: &mut OB,
		tick_timestamp: &TickTimestamp,
		remote_order_tracker: RemoteOrderTracker<IS>,
		remote_order: RemoteOrder<IS>,
		dependencies: impl IntoIterator<
			Item = (
				RemoteTimingCondition,
				RemoteOrderAction<IS>,
			),
		>,
	) -> Result<(), PendingRemoteSubmitError>
	where
		IS: Send,
	{
		let submission_timestamp = tick_timestamp.timestamp();
		let local_order_id = *remote_order_tracker.as_local_order_id();
		let order_goal = *remote_order_tracker.as_order_goal();

		let desired_directional_intent_volume = remote_order.desired_directional_intent_volume()?;

		let rest_at = if let RemoteOrder::Limit(limit_order) = &remote_order {
			Some(
				limit_order
					.price
					.into_absolute()
					.unwrap(),
			)
		} else {
			None
		};

		orders_backend
			.submit_order(
				&local_order_id,
				&submission_timestamp,
				remote_order,
			)
			.await?;

		let pending_remote_order = PendingRemoteOrder::new(
			submission_timestamp,
			local_order_id,
			dependencies,
			order_goal,
			desired_directional_intent_volume,
			rest_at,
		);

		self.0
			.push(pending_remote_order);

		directional_exposure
			.change_armed_directional_exposure(&desired_directional_intent_volume.as_zeroable());

		Ok(())
	}

	pub fn get(
		&self,
		local_order_id: &LocalOrderId,
	) -> Result<&PendingRemoteOrder<IS, CS>, OrderIdError> {
		let Some(pending_remote_order) = self
			.0
			.iter()
			.find(|pending_remote_order| &pending_remote_order.local_order_id == local_order_id)
		else {
			return local_order_id.err_invalid();
		};

		Ok(pending_remote_order)
	}

	pub(crate) fn get_mut(
		&mut self,
		local_order_id: &LocalOrderId,
	) -> Result<&mut PendingRemoteOrder<IS, CS>, OrderIdError> {
		let Some(pending_remote_order) = self
			.0
			.iter_mut()
			.find(|pending_remote_order| &pending_remote_order.local_order_id == local_order_id)
		else {
			return local_order_id.err_invalid();
		};

		Ok(pending_remote_order)
	}

	pub(crate) fn remove(
		&mut self,
		directional_exposure: &mut DirectionalExposure<IS>,
		local_order_id: &LocalOrderId,
	) -> Result<PendingRemoteOrder<IS, CS>, PendingClientCancelError> {
		let Some(idx) = self
			.0
			.iter()
			.position(|pending_remote_order| {
				&pending_remote_order.local_order_id == local_order_id
			})
		else {
			return local_order_id.err_invalid();
		};

		let pending_remote_order = self.0.swap_remove(idx);

		let desired_directional_intent_volume =
			&pending_remote_order.desired_directional_intent_volume;

		directional_exposure.change_armed_directional_exposure(
			&desired_directional_intent_volume
				.as_zeroable()
				.as_flipped(),
		);

		Ok(pending_remote_order)
	}

	pub(crate) async fn acknowledged<OB: OrdersBackend<IS>>(
		&mut self,
		tick_timestamp: &TickTimestamp,
		working_remote_orders: &mut WorkingRemoteOrders<IS, CS>,
		directional_exposure: &mut DirectionalExposure<IS>,
		deferred_order_actions: &mut DeferredOrderActions<IS>,
		local_order_id: &LocalOrderId,
		remote_order_id: RemoteOrderId,
	) -> Result<(), PendingRemoteProcessedError>
	where
		IS: Send,
	{
		let mut pending_remote_order = self.remove(
			directional_exposure,
			local_order_id,
		)?;

		for (remote_timing_condition, remote_order_action) in pending_remote_order
			.dependencies
			.iter_mut()
		{
			if remote_timing_condition != &RemoteTimingCondition::Acknowledged {
				continue;
			}

			deferred_order_actions.push(
				remote_order_action
					.clone()
					.into_order_action(),
			);
		}

		working_remote_orders.submit::<OB>(
			directional_exposure,
			tick_timestamp,
			pending_remote_order,
			remote_order_id,
		)?;

		Ok(())
	}
}

impl<IS: InstrumentSpec, CS: OrdersCapacitySpec> Default for PendingRemoteOrders<IS, CS> {
	fn default() -> Self {
		Self(SmallVec::default())
	}
}