apple-quant-algorithmic 0.1.0

Apple Quant's algorithmic trading api
Documentation
use std::{
	cmp,
	ops::{Add, AddAssign, Sub, SubAssign},
};

use crate::{UnwrapOptionExt, UnwrapResultExt, instrument::InstrumentSpec};

use super::{AggressiveVolume, DirectionalIntent, DirectionlessVolume, RestingVolume, VolumeDelta};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DirectionalIntentVolume<IS: InstrumentSpec> {
	pub directional_intent: DirectionalIntent,
	pub directionless_volume: DirectionlessVolume<IS>,
}

impl<IS: InstrumentSpec> DirectionalIntentVolume<IS> {
	pub fn as_flipped(&self) -> Self {
		Self {
			directional_intent: self
				.directional_intent
				.as_flipped(),
			directionless_volume: self.directionless_volume,
		}
	}

	pub fn as_zeroable(&self) -> ZeroableDirectionalIntentVolume<IS> {
		self.into()
	}
}

impl<IS: InstrumentSpec> Add for DirectionalIntentVolume<IS> {
	type Output = Option<Self>;

	fn add(
		mut self,
		mut rhs: Self,
	) -> Self::Output {
		if self.directional_intent == rhs.directional_intent {
			self.directionless_volume += rhs.directionless_volume;
			return Some(self);
		}

		// Cancels each other out.
		if self.directionless_volume == rhs.directionless_volume {
			return None;
		}

		// Dominate directional intent gets returned
		// with delta in their direction.
		if self.directionless_volume > rhs.directionless_volume {
			// Safe because of if statements above.
			let directionless_volume = (self.directionless_volume - rhs.directionless_volume)
				.unwrap_release_unsafe()
				.unwrap_release_unsafe();

			self.directionless_volume = directionless_volume;
			return Some(self);
		} else {
			// Safe because of if statements above.
			let directionless_volume = (rhs.directionless_volume - self.directionless_volume)
				.unwrap_release_unsafe()
				.unwrap_release_unsafe();

			rhs.directionless_volume = directionless_volume;
			return Some(rhs);
		}
	}
}

impl<IS: InstrumentSpec> Sub for DirectionalIntentVolume<IS> {
	type Output = Option<Self>;

	fn sub(
		self,
		mut rhs: Self,
	) -> Self::Output {
		rhs.directional_intent.flip();
		self + rhs
	}
}

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

impl<IS: InstrumentSpec> From<&AggressiveVolume<IS>> for DirectionalIntentVolume<IS> {
	fn from(value: &AggressiveVolume<IS>) -> Self {
		Self {
			directional_intent: value.aggressor_side().into(),
			directionless_volume: *value.as_directionless_volume(),
		}
	}
}

impl<IS: InstrumentSpec> From<RestingVolume<IS>> for DirectionalIntentVolume<IS> {
	fn from(value: RestingVolume<IS>) -> Self {
		Self {
			directional_intent: value.resting_side().into(),
			directionless_volume: *value.as_directionless_volume(),
		}
	}
}

impl<IS: InstrumentSpec> From<VolumeDelta<IS>> for Option<DirectionalIntentVolume<IS>> {
	fn from(value: VolumeDelta<IS>) -> Self {
		match value {
			VolumeDelta::Zero => None,
			VolumeDelta::AggressiveDelta {
				directionless_volume,
				aggressor_side,
			} => Some(DirectionalIntentVolume {
				directional_intent: aggressor_side.as_directional_intent(),
				directionless_volume,
			}),
			VolumeDelta::RestingDelta {
				directionless_volume,
				resting_side,
			} => Some(DirectionalIntentVolume {
				directional_intent: resting_side.as_directional_intent(),
				directionless_volume,
			}),
		}
	}
}

impl<IS: InstrumentSpec> PartialOrd for DirectionalIntentVolume<IS> {
	fn partial_cmp(
		&self,
		other: &Self,
	) -> Option<cmp::Ordering> {
		Some(self.cmp(other))
	}
}

impl<IS: InstrumentSpec> Ord for DirectionalIntentVolume<IS> {
	fn cmp(
		&self,
		other: &Self,
	) -> cmp::Ordering {
		match (
			self.directional_intent,
			other.directional_intent,
		) {
			(DirectionalIntent::Positive, DirectionalIntent::Negative) => cmp::Ordering::Greater,
			(DirectionalIntent::Negative, DirectionalIntent::Positive) => cmp::Ordering::Less,
			_ => {
				if self.directionless_volume == other.directionless_volume {
					cmp::Ordering::Equal
				} else if self.directionless_volume > other.directionless_volume {
					cmp::Ordering::Greater
				} else {
					cmp::Ordering::Less
				}
			}
		}
	}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ZeroableDirectionalIntentVolume<IS: InstrumentSpec>(Option<DirectionalIntentVolume<IS>>);

impl<IS: InstrumentSpec> ZeroableDirectionalIntentVolume<IS> {
	pub const ZERO: Self = Self(None);

	pub fn new(directional_intent_volume: Option<DirectionalIntentVolume<IS>>) -> Self {
		Self(directional_intent_volume)
	}

	pub fn as_flipped(&self) -> Self {
		let Some(directional_intent_volume) = &self.0 else {
			return Self(None);
		};

		Self(Some(
			directional_intent_volume.as_flipped(),
		))
	}

	pub fn directional_intent_volume(&self) -> &Option<DirectionalIntentVolume<IS>> {
		&self.0
	}
}

impl<IS: InstrumentSpec> From<DirectionalIntentVolume<IS>> for ZeroableDirectionalIntentVolume<IS> {
	fn from(value: DirectionalIntentVolume<IS>) -> Self {
		Self(Some(value))
	}
}

impl<IS: InstrumentSpec> From<&DirectionalIntentVolume<IS>>
	for ZeroableDirectionalIntentVolume<IS>
{
	fn from(value: &DirectionalIntentVolume<IS>) -> Self {
		Self(Some(*value))
	}
}

impl<IS: InstrumentSpec> From<Option<DirectionalIntentVolume<IS>>>
	for ZeroableDirectionalIntentVolume<IS>
{
	fn from(value: Option<DirectionalIntentVolume<IS>>) -> Self {
		Self(value)
	}
}

impl<IS: InstrumentSpec> From<&Option<DirectionalIntentVolume<IS>>>
	for ZeroableDirectionalIntentVolume<IS>
{
	fn from(value: &Option<DirectionalIntentVolume<IS>>) -> Self {
		Self(*value)
	}
}

impl<IS: InstrumentSpec> Add for ZeroableDirectionalIntentVolume<IS> {
	type Output = Self;

	fn add(
		self,
		rhs: Self,
	) -> Self::Output {
		match (self.0, rhs.0) {
			(Some(self_directional_intent_volume), Some(rhs_directional_intent_volume)) => {
				(self_directional_intent_volume + rhs_directional_intent_volume).into()
			}
			(Some(self_directional_intent_volume), None) => {
				Some(self_directional_intent_volume).into()
			}
			(None, Some(rhs_directional_intent_volume)) => {
				Some(rhs_directional_intent_volume).into()
			}
			(None, None) => None.into(),
		}
	}
}

impl<IS: InstrumentSpec> Sub for ZeroableDirectionalIntentVolume<IS> {
	type Output = Self;

	fn sub(
		self,
		mut rhs: Self,
	) -> Self::Output {
		rhs = rhs.as_flipped();
		self + rhs
	}
}

impl<IS: InstrumentSpec> AddAssign for ZeroableDirectionalIntentVolume<IS> {
	fn add_assign(
		&mut self,
		rhs: Self,
	) {
		*self = *self + rhs
	}
}

impl<IS: InstrumentSpec> SubAssign for ZeroableDirectionalIntentVolume<IS> {
	fn sub_assign(
		&mut self,
		rhs: Self,
	) {
		*self = *self - rhs;
	}
}

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

impl<IS: InstrumentSpec> PartialOrd for ZeroableDirectionalIntentVolume<IS> {
	fn partial_cmp(
		&self,
		other: &Self,
	) -> Option<cmp::Ordering> {
		Some(self.cmp(other))
	}
}

impl<IS: InstrumentSpec> Ord for ZeroableDirectionalIntentVolume<IS> {
	fn cmp(
		&self,
		other: &Self,
	) -> cmp::Ordering {
		match (self, other) {
			(Self(None), Self(None)) => cmp::Ordering::Equal,
			(Self(None), Self(Some(other_directional_intent_volume))) => {
				if other_directional_intent_volume.directional_intent == DirectionalIntent::Positive
				{
					cmp::Ordering::Less
				} else {
					cmp::Ordering::Greater
				}
			}
			(Self(Some(self_directional_intent_volume)), Self(None)) => {
				if self_directional_intent_volume.directional_intent == DirectionalIntent::Positive
				{
					cmp::Ordering::Greater
				} else {
					cmp::Ordering::Less
				}
			}
			(
				Self(Some(self_directional_intent_volume)),
				Self(Some(other_directional_intent_volume)),
			) => self_directional_intent_volume.cmp(other_directional_intent_volume),
		}
	}
}