apple-quant-algorithmic 0.4.0

Apple Quant's algorithmic library.
Documentation
#[cfg(feature = "log-trace-order-manager")]
use apple_quant_core::log::trace;

use crate::{
	price::{AbsolutePrice, BidAskPriceSpread, ProtoabsolutePrice},
	volume::{
		AggressiveVolume, AggressorSide, DirectionalIntentVolume, DirectionlessVolume,
		RestingSide, RestingVolume,
	},
	backend::OrderIdGenerator, instrument::InstrumentSpec, liquidity::LiquidityEstimation,
};

use super::{
	DesiredVolumeOrder, DesiredVolumeOrderError, MatchableOrder,
	OrderExecutionExpectation, RemoteOrderTracker,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RemoteOrder<IS: InstrumentSpec> {
	Market(MarketOrder<IS>),
	Limit(LimitOrder<IS>),
}

impl<IS: InstrumentSpec> RemoteOrder<IS> {
	pub(crate) fn parent_processed(
		&mut self,
		bid_ask_price_spread: &BidAskPriceSpread<IS>,
	) {
		let Self::Limit(
			limit_order,
		) = self else {
			return;
		};

		limit_order.parent_processed(bid_ask_price_spread);
	}

	pub fn as_order_execution_expectation(
		&self,
	) -> OrderExecutionExpectation {
		match self {
			Self::Market(_) => OrderExecutionExpectation::Immediate,
			Self::Limit(
				limit_order,
			) => limit_order.order_execution_expectation,
		}
	}

	pub fn register(
		&self,
		order_id_generator: &mut OrderIdGenerator,
	) -> Result<RemoteOrderTracker, DesiredVolumeOrderError>
	where
		IS: Send,
	{
		let local_order_id = order_id_generator.next_local_order_id();

		#[cfg(feature = "log-trace-order-manager")]
		trace!("Registering order `{self:?}` with `{local_order_id:?}`");

		let directional_intent_volume = self.desired_directional_intent_volume()?;
		let order_execution_expectation = self.as_order_execution_expectation();

		Ok(RemoteOrderTracker::new(local_order_id))
	}
}

impl<IS: InstrumentSpec> DesiredVolumeOrder<IS> for RemoteOrder<IS> {
	fn desired_directional_intent_volume(
		&self,
	) -> Result<DirectionalIntentVolume<IS>, DesiredVolumeOrderError> {
		match self {
			Self::Market(
				market_order,
			) => market_order.desired_directional_intent_volume(),
			Self::Limit(
				limit_order,
			) => limit_order.desired_directional_intent_volume(),
		}
	}

	fn desired_directionless_volume(
		&self,
	) -> Result<&DirectionlessVolume<IS>, DesiredVolumeOrderError> {
		match self {
			Self::Market(
				market_order,
			) => market_order.desired_directionless_volume(),
			Self::Limit(
				limit_order,
			) => limit_order.desired_directionless_volume(),
		}
	}
}

impl<IS: InstrumentSpec> MatchableOrder<IS> for RemoteOrder<IS> {
	fn is_liquidable(
		&self,
		liquidity_estimation: &LiquidityEstimation<IS>,
	) -> Option<AbsolutePrice<IS>> {
		match self {
			Self::Market(
				market_order,
			) => market_order.is_liquidable(liquidity_estimation),
			Self::Limit(
				limit_order,
			) => limit_order.is_liquidable(liquidity_estimation),
		}
	}
}

impl<IS: InstrumentSpec> From<MarketOrder<IS>> for RemoteOrder<IS> {
	fn from(
		value: MarketOrder<IS>,
	) -> Self {
		Self::Market(value)
	}
}

impl<IS: InstrumentSpec> From<LimitOrder<IS>> for RemoteOrder<IS> {
	fn from(
		value: LimitOrder<IS>,
	) -> Self {
		Self::Limit(value)
	}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MarketOrder<IS: InstrumentSpec> {
	pub aggressive_volume: AggressiveVolume<IS>,
}

impl<IS: InstrumentSpec> MarketOrder<IS> {
	pub fn new(
		aggressive_volume: AggressiveVolume<IS>,
	) -> Self {
		Self { aggressive_volume }
	}

	pub fn new_from_parts(
		directionless_volume: DirectionlessVolume<IS>,
		aggressor_side: AggressorSide,
	) -> Self {
		Self {
			aggressive_volume: AggressiveVolume {
				directionless_volume,
				aggressor_side,
			},
		}
	}

	pub fn into_remote(
		self,
	) -> RemoteOrder<IS> {
		self.into()
	}
}

impl<IS: InstrumentSpec> DesiredVolumeOrder<IS> for MarketOrder<IS> {
	fn desired_directional_intent_volume(
		&self,
	) -> Result<DirectionalIntentVolume<IS>, DesiredVolumeOrderError> {
		Ok(self.aggressive_volume.as_directional_intent_volume())
	}

	fn desired_directionless_volume(
		&self,
	) -> Result<&DirectionlessVolume<IS>, DesiredVolumeOrderError> {
		Ok(self.aggressive_volume.as_directionless_volume())
	}
}

impl<IS: InstrumentSpec> MatchableOrder<IS> for MarketOrder<IS> {
	fn is_liquidable(
		&self,
		liquidity_estimation: &LiquidityEstimation<IS>,
	) -> Option<AbsolutePrice<IS>> {
		let Some((
			bid_level,
			ask_level,
		)) = liquidity_estimation.furthest_bid_ask() else {
			return None;
		};

		match self.aggressive_volume.into_aggressor_side() {
			AggressorSide::Bid => Some(bid_level.price),
			AggressorSide::Ask => Some(ask_level.price),
		}
	}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LimitOrder<IS: InstrumentSpec> {
	pub price: ProtoabsolutePrice<IS>,
	pub resting_volume: RestingVolume<IS>,
	pub order_execution_expectation: OrderExecutionExpectation,
}

impl<IS: InstrumentSpec> LimitOrder<IS> {
	pub fn new(
		price: ProtoabsolutePrice<IS>,
		resting_volume: RestingVolume<IS>,
		order_execution_expectation: OrderExecutionExpectation,
	) -> Self {
		Self {
			price,
			resting_volume,
			order_execution_expectation,
		}
	}

	pub(crate) fn parent_processed(
		&mut self,
		bid_ask_price_spread: &BidAskPriceSpread<IS>,
	) {
		let ProtoabsolutePrice::Relative(
			relative_price,
		) = &self.price else {
			return;
		};

		let absolute_price = if relative_price.is_positive() {
			bid_ask_price_spread.ask_price + relative_price
		} else {
			bid_ask_price_spread.bid_price + relative_price
		};

		self.price = absolute_price.into_protoabsolute_price();
	}

	pub fn into_remote(
		self,
	) -> RemoteOrder<IS> {
		self.into()
	}
}

impl<IS: InstrumentSpec> DesiredVolumeOrder<IS> for LimitOrder<IS> {
	fn desired_directional_intent_volume(
		&self,
	) -> Result<DirectionalIntentVolume<IS>, DesiredVolumeOrderError> {
		Ok(self.resting_volume.as_directional_intent_volume())
	}

	fn desired_directionless_volume(
		&self,
	) -> Result<&DirectionlessVolume<IS>, DesiredVolumeOrderError> {
		Ok(self.resting_volume.as_directionless_volume())
	}
}

impl<IS: InstrumentSpec> MatchableOrder<IS> for LimitOrder<IS> {
	fn is_liquidable(
		&self,
		liquidity_estimation: &LiquidityEstimation<IS>,
	) -> Option<AbsolutePrice<IS>> {
		let Some((
			bid_level,
			ask_level,
		)) = liquidity_estimation.furthest_bid_ask() else {
			return None;
		};

		match self.resting_volume.resting_side {
			RestingSide::Bid => {
				let ProtoabsolutePrice::Absolute(
					absolute_price,
				) = self.price else {
					return None;
				};

				if bid_level.price <= absolute_price {
					Some(bid_level.price)
				} else {
					None
				}
			},
			RestingSide::Ask => {
				let ProtoabsolutePrice::Absolute(
					absolute_price,
				) = self.price else {
					return None;
				};

				if ask_level.price >= absolute_price {
					Some(ask_level.price)
				} else {
					None
				}
			},
		}
	}
}