apple-quant-algorithmic 0.1.0

Apple Quant's algorithmic trading api
Documentation
use std::{fmt::Debug, marker::PhantomData};

use smallvec::SmallVec;

use crate::{
	aggregation::Aggregator,
	instrument::{InstrumentData, InstrumentSpec},
};

#[derive(Debug)]
pub struct FixedHotData<
	'instrument_data,
	'aggregated_data,
	const COUNT: usize,
	IS: InstrumentSpec,
	AggregationData,
	T: Aggregator<'instrument_data, 'aggregated_data, T, AggregationData, IS>
		+ 'aggregated_data
		+ Clone
		+ Debug,
> {
	data: SmallVec<[T; COUNT]>,

	_aggregation_data: PhantomData<AggregationData>,
	_instrument_spec: PhantomData<IS>,
	_instrument_data: PhantomData<&'instrument_data ()>,
	_a: PhantomData<&'aggregated_data ()>,
}

impl<
	'instrument_data,
	'aggregated_data,
	const COUNT: usize,
	IS: InstrumentSpec,
	AggregationData,
	T: Aggregator<'instrument_data, 'aggregated_data, T, AggregationData, IS>
		+ 'aggregated_data
		+ Clone
		+ Debug,
> Default for FixedHotData<'instrument_data, 'aggregated_data, COUNT, IS, AggregationData, T>
{
	fn default() -> Self {
		Self {
			data: SmallVec::default(),

			_aggregation_data: PhantomData::default(),
			_instrument_spec: PhantomData::default(),
			_instrument_data: PhantomData::default(),
			_a: PhantomData::default(),
		}
	}
}

impl<
	'instrument_data,
	'aggregated_data,
	const COUNT: usize,
	IS: InstrumentSpec,
	AggregationData,
	T: Aggregator<'instrument_data, 'aggregated_data, T, AggregationData, IS>
		+ 'aggregated_data
		+ Clone
		+ Debug,
> FixedHotData<'instrument_data, 'aggregated_data, COUNT, IS, AggregationData, T>
{
	pub fn len(&self) -> usize {
		self.data.len()
	}

	pub fn capacity(&self) -> usize {
		COUNT
	}

	pub fn is_empty(&self) -> bool {
		self.data.is_empty()
	}

	pub fn clear(&mut self) {
		self.data.clear();
	}

	pub fn remove_newest(&mut self) -> Option<T> {
		self.data.pop()
	}

	pub fn newest(&self) -> Option<&T> {
		self.data.last()
	}

	pub fn oldest(&self) -> Option<&T> {
		self.data.first()
	}

	pub fn data_update(
		&mut self,
		instrument_data: &'instrument_data InstrumentData<'instrument_data, 'aggregated_data, IS>,
	) {
		let aggregated_data: SmallVec<[T; 4]> = {
			let aggregation_data = T::hot_data(
				instrument_data,
				self.iter_rev(),
			);

			T::aggregate_hot(&aggregation_data).collect()
		};

		self.append_manual(aggregated_data.into_iter());
	}

	pub fn append_manual(
		&mut self,
		append_data: impl ExactSizeIterator<Item = T>,
	) {
		debug_assert!(self.data.len() <= COUNT);

		let unused_capacity = COUNT - self.data.len();
		let mut drain_count = append_data
			.len()
			.saturating_sub(unused_capacity)
			.min(COUNT);

		// let drain_to_idx = (COUNT - drain_count)
		// 	.checked_sub(1)
		// 	.unwrap();

		let data_skip_count = append_data
			.len()
			.saturating_sub(COUNT)
			.min(COUNT);

		drain_count = drain_count
			.checked_sub(data_skip_count)
			.unwrap();

		// println!(
		// 	"unused_capacity: {unused_capacity}. drain_count: {drain_count}. data_skip_count: {data_skip_count}. appending data len: {}",
		// 	data.len(),
		// );

		self.data
			.drain(0..drain_count);

		self.data.extend(
			append_data
				.into_iter()
				.take(COUNT)
				.skip(data_skip_count),
		);

		debug_assert!(!self.data.spilled());
	}

	pub fn get(
		&self,
		idx: usize,
	) -> Option<&T> {
		debug_assert!(idx < COUNT);

		self.data.get(idx)
	}

	pub fn iter(&self) -> impl Iterator<Item = &T> {
		self.data.iter()
	}

	pub fn iter_rev(&self) -> impl Iterator<Item = &T> + use<'_, COUNT, IS, AggregationData, T> {
		self.data.iter().rev()
	}

	pub fn forward_slice_from<P: FnMut(&T) -> bool>(
		&'aggregated_data self,
		exclude: P,
	) -> &'aggregated_data [T] {
		let Some(idx) = self
			.data
			.iter()
			.rev()
			.position(exclude)
		else {
			return &self.data;
		};

		let idx = idx + 1;

		if idx >= self.data.len() {
			return &self.data;
		}

		&self.data[idx..]
	}

	pub fn forward_slice(&'aggregated_data self) -> &'aggregated_data [T] {
		&self.data
	}
}