df_hyperliquid_core/live/store/
account.rs1use std::{
6 cmp::Reverse,
7 collections::{BTreeMap, HashSet},
8 hash::Hash,
9};
10
11use crate::account::history::{Fill, LedgerUpdate};
12use crate::account::{Abstraction, ClearinghouseState, DexState, FrontendOrder, Position, SpotState, Standing};
13use crate::live::Funding;
14use crate::live::store::stamp::{Stamped, put};
15
16pub const KEEP: usize = 4_000;
18
19#[derive(Debug, Clone, Default)]
21pub struct Account {
22 pub(crate) dexes: BTreeMap<String, Stamped<ClearinghouseState>>,
23 pub(crate) spot: Option<Stamped<SpotState>>,
24 pub(crate) mode: Option<Stamped<Abstraction>>,
25 pub(crate) orders: BTreeMap<String, Stamped<Vec<FrontendOrder>>>,
26 pub(crate) fills: Option<Stamped<Vec<Fill>>>,
27 pub(crate) fundings: Option<Stamped<Vec<Funding>>>,
28 pub(crate) ledger: Option<Stamped<Vec<LedgerUpdate>>>,
29 standing: Standing,
31}
32
33impl Account {
34 #[must_use]
36 pub const fn standing(&self) -> &Standing {
37 &self.standing
38 }
39
40 #[must_use]
42 pub const fn mode(&self) -> Option<&Stamped<Abstraction>> {
43 self.mode.as_ref()
44 }
45
46 #[must_use]
47 pub const fn spot(&self) -> Option<&Stamped<SpotState>> {
48 self.spot.as_ref()
49 }
50
51 #[must_use]
53 pub fn dex(&self, dex: &str) -> Option<&Stamped<ClearinghouseState>> {
54 self.dexes.get(dex)
55 }
56
57 pub fn dexes(&self) -> impl Iterator<Item = (&str, &Stamped<ClearinghouseState>)> {
59 self.dexes.iter().map(|(dex, state)| (dex.as_str(), state))
60 }
61
62 pub fn positions(&self) -> impl Iterator<Item = &Position> {
64 self.dexes.values().flat_map(|state| state.positions())
65 }
66
67 #[must_use]
69 pub fn orders(&self, dex: &str) -> Option<&Stamped<Vec<FrontendOrder>>> {
70 self.orders.get(dex)
71 }
72
73 pub fn all_orders(&self) -> impl Iterator<Item = &FrontendOrder> {
75 self.orders.values().flat_map(|orders| orders.iter())
76 }
77
78 #[must_use]
80 pub const fn fills(&self) -> Option<&Stamped<Vec<Fill>>> {
81 self.fills.as_ref()
82 }
83
84 #[must_use]
86 pub const fn fundings(&self) -> Option<&Stamped<Vec<Funding>>> {
87 self.fundings.as_ref()
88 }
89
90 #[must_use]
92 pub const fn ledger(&self) -> Option<&Stamped<Vec<LedgerUpdate>>> {
93 self.ledger.as_ref()
94 }
95
96 #[must_use]
98 pub fn heard(&self) -> Option<u64> {
99 let rows = [
100 self.fills.as_ref().map(|fills| fills.heard),
101 self.fundings.as_ref().map(|fundings| fundings.heard),
102 self.ledger.as_ref().map(|ledger| ledger.heard),
103 ];
104 let orders = self.orders.values().map(|orders| Some(orders.heard));
105 rows.into_iter().chain(orders).chain([self.state_heard()]).flatten().max()
106 }
107
108 fn state_heard(&self) -> Option<u64> {
110 let dexes = self.dexes.values().map(|state| state.heard);
111 let rest = [self.spot.as_ref().map(|spot| spot.heard), self.mode.as_ref().map(|mode| mode.heard)];
112 dexes.chain(rest.into_iter().flatten()).max()
113 }
114
115 pub(crate) fn set_dex(&mut self, now: u64, dex: &str, state: ClearinghouseState) -> bool {
117 let changed = match self.dexes.get_mut(dex) {
118 Some(held) => held.set(now, state),
119 None => {
120 self.dexes.insert(dex.to_owned(), Stamped::new(now, state));
121 true
122 }
123 };
124 self.state_told(changed)
125 }
126
127 pub(crate) fn set_dexes(&mut self, now: u64, states: Vec<(String, ClearinghouseState)>) -> bool {
129 let before = self.dexes.len();
130 self.dexes.retain(|dex, _| states.iter().any(|(listed, _)| listed == dex));
131 let mut changed = self.dexes.len() != before;
132 for (dex, state) in states {
133 changed |= match self.dexes.get_mut(&dex) {
134 Some(held) => held.set(now, state),
135 None => {
136 self.dexes.insert(dex, Stamped::new(now, state));
137 true
138 }
139 };
140 }
141 self.state_told(changed)
142 }
143
144 pub(crate) fn set_spot(&mut self, now: u64, spot: SpotState) -> bool {
145 let changed = put(&mut self.spot, now, spot);
146 self.state_told(changed)
147 }
148
149 pub(crate) fn set_mode(&mut self, now: u64, mode: Abstraction) -> bool {
150 let changed = put(&mut self.mode, now, mode);
151 self.state_told(changed)
152 }
153
154 pub(crate) fn set_orders(&mut self, now: u64, dex: &str, orders: Vec<FrontendOrder>) -> bool {
155 match self.orders.get_mut(dex) {
156 Some(held) => held.set(now, orders),
157 None => {
158 self.orders.insert(dex.to_owned(), Stamped::new(now, orders));
159 true
160 }
161 }
162 }
163
164 pub(crate) fn fold_fills(&mut self, now: u64, fills: Vec<Fill>, whole: bool) -> bool {
169 fold_rows(&mut self.fills, now, fills, whole, |fill| (fill.tid, fill.oid), |fill| fill.time)
170 }
171
172 pub(crate) fn fold_fundings(&mut self, now: u64, fundings: Vec<Funding>, whole: bool) -> bool {
175 fold_rows(&mut self.fundings, now, fundings, whole, |row| (row.time, row.coin.clone()), |row| row.time)
176 }
177
178 pub(crate) fn fold_ledger(&mut self, now: u64, rows: Vec<LedgerUpdate>, whole: bool) -> bool {
181 fold_rows(
182 &mut self.ledger,
183 now,
184 rows,
185 whole,
186 |row| (row.time, row.hash.clone(), format!("{:?}", row.delta)),
187 |row| row.time,
188 )
189 }
190
191 pub(crate) fn keep(&mut self, keep: &Keep<'_>) {
194 if !keep.state && (self.spot.is_some() || self.mode.is_some() || !self.dexes.is_empty()) {
195 self.dexes.clear();
196 self.spot = None;
197 self.mode = None;
198 self.restand();
199 }
200 self.orders.retain(|dex, _| (keep.orders)(dex));
201 if !keep.fills {
202 self.fills = None;
203 }
204 if !keep.fundings {
205 self.fundings = None;
206 }
207 if !keep.ledger {
208 self.ledger = None;
209 }
210 }
211
212 fn state_told(&mut self, changed: bool) -> bool {
215 match changed {
216 true => self.restand(),
217 false => self.standing.heard = self.state_heard(),
218 }
219 changed
220 }
221
222 fn restand(&mut self) {
223 self.standing = Standing {
224 mode: self.mode.as_ref().map(|mode| mode.value.clone()),
225 dexes: self
226 .dexes
227 .iter()
228 .map(|(dex, state)| DexState { dex: dex.clone(), state: state.value.clone() })
229 .collect(),
230 spot: self.spot.as_ref().map(|spot| spot.value.clone()),
231 heard: self.state_heard(),
232 };
233 }
234}
235
236fn fold_rows<T: PartialEq, K: Hash + Eq>(
240 slot: &mut Option<Stamped<Vec<T>>>,
241 now: u64,
242 rows: Vec<T>,
243 whole: bool,
244 key: impl Fn(&T) -> K,
245 time: impl Fn(&T) -> u64,
246) -> bool {
247 let mut seen: HashSet<K> = HashSet::new();
248 let held = match (whole, slot.as_mut()) {
249 (false, Some(held)) => {
250 seen.extend(held.iter().map(&key));
251 Some(held)
252 }
253 _ => None,
254 };
255 let mut fresh: Vec<T> = rows.into_iter().filter(|row| seen.insert(key(row))).collect();
256 let Some(held) = held else {
257 fresh.sort_by_key(|row| Reverse(time(row)));
258 fresh.truncate(KEEP);
259 return put(slot, now, fresh);
260 };
261 if fresh.is_empty() {
262 held.touch(now, false);
263 return false;
264 }
265 fresh.append(&mut held.value);
266 fresh.sort_by_key(|row| Reverse(time(row)));
267 fresh.truncate(KEEP);
268 held.value = fresh;
269 held.touch(now, true);
270 true
271}
272
273#[derive(Clone, Copy)]
275pub(crate) struct Keep<'a> {
276 pub(crate) state: bool,
277 pub(crate) orders: &'a dyn Fn(&str) -> bool,
278 pub(crate) fills: bool,
279 pub(crate) fundings: bool,
280 pub(crate) ledger: bool,
281}
282
283#[cfg(test)]
284#[path = "tests/account.rs"]
285mod tests;