use std::collections::VecDeque;
use crate::bar_indicators::indicator_value::IndicatorValue;
use crate::bar_indicators::orderbook_delta_consumer::OrderbookDeltaConsumer;
use crate::core::types::OrderbookDelta;
#[derive(Clone)]
pub struct LevelReplenishmentRate {
rolling_window: usize,
events: VecDeque<(i64, f64)>,
last_rate: f64,
}
impl LevelReplenishmentRate {
pub fn new(rolling_window: usize) -> Self {
Self {
rolling_window: rolling_window.max(2),
events: VecDeque::new(),
last_rate: 0.0,
}
}
#[inline]
fn compute_rate(&self) -> f64 {
let count = self.events.len();
if count < 2 {
return count as f64;
}
let oldest_ts = self.events.front().map(|e| e.0).unwrap_or(0);
let newest_ts = self.events.back().map(|e| e.0).unwrap_or(0);
let span_ms = (newest_ts - oldest_ts).max(1);
let span_sec = span_ms as f64 / 1000.0;
count as f64 / span_sec
}
}
impl OrderbookDeltaConsumer for LevelReplenishmentRate {
fn update_delta(&mut self, delta: &OrderbookDelta) -> IndicatorValue {
for level in delta.updated_bids().chain(delta.updated_asks()) {
self.events.push_back((delta.timestamp, level.price));
}
while self.events.len() > self.rolling_window {
self.events.pop_front();
}
self.last_rate = self.compute_rate();
IndicatorValue::Single(self.last_rate)
}
fn value(&self) -> IndicatorValue {
IndicatorValue::Single(self.last_rate)
}
fn reset(&mut self) {
self.events.clear();
self.last_rate = 0.0;
}
fn is_ready(&self) -> bool {
self.events.len() >= 2
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::types::OrderBookLevel;
fn make_delta(updates: &[(f64, f64)], ts: i64) -> OrderbookDelta {
OrderbookDelta {
bids: updates.iter().map(|&(p, s)| OrderBookLevel::new(p, s)).collect(),
asks: vec![],
timestamp: ts,
first_update_id: None,
last_update_id: None,
prev_update_id: None,
..Default::default()
}
}
#[test]
fn not_ready_with_single_event() {
let mut rate = LevelReplenishmentRate::new(50);
rate.update_delta(&make_delta(&[(100.0, 5.0)], 1000));
assert!(!rate.is_ready());
}
#[test]
fn ready_with_two_events() {
let mut rate = LevelReplenishmentRate::new(50);
rate.update_delta(&make_delta(&[(100.0, 5.0)], 1000));
rate.update_delta(&make_delta(&[(101.0, 5.0)], 2000));
assert!(rate.is_ready());
}
#[test]
fn rate_is_positive_with_data() {
let mut rate = LevelReplenishmentRate::new(50);
rate.update_delta(&make_delta(&[(100.0, 5.0)], 0));
rate.update_delta(&make_delta(&[(101.0, 5.0)], 1000));
rate.update_delta(&make_delta(&[(102.0, 5.0)], 2000));
assert!(rate.is_ready());
let v = rate.value().main();
assert!(v > 0.0, "rate should be positive");
assert!(v.is_finite(), "rate should be finite");
}
#[test]
fn window_trims_old_events() {
let mut rate = LevelReplenishmentRate::new(3);
for i in 0..10 {
rate.update_delta(&make_delta(&[(100.0 + i as f64, 5.0)], i * 1000));
}
assert!(rate.events.len() <= 3);
}
#[test]
fn reset_clears_state() {
let mut rate = LevelReplenishmentRate::new(50);
rate.update_delta(&make_delta(&[(100.0, 5.0)], 1000));
rate.update_delta(&make_delta(&[(101.0, 5.0)], 2000));
rate.reset();
assert!(!rate.is_ready());
assert_eq!(rate.value().main(), 0.0);
}
#[test]
fn removals_not_counted() {
let mut rate = LevelReplenishmentRate::new(50);
rate.update_delta(&make_delta(&[(100.0, 0.0), (101.0, 0.0)], 1000));
assert!(!rate.is_ready());
}
}