use std::borrow::Borrow;
use std::ops::Deref;
use apple_quant_core::{
AddChecked, AddReleaseUnchecked, AddUnchecked, SubChecked, SubReleaseUnchecked,
SubUnchecked,
};
use num_traits::{Signed, Zero};
use rust_decimal::Decimal;
use thiserror::Error;
use tracing::instrument;
use crate::{
instrument::{AsDecimal, InstrumentSpec},
points::{Subpoints, WholePoints},
volume::{VolumeType, ZeroableVolume},
};
use super::{
DirectionalIntent, DirectionalIntentVolume, FromVolumeType, FromVolumeTypeError,
MatchReduceResult, VolumeConstruct,
};
#[derive(Debug, Error)]
pub enum DirectionlessVolumeInvalidError {
#[error("Invalid negative value: {0}.")]
Negative(Decimal),
#[error("Invalid zero value.")]
Zero,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DirectionlessVolume<IS: InstrumentSpec>(IS::VolumeType);
impl<IS: InstrumentSpec> DirectionlessVolume<IS> {
pub fn as_volume_type(
&self,
) -> IS::VolumeType {
debug_assert!(self.0.is_positive() || self.0.is_zero());
self.0
}
#[instrument(skip_all)]
pub fn match_reduce(
self,
matcher: &Self,
) -> MatchReduceResult<Self> {
let self_volume_type = self.as_volume_type();
let matcher_volume_type = matcher.as_volume_type();
let self_delta = self_volume_type - matcher_volume_type;
if self_delta.is_zero() {
return MatchReduceResult {
new_matched: None,
new_matcher: None,
matched_quantity: self_volume_type.into(),
};
}
if self_delta.is_positive() {
return MatchReduceResult {
new_matched: Some(Self::new_unchecked_release(self_delta)),
new_matcher: None,
matched_quantity: matcher_volume_type.into(),
};
}
let new_matcher = matcher_volume_type - self_volume_type;
let new_matcher = Self::new_unchecked_release(new_matcher);
MatchReduceResult {
new_matched: None,
new_matcher: Some(new_matcher),
matched_quantity: self_volume_type.into(),
}
}
pub fn with_directional_intent(
self,
directional_intent: DirectionalIntent,
) -> DirectionalIntentVolume<IS> {
DirectionalIntentVolume::new_unchecked_release((directional_intent, self))
}
}
impl<IS: InstrumentSpec> VolumeConstruct<
IS::VolumeType,
DirectionlessVolumeInvalidError,
> for DirectionlessVolume<IS> {
fn new_checked(
t: impl Into<IS::VolumeType>,
) -> Result<Self, DirectionlessVolumeInvalidError>
where
Self: Sized,
{
let t = t.into();
if t.is_zero() {
return Err(DirectionlessVolumeInvalidError::Zero);
}
if t.is_negative() {
return Err(DirectionlessVolumeInvalidError::Negative(t.as_decimal()));
}
Ok(Self::new_unchecked(t))
}
fn new_unchecked(
t: impl Into<IS::VolumeType>,
) -> Self {
let volume_type = t.into();
debug_assert!(volume_type.is_positive() || volume_type.is_zero());
Self(volume_type)
}
}
impl<IS: InstrumentSpec> FromVolumeType<IS> for DirectionlessVolume<IS> {
fn from_volume_type_checked(
volume_type: impl Into<IS::VolumeType>,
) -> Result<Self, FromVolumeTypeError>
where
Self: Sized,
{
let volume_type = volume_type.into();
Self::new_checked(volume_type).map_err(|_| {
FromVolumeTypeError::new(volume_type.as_decimal())
})
}
fn from_volume_type_unchecked(
volume_type: impl Into<IS::VolumeType>,
) -> Self {
Self::new_unchecked(volume_type)
}
}
impl<IS: InstrumentSpec> ZeroableVolume for DirectionlessVolume<IS> {
const ZERO: Self = Self(IS::VolumeType::ZERO);
fn is_zero(
&self,
) -> bool {
*self == Self::ZERO
}
}
impl<IS: InstrumentSpec> Deref for DirectionlessVolume<IS> {
type Target = IS::VolumeType;
fn deref(
&self,
) -> &Self::Target {
&self.0
}
}
impl<IS: InstrumentSpec> From<WholePoints> for DirectionlessVolume<IS> {
fn from(
value: WholePoints,
) -> Self {
Self::new_unchecked_release(value.into_subpoints::<IS::VolumeSpec>())
}
}
impl<IS: InstrumentSpec> From<&WholePoints> for DirectionlessVolume<IS> {
fn from(
value: &WholePoints,
) -> Self {
(*value).into()
}
}
impl<IS: InstrumentSpec> From<Subpoints> for DirectionlessVolume<IS> {
fn from(
value: Subpoints,
) -> Self {
Self::new_unchecked_release(value)
}
}
impl<IS: InstrumentSpec> From<&Subpoints> for DirectionlessVolume<IS> {
fn from(
value: &Subpoints,
) -> Self {
(*value).into()
}
}
impl<IS: InstrumentSpec> From<DirectionlessVolume<IS>> for Result<
DirectionlessVolume<IS>,
FromVolumeTypeError,
> {
fn from(
value: DirectionlessVolume<IS>,
) -> Self {
Ok(value)
}
}
impl_volume_binop!(
DirectionlessVolume,
AddReleaseUnchecked,
AddChecked,
add_checked,
AddUnchecked,
add_unchecked,
|lhs: DirectionlessVolume<IS>, rhs: DirectionlessVolume<IS>| { let volume_type =
lhs.0 + rhs.0; DirectionlessVolume::<IS>::from_volume_type_checked(volume_type) },
|lhs: DirectionlessVolume<IS>, rhs: DirectionlessVolume<IS>| { let volume_type =
lhs.0 + rhs.0; DirectionlessVolume::<IS>::from_volume_type_unchecked(volume_type) },
FromVolumeTypeError,
IS,
InstrumentSpec
);
impl_volume_binop!(
DirectionlessVolume,
SubReleaseUnchecked,
SubChecked,
sub_checked,
SubUnchecked,
sub_unchecked,
|lhs: DirectionlessVolume<IS>, rhs: DirectionlessVolume<IS>| { let volume_type =
lhs.0 - rhs.0; DirectionlessVolume::<IS>::from_volume_type_checked(volume_type) },
|lhs: DirectionlessVolume<IS>, rhs: DirectionlessVolume<IS>| { let volume_type =
lhs.0 - rhs.0; DirectionlessVolume::<IS>::from_volume_type_unchecked(volume_type) },
FromVolumeTypeError,
IS,
InstrumentSpec
);