apple-quant-algorithmic 0.3.0

Apple Quant's algorithmic library.
use crate::{
	price::{AbsolutePrice, BidAskPriceSpread, ProtoabsolutePrice},
	backend::OrderIdGenerator, instrument::InstrumentSpec,
	liquidity::LiquidityEstimation, volume::DirectionalIntent,
};

use super::{ClientOrderTracker, MatchableOrder};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClientOrder<IS: InstrumentSpec> {
	Stop(ClientStopOrder<IS>),
}

impl<IS: InstrumentSpec> ClientOrder<IS> {
	pub(crate) fn parent_processed(
		&mut self,
		bid_ask_price_spread: &BidAskPriceSpread<IS>,
	) {
		#[allow(irrefutable_let_patterns)]
		let Self::Stop(
			client_stop_order,
		) = self else {
			return;
		};

		client_stop_order.parent_processed(bid_ask_price_spread);
	}

	pub fn register(
		&self,
		order_id_generator: &mut OrderIdGenerator,
	) -> ClientOrderTracker
	where
		IS: Send,
	{
		let local_order_id = order_id_generator.next_local_order_id();
		ClientOrderTracker::new(local_order_id)
	}
}

impl<IS: InstrumentSpec> MatchableOrder<IS> for ClientOrder<IS> {
	fn is_liquidable(
		&self,
		liquidity_estimation: &LiquidityEstimation<IS>,
	) -> Option<AbsolutePrice<IS>> {
		match self {
			Self::Stop(
				client_stop_order,
			) => client_stop_order.is_liquidable(liquidity_estimation),
		}
	}
}

impl<IS: InstrumentSpec> From<ClientStopOrder<IS>> for ClientOrder<IS> {
	fn from(
		value: ClientStopOrder<IS>,
	) -> Self {
		Self::Stop(value)
	}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClientStopOrder<IS: InstrumentSpec> {
	pub price: ProtoabsolutePrice<IS>,
	pub cross_over_directional_intent: DirectionalIntent,
}

impl<IS: InstrumentSpec> ClientStopOrder<IS> {
	pub fn new(
		price: ProtoabsolutePrice<IS>,
		cross_over_directional_intent: DirectionalIntent,
	) -> Self {
		Self {
			price,
			cross_over_directional_intent,
		}
	}

	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_client_order(
		self,
	) -> ClientOrder<IS> {
		self.into()
	}
}

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

		let ProtoabsolutePrice::Absolute(
			absolute_price,
		) = &self.price else {
			return None;
		};

		match self.cross_over_directional_intent {
			DirectionalIntent::Positive => {
				if &ask_price_volume_level.price < absolute_price {
					return None;
				}

				Some(ask_price_volume_level.price)
			},
			DirectionalIntent::Negative => {
				if &bid_price_volume_level.price > absolute_price {
					return None;
				}

				Some(bid_price_volume_level.price)
			},
		}
	}
}