Skip to main content

apple_quant_algorithmic/volume/
directional_intent_volume.rs

1use std::borrow::Borrow;
2use std::cmp;
3
4use apple_quant_core::{
5	log::error, AddChecked, AddReleaseUnchecked, AddUnchecked, SubChecked,
6	SubReleaseUnchecked, SubUnchecked,
7};
8
9use num_traits::{Signed, Zero};
10
11use crate::{
12	instrument::{AsDecimal, InstrumentSpec},
13	points::{Subpoints, WholePoints},
14};
15
16use super::{
17	AggressiveVolume, DirectionalIntent, DirectionlessVolume, FromVolumeType,
18	FromVolumeTypeError, RestingVolume, VolumeConstruct, ZeroableVolume,
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct DirectionalIntentVolume<IS: InstrumentSpec> {
23	pub directional_intent: DirectionalIntent,
24	pub directionless_volume: DirectionlessVolume<IS>,
25}
26
27impl<IS: InstrumentSpec> DirectionalIntentVolume<IS> {
28	pub fn from_volume_type_positive_unchecked(
29		volume_type: IS::VolumeType,
30	) -> Self {
31		debug_assert!(volume_type.is_positive() || volume_type.is_zero());
32
33		Self {
34			directional_intent: DirectionalIntent::Positive,
35			directionless_volume: DirectionlessVolume::<IS>::new_unchecked(volume_type),
36		}
37	}
38
39	pub fn from_volume_type_negative_unchecked(
40		volume_type: IS::VolumeType,
41	) -> Self {
42		debug_assert!(volume_type.is_positive() || volume_type.is_zero());
43
44		Self {
45			directional_intent: DirectionalIntent::Negative,
46			directionless_volume: DirectionlessVolume::<IS>::new_unchecked(volume_type),
47		}
48	}
49
50	pub fn as_flipped(
51		&self,
52	) -> Self {
53		Self {
54			directional_intent: self.directional_intent.as_flipped(),
55			directionless_volume: self.directionless_volume,
56		}
57	}
58
59	pub fn as_volume_type_signed(
60		&self,
61	) -> IS::VolumeType {
62		let volume_type = self.directionless_volume.as_volume_type();
63		debug_assert!(volume_type.is_positive() || volume_type.is_zero());
64
65		if self.directional_intent.is_positive() {
66			return volume_type;
67		} else {
68			return -volume_type;
69		}
70	}
71}
72
73impl<IS: InstrumentSpec> VolumeConstruct<
74	(
75		DirectionalIntent,
76		DirectionlessVolume<IS>,
77	),
78	(),
79> for DirectionalIntentVolume<IS> {
80	fn new_checked(
81		t: impl Into<(
82			DirectionalIntent,
83			DirectionlessVolume<IS>,
84		)>,
85	) -> Result<Self, ()>
86	where
87		Self: Sized,
88	{
89		Ok(Self::new_unchecked(t))
90	}
91
92	fn new_unchecked(
93		t: impl Into<(
94			DirectionalIntent,
95			DirectionlessVolume<IS>,
96		)>,
97	) -> Self {
98		let (
99			directional_intent,
100			directionless_volume,
101		) = t.into();
102
103		Self {
104			directional_intent,
105			directionless_volume,
106		}
107	}
108}
109
110impl<IS: InstrumentSpec> FromVolumeType<IS> for DirectionalIntentVolume<IS> {
111	fn from_volume_type_checked(
112		volume_type: impl Into<IS::VolumeType>,
113	) -> Result<Self, FromVolumeTypeError>
114	where
115		Self: Sized,
116	{
117		let volume_type = volume_type.into();
118
119		if volume_type.is_zero() {
120			return Err(FromVolumeTypeError::new(volume_type.as_decimal()));
121		}
122
123		Ok(Self::from_volume_type_unchecked(volume_type))
124	}
125
126	fn from_volume_type_unchecked(
127		volume_type: impl Into<IS::VolumeType>,
128	) -> Self {
129		let volume_type = volume_type.into();
130
131		if volume_type.is_positive() {
132			return Self::from_volume_type_positive_unchecked(volume_type);
133		}
134
135		Self::from_volume_type_negative_unchecked(volume_type.abs())
136	}
137}
138
139impl<IS: InstrumentSpec> ZeroableVolume for DirectionalIntentVolume<IS> {
140	const ZERO: Self = DirectionalIntentVolume {
141		directional_intent: DirectionalIntent::Positive,
142		directionless_volume: DirectionlessVolume::ZERO,
143	};
144
145	fn is_zero(
146		&self,
147	) -> bool {
148		self.directionless_volume == DirectionlessVolume::ZERO
149	}
150}
151
152impl<IS: InstrumentSpec> From<AggressiveVolume<IS>> for DirectionalIntentVolume<IS> {
153	fn from(
154		value: AggressiveVolume<IS>,
155	) -> Self {
156		let directional_intent = value.as_aggressor_side().into();
157		let directionless_volume = value.into_directionless_volume();
158
159		Self {
160			directional_intent,
161			directionless_volume,
162		}
163	}
164}
165
166impl<IS: InstrumentSpec> From<&AggressiveVolume<IS>> for DirectionalIntentVolume<IS> {
167	fn from(
168		value: &AggressiveVolume<IS>,
169	) -> Self {
170		Self::from(*value)
171	}
172}
173
174impl<IS: InstrumentSpec> From<RestingVolume<IS>> for DirectionalIntentVolume<IS> {
175	fn from(
176		value: RestingVolume<IS>,
177	) -> Self {
178		let directional_intent = value.as_resting_side().into();
179		let directionless_volume = value.into_directionless_volume();
180
181		Self {
182			directional_intent,
183			directionless_volume,
184		}
185	}
186}
187
188impl<IS: InstrumentSpec> From<&RestingVolume<IS>> for DirectionalIntentVolume<IS> {
189	fn from(
190		value: &RestingVolume<IS>,
191	) -> Self {
192		Self::from(*value)
193	}
194}
195
196impl<IS: InstrumentSpec> PartialOrd for DirectionalIntentVolume<IS> {
197	fn partial_cmp(
198		&self,
199		other: &Self,
200	) -> Option<cmp::Ordering> {
201		Some(self.cmp(other))
202	}
203}
204
205impl<IS: InstrumentSpec> Ord for DirectionalIntentVolume<IS> {
206	fn cmp(
207		&self,
208		other: &Self,
209	) -> cmp::Ordering {
210		let lhs = self.as_volume_type_signed();
211		let rhs = other.as_volume_type_signed();
212
213		lhs.cmp(&rhs)
214	}
215}
216
217impl<IS: InstrumentSpec> From<WholePoints> for DirectionalIntentVolume<IS> {
218	fn from(
219		value: WholePoints,
220	) -> Self {
221		match Self::from_volume_type_unchecked_release(
222			value.into_subpoints::<IS::VolumeSpec>(),
223		) {
224			Ok(
225				directional_intent_volume,
226			) => directional_intent_volume,
227			Err(e) => {
228				error!("{e}");
229				panic!();
230			},
231		}
232	}
233}
234
235impl<IS: InstrumentSpec> From<&WholePoints> for DirectionalIntentVolume<IS> {
236	fn from(
237		value: &WholePoints,
238	) -> Self {
239		(*value).into()
240	}
241}
242
243impl<IS: InstrumentSpec> From<Subpoints> for DirectionalIntentVolume<IS> {
244	fn from(
245		value: Subpoints,
246	) -> Self {
247		match Self::from_volume_type_unchecked_release(value) {
248			Ok(
249				directional_intent_volume,
250			) => directional_intent_volume,
251			Err(e) => {
252				error!("{e}");
253				panic!();
254			},
255		}
256	}
257}
258
259impl<IS: InstrumentSpec> From<&Subpoints> for DirectionalIntentVolume<IS> {
260	fn from(
261		value: &Subpoints,
262	) -> Self {
263		(*value).into()
264	}
265}
266
267impl<IS: InstrumentSpec> From<DirectionalIntentVolume<IS>> for Result<
268	DirectionalIntentVolume<IS>,
269	FromVolumeTypeError,
270> {
271	fn from(
272		value: DirectionalIntentVolume<IS>,
273	) -> Self {
274		Ok(value)
275	}
276}
277
278impl_volume_binop!(
279	DirectionalIntentVolume,
280	AddReleaseUnchecked,
281	AddChecked,
282	add_checked,
283	AddUnchecked,
284	add_unchecked,
285	|lhs: DirectionalIntentVolume<IS>, rhs: DirectionalIntentVolume<IS>| { let lhs =
286	lhs.as_volume_type_signed(); let rhs = rhs.as_volume_type_signed(); let volume_type =
287	lhs + rhs; DirectionalIntentVolume::<IS>::from_volume_type_checked(volume_type) },
288	|lhs: DirectionalIntentVolume<IS>, rhs: DirectionalIntentVolume<IS>| { let lhs =
289	lhs.as_volume_type_signed(); let rhs = rhs.as_volume_type_signed(); let volume_type =
290	lhs + rhs; DirectionalIntentVolume::<IS>::from_volume_type_unchecked(volume_type) },
291	FromVolumeTypeError,
292	IS,
293	InstrumentSpec
294);
295
296impl_volume_binop!(
297	DirectionalIntentVolume,
298	SubReleaseUnchecked,
299	SubChecked,
300	sub_checked,
301	SubUnchecked,
302	sub_unchecked,
303	|lhs: DirectionalIntentVolume<IS>, rhs: DirectionalIntentVolume<IS>| { let lhs =
304	lhs.as_volume_type_signed(); let rhs = rhs.as_volume_type_signed(); let volume_type =
305	lhs - rhs; DirectionalIntentVolume::<IS>::from_volume_type_checked(volume_type) },
306	|lhs: DirectionalIntentVolume<IS>, rhs: DirectionalIntentVolume<IS>| { let lhs =
307	lhs.as_volume_type_signed(); let rhs = rhs.as_volume_type_signed(); let volume_type =
308	lhs - rhs; DirectionalIntentVolume::<IS>::from_volume_type_unchecked(volume_type) },
309	FromVolumeTypeError,
310	IS,
311	InstrumentSpec
312);