apple-quant-algorithmic 0.1.0

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

use crate::{backend::LocalOrderId, instrument::InstrumentSpec, volume::DirectionalIntentVolume};

use super::OrderExecutionExpectation;

#[derive(Debug, Error)]
pub(crate) enum AddOrderGoalError {
	#[error("{0:?} already exists.")]
	AlreadyExists(OrderGoalT),
}

#[derive(Debug, Error)]
pub(crate) enum RemoveOrderGoalError {
	#[error("{0:?} already exists.")]
	AlreadyExists(OrderGoalT),
}

#[derive(Debug, Error)]
pub enum OrderGoalAsExecutionExpectationError {
	#[error("Can not convert {0:?} to an OrderExecutionExpectation.")]
	InvalidConversion(OrderGoalT),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OrderGoalT {
	CancelOrder,
	CancelRevertOrder,
	SubmitImmediatelyExecutableRemoteOrder,
	SubmitRestingRemoteOrder,
}

impl<IS: InstrumentSpec> From<OrderGoal<IS>> for OrderGoalT {
	fn from(value: OrderGoal<IS>) -> Self {
		(&value).into()
	}
}

impl<IS: InstrumentSpec> From<&OrderGoal<IS>> for OrderGoalT {
	fn from(value: &OrderGoal<IS>) -> Self {
		match value {
			OrderGoal::CancelOrder(_) => Self::CancelOrder,
			OrderGoal::CancelRevertOrder(_) => Self::CancelRevertOrder,
			OrderGoal::SubmitImmediatelyExecutableRemoteOrder {
				local_order_id: _,
				directional_intent_volume: _,
			} => Self::SubmitImmediatelyExecutableRemoteOrder,
			OrderGoal::SubmitRestingRemoteOrder {
				local_order_id: _,
				directional_intent_volume: _,
			} => Self::SubmitRestingRemoteOrder,
		}
	}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OrderGoal<IS: InstrumentSpec> {
	/// Cancels any remaining potential exposure for an order that has not been matched yet. Does not revert partially filled or fully filled orders. After the goal is completed, there should be no potential exposure, however, there may be effective exposure due to a partial or a full fill that could not be canceled passively.
	CancelOrder(LocalOrderId),

	/// Cancels any remaining potential exposure for an order that has not been matched yet. Aggressively reverts any partial fills fragments that are then received by the client. After the goal is completed, there should be no potential or effective exposure related to this order anymore.
	CancelRevertOrder(LocalOrderId),

	/// Completes when the order is fully matched (fully filled only, not partially fill).
	SubmitImmediatelyExecutableRemoteOrder {
		local_order_id: LocalOrderId,
		directional_intent_volume: DirectionalIntentVolume<IS>,
	},

	/// Completes when the order is booked (resting, partially filled, or fully filled).
	SubmitRestingRemoteOrder {
		local_order_id: LocalOrderId,
		directional_intent_volume: DirectionalIntentVolume<IS>,
	},
}

impl<IS: InstrumentSpec> OrderGoal<IS> {
	pub fn new_submit_remote_with_execution_expectation(
		local_order_id: LocalOrderId,
		directional_intent_volume: DirectionalIntentVolume<IS>,
		order_execution_expectation: OrderExecutionExpectation,
	) -> Self {
		match order_execution_expectation {
			OrderExecutionExpectation::Immediate => Self::SubmitImmediatelyExecutableRemoteOrder {
				local_order_id,
				directional_intent_volume,
			},
			OrderExecutionExpectation::Resting => Self::SubmitRestingRemoteOrder {
				local_order_id,
				directional_intent_volume,
			},
		}
	}

	pub fn as_order_execution_expectation(
		&self
	) -> Result<OrderExecutionExpectation, OrderGoalAsExecutionExpectationError> {
		match self {
			Self::CancelOrder(_) => {
				Err(OrderGoalAsExecutionExpectationError::InvalidConversion(self.as_t()))
			}
			Self::CancelRevertOrder(_) => {
				Err(OrderGoalAsExecutionExpectationError::InvalidConversion(self.as_t()))
			}
			Self::SubmitImmediatelyExecutableRemoteOrder {
				local_order_id: _,
				directional_intent_volume: _,
			} => Ok(OrderExecutionExpectation::Immediate),
			Self::SubmitRestingRemoteOrder {
				local_order_id: _,
				directional_intent_volume: _,
			} => Ok(OrderExecutionExpectation::Resting),
		}
	}

	pub fn as_local_order_id(&self) -> Result<&LocalOrderId, ()> {
		match self {
			Self::CancelOrder(local_order_id) => Ok(local_order_id),
			Self::CancelRevertOrder(local_order_id) => Ok(local_order_id),
			Self::SubmitImmediatelyExecutableRemoteOrder {
				local_order_id,
				directional_intent_volume,
			} => Ok(local_order_id),
			Self::SubmitRestingRemoteOrder {
				local_order_id,
				directional_intent_volume,
			} => Ok(local_order_id),
		}
	}

	pub fn as_directional_intent_volume(&self) -> Result<&DirectionalIntentVolume<IS>, ()> {
		match self {
			Self::CancelOrder(_) => Err(()),
			Self::CancelRevertOrder(_) => Err(()),
			Self::SubmitImmediatelyExecutableRemoteOrder {
				local_order_id: _,
				directional_intent_volume,
			} => Ok(directional_intent_volume),
			Self::SubmitRestingRemoteOrder {
				local_order_id: _,
				directional_intent_volume,
			} => Ok(directional_intent_volume),
		}
	}

	pub fn as_t(&self) -> OrderGoalT {
		self.into()
	}
}

#[derive(Debug)]
pub struct ActiveOrderGoals<IS: InstrumentSpec>(SmallVec<[OrderGoal<IS>; 4]>);

impl<IS: InstrumentSpec> ActiveOrderGoals<IS> {
	pub(crate) fn reset(&mut self) {
		self.0.clear()
	}

	pub(crate) fn add_order_goal(
		&mut self,
		order_goal: OrderGoal<IS>,
	) -> Result<(), AddOrderGoalError> {
		if self
			.0
			.iter()
			.find(|&iter_order_goal| iter_order_goal == &order_goal)
			.is_some()
		{
			return Err(AddOrderGoalError::AlreadyExists(order_goal.as_t()));
		}

		self.0.push(order_goal);
		Ok(())
	}

	pub(crate) fn remove_order_goal(
		&mut self,
		order_goal: &OrderGoal<IS>,
	) -> Result<(), RemoveOrderGoalError> {
		let Some(active_order_goals_idx) = self
			.0
			.iter()
			.position(|iter_order_goal| iter_order_goal == order_goal)
		else {
			return Err(RemoveOrderGoalError::AlreadyExists(order_goal.as_t()));
		};

		self.0
			.swap_remove(active_order_goals_idx);
		Ok(())
	}

	pub fn contains(
		&self,
		order_goal: &OrderGoal<IS>,
	) -> bool {
		self.0.contains(order_goal)
	}
}

impl<IS: InstrumentSpec> Default for ActiveOrderGoals<IS> {
	fn default() -> Self {
		Self(SmallVec::default())
	}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StateGoal {
	CancelAll,
	CancelFlattenAll,
	CancelFlattenAllShutdown,
	CancelFlattenAllRestart,
}

impl StateGoal {
	pub fn completed_result(&self) -> UpdateStateGoalResult {
		match self {
			Self::CancelAll => UpdateStateGoalResult::Inactive,
			Self::CancelFlattenAll => UpdateStateGoalResult::Inactive,
			Self::CancelFlattenAllShutdown => UpdateStateGoalResult::Shutdown,
			Self::CancelFlattenAllRestart => UpdateStateGoalResult::Restart,
		}
	}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UpdateStateGoalResult {
	Active,
	Inactive,
	Shutdown,
	Restart,
}

#[derive(Debug, Default, Deref)]
pub struct ActiveStateGoal(Option<StateGoal>);