apple-quant-algorithmic 0.3.0

Apple Quant's algorithmic library.
#[allow(unused_imports)]
use apple_quant_core::log::trace;

use apple_quant_core::log::{error, warn, CtxExt};

use tracing::instrument;

use crate::{
	backend::{LocalOrderId, OrderIdGenerator, OrdersBackend},
	order::{OrderDependency, RemoteOrderBuilder, StateGoal, TriggerError},
	order_manager::{OrderManager, OrdersCapacitySpec, PendingClientCancelError},
	volume::{AggressorSide, DirectionalExposure, DirectionalIntent, ZeroableExt},
	instrument::InstrumentSpec, points::Subpoints, timestamp::TickTimestamp,
};

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CancelClientOrder {
	pub local_order_id: LocalOrderId,
	pub attempt_exposure_reversion: bool,
}

impl CancelClientOrder {
	pub fn new(
		local_order_id: LocalOrderId,
		attempt_exposure_reversion: bool,
	) -> Self {
		Self {
			local_order_id,
			attempt_exposure_reversion,
		}
	}
}

impl<IS: InstrumentSpec> OrderDependency<IS> for CancelClientOrder {
	// Includes the current implementation of exposure reversion. This is not accurate in all cases
	// and should only be used for simple bracket order strategies.
	#[instrument(skip_all)]
	async fn trigger<
		OB: OrdersBackend<IS>,
		CS: OrdersCapacitySpec,
	>(
		&self,
		tick_timestamp: &TickTimestamp,
		order_manager: &mut OrderManager<IS, CS>,
		orders_backend: &mut OB,
		order_id_generator: &mut OrderIdGenerator,
		directional_exposure: &mut DirectionalExposure<IS>,
		state_goal: &mut StateGoal,
	) -> Result<bool, TriggerError>
	where
		IS: Send,
	{
		#[cfg(feature = "log-trace-order-actions")]
		trace!("Triggered order action: `{self:#?}`.");

		match order_manager.pending_client_orders.remove(
			directional_exposure,
			&self.local_order_id,
		) {
			Ok(_) => return Ok(false),
			Err(
				error,
			) => {
				if !self.attempt_exposure_reversion {
					return Err(error.into());
				}

				match error {
					PendingClientCancelError::OrderId(_) => {},
					_ => return Err(error.into()),
				}
			},
		}

		let Some(
			effective_directional_intent_volume,
		) = directional_exposure.combined().into_optional_nonzero() else {
			warn!("Exposure reversion trigger failed with 0 combined directional exposure.");

			return Ok(false);
		};

		// Market aggressive against the current effective exposure.
		let aggressor_side = match effective_directional_intent_volume.directional_intent {
			DirectionalIntent::Positive => AggressorSide::Bid,
			DirectionalIntent::Negative => AggressorSide::Ask,
		};

		let directionless_volume = effective_directional_intent_volume.directionless_volume;

		warn!(
			"Exposure reversion triggered. Submitting detached market `{:?}` with `{:?}`.",
			aggressor_side,
			directionless_volume
		);

		let order = match RemoteOrderBuilder::<IS>::new_market_parts(
			order_id_generator,
			aggressor_side,
			directionless_volume,
		) {
			Ok(
				remote_order_builder,
			) => remote_order_builder,
			error_result => {
				error!(
					"{:?}",
					error_result.ctx("While constructing exposure reversion market order.")
				);

				return Ok(false);
			},
		};

		let order_action = order.into_condensed().into_order_action::<4>().0;

		Box::pin(order_action.trigger(
			tick_timestamp,
			order_manager,
			orders_backend,
			order_id_generator,
			directional_exposure,
			state_goal,
		)).await
	}
}