apple_quant_algorithmic/volume/
directionless.rs1use std::borrow::Borrow;
2use std::ops::Deref;
3
4use apple_quant_core::{
5 AddChecked, AddReleaseUnchecked, AddUnchecked, SubChecked, SubReleaseUnchecked,
6 SubUnchecked,
7};
8
9use num_traits::{Signed, Zero};
10
11use rust_decimal::Decimal;
12use thiserror::Error;
13use tracing::instrument;
14
15use crate::{
16 instrument::{AsDecimal, InstrumentSpec},
17 points::{Subpoints, WholePoints},
18 volume::{VolumeType, ZeroableVolume},
19};
20
21use super::{
22 DirectionalIntent, DirectionalIntentVolume, FromVolumeType, FromVolumeTypeError,
23 MatchReduceResult, VolumeConstruct,
24};
25
26#[derive(Debug, Error)]
27pub enum DirectionlessVolumeInvalidError {
28 #[error("Invalid negative value: {0}.")]
29 Negative(Decimal),
30
31 #[error("Invalid zero value.")]
32 Zero,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct DirectionlessVolume<IS: InstrumentSpec>(IS::VolumeType);
37
38impl<IS: InstrumentSpec> DirectionlessVolume<IS> {
39 pub fn as_volume_type(
40 &self,
41 ) -> IS::VolumeType {
42 debug_assert!(self.0.is_positive() || self.0.is_zero());
43 self.0
44 }
45
46 #[instrument(skip_all)]
47 pub fn match_reduce(
48 self,
49 matcher: &Self,
50 ) -> MatchReduceResult<Self> {
51 let self_volume_type = self.as_volume_type();
52 let matcher_volume_type = matcher.as_volume_type();
53 let self_delta = self_volume_type - matcher_volume_type;
54
55 if self_delta.is_zero() {
56 return MatchReduceResult {
57 new_matched: None,
58 new_matcher: None,
59 matched_quantity: self_volume_type.into(),
60 };
61 }
62
63 if self_delta.is_positive() {
64 return MatchReduceResult {
65 new_matched: Some(Self::new_unchecked_release(self_delta)),
66 new_matcher: None,
67 matched_quantity: matcher_volume_type.into(),
68 };
69 }
70
71 let new_matcher = matcher_volume_type - self_volume_type;
73 let new_matcher = Self::new_unchecked_release(new_matcher);
74
75 MatchReduceResult {
76 new_matched: None,
77 new_matcher: Some(new_matcher),
78 matched_quantity: self_volume_type.into(),
79 }
80 }
81
82 pub fn with_directional_intent(
83 self,
84 directional_intent: DirectionalIntent,
85 ) -> DirectionalIntentVolume<IS> {
86 DirectionalIntentVolume::new_unchecked_release((directional_intent, self))
87 }
88}
89
90impl<IS: InstrumentSpec> VolumeConstruct<
91 IS::VolumeType,
92 DirectionlessVolumeInvalidError,
93> for DirectionlessVolume<IS> {
94 fn new_checked(
95 t: impl Into<IS::VolumeType>,
96 ) -> Result<Self, DirectionlessVolumeInvalidError>
97 where
98 Self: Sized,
99 {
100 let t = t.into();
101
102 if t.is_zero() {
103 return Err(DirectionlessVolumeInvalidError::Zero);
104 }
105
106 if t.is_negative() {
107 return Err(DirectionlessVolumeInvalidError::Negative(t.as_decimal()));
108 }
109
110 Ok(Self::new_unchecked(t))
111 }
112
113 fn new_unchecked(
114 t: impl Into<IS::VolumeType>,
115 ) -> Self {
116 let volume_type = t.into();
117 debug_assert!(volume_type.is_positive() || volume_type.is_zero());
118 Self(volume_type)
119 }
120}
121
122impl<IS: InstrumentSpec> FromVolumeType<IS> for DirectionlessVolume<IS> {
123 fn from_volume_type_checked(
124 volume_type: impl Into<IS::VolumeType>,
125 ) -> Result<Self, FromVolumeTypeError>
126 where
127 Self: Sized,
128 {
129 let volume_type = volume_type.into();
130
131 Self::new_checked(volume_type).map_err(|_| {
132 FromVolumeTypeError::new(volume_type.as_decimal())
133 })
134 }
135
136 fn from_volume_type_unchecked(
137 volume_type: impl Into<IS::VolumeType>,
138 ) -> Self {
139 Self::new_unchecked(volume_type)
140 }
141}
142
143impl<IS: InstrumentSpec> ZeroableVolume for DirectionlessVolume<IS> {
144 const ZERO: Self = Self(IS::VolumeType::ZERO);
145
146 fn is_zero(
147 &self,
148 ) -> bool {
149 *self == Self::ZERO
150 }
151}
152
153impl<IS: InstrumentSpec> Deref for DirectionlessVolume<IS> {
154 type Target = IS::VolumeType;
155
156 fn deref(
157 &self,
158 ) -> &Self::Target {
159 &self.0
160 }
161}
162
163impl<IS: InstrumentSpec> From<WholePoints> for DirectionlessVolume<IS> {
164 fn from(
165 value: WholePoints,
166 ) -> Self {
167 Self::new_unchecked_release(value.into_subpoints::<IS::VolumeSpec>())
168 }
169}
170
171impl<IS: InstrumentSpec> From<&WholePoints> for DirectionlessVolume<IS> {
172 fn from(
173 value: &WholePoints,
174 ) -> Self {
175 (*value).into()
176 }
177}
178
179impl<IS: InstrumentSpec> From<Subpoints> for DirectionlessVolume<IS> {
180 fn from(
181 value: Subpoints,
182 ) -> Self {
183 Self::new_unchecked_release(value)
184 }
185}
186
187impl<IS: InstrumentSpec> From<&Subpoints> for DirectionlessVolume<IS> {
188 fn from(
189 value: &Subpoints,
190 ) -> Self {
191 (*value).into()
192 }
193}
194
195impl<IS: InstrumentSpec> From<DirectionlessVolume<IS>> for Result<
196 DirectionlessVolume<IS>,
197 FromVolumeTypeError,
198> {
199 fn from(
200 value: DirectionlessVolume<IS>,
201 ) -> Self {
202 Ok(value)
203 }
204}
205
206impl_volume_binop!(
207 DirectionlessVolume,
208 AddReleaseUnchecked,
209 AddChecked,
210 add_checked,
211 AddUnchecked,
212 add_unchecked,
213 |lhs: DirectionlessVolume<IS>, rhs: DirectionlessVolume<IS>| { let volume_type =
214 lhs.0 + rhs.0; DirectionlessVolume::<IS>::from_volume_type_checked(volume_type) },
215 |lhs: DirectionlessVolume<IS>, rhs: DirectionlessVolume<IS>| { let volume_type =
216 lhs.0 + rhs.0; DirectionlessVolume::<IS>::from_volume_type_unchecked(volume_type) },
217 FromVolumeTypeError,
218 IS,
219 InstrumentSpec
220);
221
222impl_volume_binop!(
223 DirectionlessVolume,
224 SubReleaseUnchecked,
225 SubChecked,
226 sub_checked,
227 SubUnchecked,
228 sub_unchecked,
229 |lhs: DirectionlessVolume<IS>, rhs: DirectionlessVolume<IS>| { let volume_type =
230 lhs.0 - rhs.0; DirectionlessVolume::<IS>::from_volume_type_checked(volume_type) },
231 |lhs: DirectionlessVolume<IS>, rhs: DirectionlessVolume<IS>| { let volume_type =
232 lhs.0 - rhs.0; DirectionlessVolume::<IS>::from_volume_type_unchecked(volume_type) },
233 FromVolumeTypeError,
234 IS,
235 InstrumentSpec
236);