apple-quant-algorithmic 0.1.0

Apple Quant's algorithmic trading api
Documentation
use num_traits::Signed;

use crate::{
	backend::OrderIdGenerator,
	instrument::InstrumentSpec,
	liquidity::LiquidityEstimation,
	price::{AbsolutePrice, BidAskPriceSpread, Price},
	volume::DirectionalIntent,
};

use super::{ClientOrderTracker, MatchableOrder};

#[derive(Debug, Clone, 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, PartialEq, Eq, Hash)]
pub struct ClientStopOrder<IS: InstrumentSpec> {
	pub price: Price<IS>,
	pub cross_over_directional_intent: DirectionalIntent,
}

impl<IS: InstrumentSpec> ClientStopOrder<IS> {
	pub fn new(
		price: Price<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 Price::Relative(relative_price) = &mut self.price else {
			return;
		};

		let absolute_price: AbsolutePrice<IS> = if relative_price.is_positive() {
			AbsolutePrice::new(*bid_ask_price_spread.ask_price + **relative_price)
		} else {
			AbsolutePrice::new(*bid_ask_price_spread.bid_price + **relative_price)
		};

		self.price = Price::Absolute(absolute_price);
	}
}

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 Price::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)
			}
		}
	}
}