use crossbeam_skiplist::SkipMap;
use crossbeam_skiplist::map::Iter;
use either::Either;
use pricelevel::{PriceLevel, Side};
use std::iter::Rev;
use std::sync::Arc;
type PriceLevelIter<'a> =
Either<Rev<Iter<'a, u128, Arc<PriceLevel>>>, Iter<'a, u128, Arc<PriceLevel>>>;
#[derive(Debug, Clone)]
pub struct LevelInfo {
pub price: u128,
pub quantity: u64,
pub cumulative_depth: u64,
}
pub struct LevelsWithCumulativeDepth<'a> {
iter: PriceLevelIter<'a>,
cumulative_depth: u64,
}
impl<'a> LevelsWithCumulativeDepth<'a> {
pub fn new(price_levels: &'a SkipMap<u128, Arc<PriceLevel>>, side: Side) -> Self {
let iter = match side {
Side::Buy => Either::Left(price_levels.iter().rev()), Side::Sell => Either::Right(price_levels.iter()), };
Self {
iter,
cumulative_depth: 0,
}
}
}
impl<'a> Iterator for LevelsWithCumulativeDepth<'a> {
type Item = LevelInfo;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|entry| {
let price = *entry.key();
let quantity = entry.value().total_quantity().unwrap_or(0);
self.cumulative_depth = self.cumulative_depth.saturating_add(quantity);
LevelInfo {
price,
quantity,
cumulative_depth: self.cumulative_depth,
}
})
}
}
pub struct LevelsUntilDepth<'a> {
iter: PriceLevelIter<'a>,
target_depth: u64,
cumulative_depth: u64,
finished: bool,
}
impl<'a> LevelsUntilDepth<'a> {
pub fn new(
price_levels: &'a SkipMap<u128, Arc<PriceLevel>>,
side: Side,
target_depth: u64,
) -> Self {
let iter = match side {
Side::Buy => Either::Left(price_levels.iter().rev()),
Side::Sell => Either::Right(price_levels.iter()),
};
Self {
iter,
target_depth,
cumulative_depth: 0,
finished: false,
}
}
}
impl<'a> Iterator for LevelsUntilDepth<'a> {
type Item = LevelInfo;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
self.iter.next().map(|entry| {
let price = *entry.key();
let quantity = entry.value().total_quantity().unwrap_or(0);
self.cumulative_depth = self.cumulative_depth.saturating_add(quantity);
let level_info = LevelInfo {
price,
quantity,
cumulative_depth: self.cumulative_depth,
};
if self.cumulative_depth >= self.target_depth {
self.finished = true;
}
level_info
})
}
}
pub struct LevelsInRange<'a> {
iter: PriceLevelIter<'a>,
side: Side,
min_price: u128,
max_price: u128,
finished: bool,
}
impl<'a> LevelsInRange<'a> {
pub fn new(
price_levels: &'a SkipMap<u128, Arc<PriceLevel>>,
side: Side,
min_price: u128,
max_price: u128,
) -> Self {
let iter = match side {
Side::Buy => Either::Left(price_levels.iter().rev()),
Side::Sell => Either::Right(price_levels.iter()),
};
Self {
iter,
side,
min_price,
max_price,
finished: false,
}
}
}
impl<'a> Iterator for LevelsInRange<'a> {
type Item = LevelInfo;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
for entry in self.iter.by_ref() {
let price = *entry.key();
let past_far_edge = match self.side {
Side::Buy => price < self.min_price,
Side::Sell => price > self.max_price,
};
if past_far_edge {
self.finished = true;
return None;
}
if price >= self.min_price && price <= self.max_price {
let quantity = entry.value().total_quantity().unwrap_or(0);
return Some(LevelInfo {
price,
quantity,
cumulative_depth: 0, });
}
}
self.finished = true;
None
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_map(prices: impl IntoIterator<Item = u128>) -> SkipMap<u128, Arc<PriceLevel>> {
let map = SkipMap::new();
for p in prices {
map.insert(p, Arc::new(PriceLevel::new(p)));
}
map
}
#[test]
fn test_levels_in_range_terminates_at_far_edge_sell() {
let map = make_map(1..=1000u128);
let mut it = LevelsInRange::new(&map, Side::Sell, 10, 12);
let prices: Vec<u128> = (&mut it).map(|l| l.price).collect();
assert_eq!(prices, vec![10, 11, 12], "only in-band levels are yielded");
assert!(
it.finished,
"iterator marks itself finished at the far edge"
);
assert!(
it.iter.next().is_some(),
"iteration must stop at the far edge, leaving later entries unconsumed"
);
}
#[test]
fn test_levels_in_range_terminates_at_far_edge_buy() {
let map = make_map(1..=1000u128);
let mut it = LevelsInRange::new(&map, Side::Buy, 988, 990);
let prices: Vec<u128> = (&mut it).map(|l| l.price).collect();
assert_eq!(prices, vec![990, 989, 988], "descending in-band yield");
assert!(it.finished);
assert!(
it.iter.next().is_some(),
"iteration must stop below the band, leaving lower entries unconsumed"
);
}
#[test]
fn test_levels_in_range_empty_when_band_outside_book() {
let map = make_map(1..=10u128);
let got: Vec<u128> = LevelsInRange::new(&map, Side::Sell, 100, 200)
.map(|l| l.price)
.collect();
assert!(got.is_empty());
}
}