apple-quant-algorithmic 0.3.0

Apple Quant's algorithmic library.
use std::borrow::Borrow;
use std::cmp;

use apple_quant_core::{
	log::error, AddChecked, AddReleaseUnchecked, AddUnchecked, SubChecked,
	SubReleaseUnchecked, SubUnchecked,
};

use num_traits::{Signed, Zero};

use crate::{
	instrument::{AsDecimal, InstrumentSpec},
	points::{Subpoints, WholePoints},
};

use super::{
	AggressiveVolume, DirectionalIntent, DirectionlessVolume, FromVolumeType,
	FromVolumeTypeError, RestingVolume, VolumeConstruct, ZeroableVolume,
};

#[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 from_volume_type_positive_unchecked(
		volume_type: IS::VolumeType,
	) -> Self {
		debug_assert!(volume_type.is_positive() || volume_type.is_zero());

		Self {
			directional_intent: DirectionalIntent::Positive,
			directionless_volume: DirectionlessVolume::<IS>::new_unchecked(volume_type),
		}
	}

	pub fn from_volume_type_negative_unchecked(
		volume_type: IS::VolumeType,
	) -> Self {
		debug_assert!(volume_type.is_positive() || volume_type.is_zero());

		Self {
			directional_intent: DirectionalIntent::Negative,
			directionless_volume: DirectionlessVolume::<IS>::new_unchecked(volume_type),
		}
	}

	pub fn as_flipped(
		&self,
	) -> Self {
		Self {
			directional_intent: self.directional_intent.as_flipped(),
			directionless_volume: self.directionless_volume,
		}
	}

	pub fn as_volume_type_signed(
		&self,
	) -> IS::VolumeType {
		let volume_type = self.directionless_volume.as_volume_type();
		debug_assert!(volume_type.is_positive() || volume_type.is_zero());

		if self.directional_intent.is_positive() {
			return volume_type;
		} else {
			return -volume_type;
		}
	}
}

impl<IS: InstrumentSpec> VolumeConstruct<
	(
		DirectionalIntent,
		DirectionlessVolume<IS>,
	),
	(),
> for DirectionalIntentVolume<IS> {
	fn new_checked(
		t: impl Into<(
			DirectionalIntent,
			DirectionlessVolume<IS>,
		)>,
	) -> Result<Self, ()>
	where
		Self: Sized,
	{
		Ok(Self::new_unchecked(t))
	}

	fn new_unchecked(
		t: impl Into<(
			DirectionalIntent,
			DirectionlessVolume<IS>,
		)>,
	) -> Self {
		let (
			directional_intent,
			directionless_volume,
		) = t.into();

		Self {
			directional_intent,
			directionless_volume,
		}
	}
}

impl<IS: InstrumentSpec> FromVolumeType<IS> for DirectionalIntentVolume<IS> {
	fn from_volume_type_checked(
		volume_type: impl Into<IS::VolumeType>,
	) -> Result<Self, FromVolumeTypeError>
	where
		Self: Sized,
	{
		let volume_type = volume_type.into();

		if volume_type.is_zero() {
			return Err(FromVolumeTypeError::new(volume_type.as_decimal()));
		}

		Ok(Self::from_volume_type_unchecked(volume_type))
	}

	fn from_volume_type_unchecked(
		volume_type: impl Into<IS::VolumeType>,
	) -> Self {
		let volume_type = volume_type.into();

		if volume_type.is_positive() {
			return Self::from_volume_type_positive_unchecked(volume_type);
		}

		Self::from_volume_type_negative_unchecked(volume_type.abs())
	}
}

impl<IS: InstrumentSpec> ZeroableVolume for DirectionalIntentVolume<IS> {
	const ZERO: Self = DirectionalIntentVolume {
		directional_intent: DirectionalIntent::Positive,
		directionless_volume: DirectionlessVolume::ZERO,
	};

	fn is_zero(
		&self,
	) -> bool {
		self.directionless_volume == DirectionlessVolume::ZERO
	}
}

impl<IS: InstrumentSpec> From<AggressiveVolume<IS>> for DirectionalIntentVolume<IS> {
	fn from(
		value: AggressiveVolume<IS>,
	) -> Self {
		let directional_intent = value.as_aggressor_side().into();
		let directionless_volume = value.into_directionless_volume();

		Self {
			directional_intent,
			directionless_volume,
		}
	}
}

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

impl<IS: InstrumentSpec> From<RestingVolume<IS>> for DirectionalIntentVolume<IS> {
	fn from(
		value: RestingVolume<IS>,
	) -> Self {
		let directional_intent = value.as_resting_side().into();
		let directionless_volume = value.into_directionless_volume();

		Self {
			directional_intent,
			directionless_volume,
		}
	}
}

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

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 {
		let lhs = self.as_volume_type_signed();
		let rhs = other.as_volume_type_signed();

		lhs.cmp(&rhs)
	}
}

impl<IS: InstrumentSpec> From<WholePoints> for DirectionalIntentVolume<IS> {
	fn from(
		value: WholePoints,
	) -> Self {
		match Self::from_volume_type_unchecked_release(
			value.into_subpoints::<IS::VolumeSpec>(),
		) {
			Ok(
				directional_intent_volume,
			) => directional_intent_volume,
			Err(e) => {
				error!("{e}");
				panic!();
			},
		}
	}
}

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

impl<IS: InstrumentSpec> From<Subpoints> for DirectionalIntentVolume<IS> {
	fn from(
		value: Subpoints,
	) -> Self {
		match Self::from_volume_type_unchecked_release(value) {
			Ok(
				directional_intent_volume,
			) => directional_intent_volume,
			Err(e) => {
				error!("{e}");
				panic!();
			},
		}
	}
}

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

impl<IS: InstrumentSpec> From<DirectionalIntentVolume<IS>> for Result<
	DirectionalIntentVolume<IS>,
	FromVolumeTypeError,
> {
	fn from(
		value: DirectionalIntentVolume<IS>,
	) -> Self {
		Ok(value)
	}
}

impl_volume_binop!(
	DirectionalIntentVolume,
	AddReleaseUnchecked,
	AddChecked,
	add_checked,
	AddUnchecked,
	add_unchecked,
	|lhs: DirectionalIntentVolume<IS>, rhs: DirectionalIntentVolume<IS>| { let lhs =
	lhs.as_volume_type_signed(); let rhs = rhs.as_volume_type_signed(); let volume_type =
	lhs + rhs; DirectionalIntentVolume::<IS>::from_volume_type_checked(volume_type) },
	|lhs: DirectionalIntentVolume<IS>, rhs: DirectionalIntentVolume<IS>| { let lhs =
	lhs.as_volume_type_signed(); let rhs = rhs.as_volume_type_signed(); let volume_type =
	lhs + rhs; DirectionalIntentVolume::<IS>::from_volume_type_unchecked(volume_type) },
	FromVolumeTypeError,
	IS,
	InstrumentSpec
);

impl_volume_binop!(
	DirectionalIntentVolume,
	SubReleaseUnchecked,
	SubChecked,
	sub_checked,
	SubUnchecked,
	sub_unchecked,
	|lhs: DirectionalIntentVolume<IS>, rhs: DirectionalIntentVolume<IS>| { let lhs =
	lhs.as_volume_type_signed(); let rhs = rhs.as_volume_type_signed(); let volume_type =
	lhs - rhs; DirectionalIntentVolume::<IS>::from_volume_type_checked(volume_type) },
	|lhs: DirectionalIntentVolume<IS>, rhs: DirectionalIntentVolume<IS>| { let lhs =
	lhs.as_volume_type_signed(); let rhs = rhs.as_volume_type_signed(); let volume_type =
	lhs - rhs; DirectionalIntentVolume::<IS>::from_volume_type_unchecked(volume_type) },
	FromVolumeTypeError,
	IS,
	InstrumentSpec
);