use std::collections::VecDeque;
use crate::bar_indicators::indicator_value::IndicatorValue;
use crate::bar_indicators::liquidation_consumer::LiquidationConsumer;
use crate::core::types::Liquidation;
#[derive(Clone)]
pub struct LiquidationRate {
window_ms: i64,
events: VecDeque<i64>,
last_rate: f64,
}
impl LiquidationRate {
pub fn new(window_ms: i64) -> Self {
Self {
window_ms: window_ms.max(1),
events: VecDeque::new(),
last_rate: 0.0,
}
}
fn evict(&mut self, now: i64) {
while let Some(&front) = self.events.front() {
if now - front > self.window_ms {
self.events.pop_front();
} else {
break;
}
}
}
}
impl LiquidationConsumer for LiquidationRate {
fn update_liquidation(&mut self, liq: &Liquidation) -> IndicatorValue {
self.events.push_back(liq.timestamp);
self.evict(liq.timestamp);
let span_seconds = self.window_ms as f64 / 1_000.0;
self.last_rate = self.events.len() as f64 / span_seconds;
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.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::types::TradeSide;
fn liq(ts: i64) -> Liquidation {
Liquidation { symbol: String::new(), side: TradeSide::Buy, price: 30_000.0, quantity: 0.1, timestamp: ts, value: None }
}
#[test]
fn rate_zero_initially() {
let lr = LiquidationRate::new(60_000);
assert!(!lr.is_ready());
assert_eq!(lr.value(), IndicatorValue::Single(0.0));
}
#[test]
fn rate_after_single_event() {
let mut lr = LiquidationRate::new(60_000);
lr.update_liquidation(&liq(0));
assert!(lr.is_ready());
if let IndicatorValue::Single(r) = lr.value() {
assert!((r - 1.0 / 60.0).abs() < 1e-9);
} else {
panic!("expected Single");
}
}
#[test]
fn old_events_evicted() {
let mut lr = LiquidationRate::new(10_000); lr.update_liquidation(&liq(0));
lr.update_liquidation(&liq(15_000));
if let IndicatorValue::Single(r) = lr.value() {
let expected = 1.0 / 10.0; assert!((r - expected).abs() < 1e-9, "rate={r}");
}
}
#[test]
fn reset_clears_state() {
let mut lr = LiquidationRate::new(60_000);
lr.update_liquidation(&liq(0));
lr.reset();
assert!(!lr.is_ready());
assert_eq!(lr.value(), IndicatorValue::Single(0.0));
}
}