use crate::{AccountEventKind, UnindexedAccountEvent, order::state::OrderState};
use lru::LruCache;
use rust_decimal::Decimal;
use smol_str::SmolStr;
use std::{num::NonZeroUsize, sync::Arc};
pub(crate) const DEDUP_CACHE_SIZE: usize = 10_000;
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
pub(crate) enum DedupEventKind {
Trade,
OrderState {
filled_quantity: Decimal,
},
Cancelled,
#[cfg(feature = "hyperliquid")]
Funding,
Notice {
time_exchange_ms: i64,
},
}
#[derive(Debug, Hash, Eq, PartialEq)]
pub(crate) struct DedupKey {
pub(crate) instrument: SmolStr,
pub(crate) id: SmolStr,
pub(crate) kind: DedupEventKind,
}
pub(crate) type SharedDedupCache = Arc<parking_lot::Mutex<LruCache<DedupKey, ()>>>;
pub(crate) fn new_dedup_cache() -> SharedDedupCache {
#[allow(clippy::unwrap_used)]
Arc::new(parking_lot::Mutex::new(LruCache::new(
NonZeroUsize::new(DEDUP_CACHE_SIZE).unwrap(),
)))
}
pub(crate) fn dedup_key_from_event(event: &UnindexedAccountEvent) -> Option<DedupKey> {
match &event.kind {
AccountEventKind::Trade(trade) => Some(DedupKey {
instrument: trade.instrument.name().clone(),
id: trade.id.0.clone(),
kind: DedupEventKind::Trade,
}),
AccountEventKind::OrderSnapshot(snap) => {
match &snap.0.state {
OrderState::Active(active) => {
use crate::order::state::ActiveOrderState;
match active {
ActiveOrderState::Open(open) => Some(DedupKey {
instrument: snap.0.key.instrument.name().clone(),
id: match open.id.assigned() {
Some(id) => id.0.clone(),
None => snap.0.key.cid.0.clone(),
},
kind: DedupEventKind::OrderState {
filled_quantity: open.filled_quantity,
},
}),
_ => None,
}
}
_ => None,
}
}
AccountEventKind::OrderCancelled(resp) => match &resp.state {
Ok(cancelled) => Some(DedupKey {
instrument: resp.key.instrument.name().clone(),
id: cancelled.id.0.clone(),
kind: DedupEventKind::Cancelled,
}),
Err(_) => None, },
AccountEventKind::Notice(notice) => Some(DedupKey {
instrument: notice
.instrument
.as_ref()
.map(|instrument| instrument.name().clone())
.unwrap_or_default(),
id: notice.status.clone(),
kind: DedupEventKind::Notice {
time_exchange_ms: notice.time_exchange.timestamp_millis(),
},
}),
_ => None, }
}
pub(crate) fn is_duplicate(cache: &SharedDedupCache, key: DedupKey) -> bool {
let mut guard = cache.lock();
if guard.peek(&key).is_some() {
return true;
}
guard.put(key, ());
false
}