use crate::bar_indicators::indicator_value::IndicatorValue;
use crate::bar_indicators::liquidation_consumer::LiquidationConsumer;
use crate::core::types::Liquidation;
#[derive(Clone)]
pub struct LiquidationCooldown {
last_ts: Option<i64>,
last_delta_sec: f64,
}
impl LiquidationCooldown {
pub fn new() -> Self {
Self { last_ts: None, last_delta_sec: 0.0 }
}
}
impl Default for LiquidationCooldown {
fn default() -> Self {
Self::new()
}
}
impl LiquidationConsumer for LiquidationCooldown {
fn update_liquidation(&mut self, liq: &Liquidation) -> IndicatorValue {
if let Some(prev_ts) = self.last_ts {
let delta_ms = liq.timestamp.saturating_sub(prev_ts);
self.last_delta_sec = delta_ms as f64 / 1_000.0;
}
self.last_ts = Some(liq.timestamp);
IndicatorValue::Single(self.last_delta_sec)
}
fn value(&self) -> IndicatorValue {
IndicatorValue::Single(self.last_delta_sec)
}
fn reset(&mut self) {
self.last_ts = None;
self.last_delta_sec = 0.0;
}
fn is_ready(&self) -> bool {
self.last_delta_sec > 0.0
}
}
#[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 zero_before_any_event() {
let lc = LiquidationCooldown::new();
assert_eq!(lc.value(), IndicatorValue::Single(0.0));
assert!(!lc.is_ready());
}
#[test]
fn zero_after_first_event() {
let mut lc = LiquidationCooldown::new();
let v = lc.update_liquidation(&liq(1_000));
assert_eq!(v, IndicatorValue::Single(0.0));
assert!(!lc.is_ready());
}
#[test]
fn cooldown_after_second_event() {
let mut lc = LiquidationCooldown::new();
lc.update_liquidation(&liq(0));
let v = lc.update_liquidation(&liq(5_000));
assert_eq!(v, IndicatorValue::Single(5.0));
assert!(lc.is_ready());
}
#[test]
fn successive_cooldowns() {
let mut lc = LiquidationCooldown::new();
lc.update_liquidation(&liq(0));
lc.update_liquidation(&liq(2_000)); let v3 = lc.update_liquidation(&liq(7_000)); assert_eq!(v3, IndicatorValue::Single(5.0));
}
#[test]
fn reset_clears_state() {
let mut lc = LiquidationCooldown::new();
lc.update_liquidation(&liq(0));
lc.update_liquidation(&liq(3_000));
lc.reset();
assert_eq!(lc.value(), IndicatorValue::Single(0.0));
assert!(!lc.is_ready());
}
#[test]
fn no_underflow_on_equal_timestamps() {
let mut lc = LiquidationCooldown::new();
lc.update_liquidation(&liq(1_000));
let v = lc.update_liquidation(&liq(1_000));
assert_eq!(v, IndicatorValue::Single(0.0));
}
}