nautilus_common/cache/database.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Provides a `Cache` database backing.
17
18use std::fmt::Debug;
19
20use ahash::AHashMap;
21use bytes::Bytes;
22use nautilus_core::{UUID4, UnixNanos};
23use nautilus_model::{
24 accounts::AccountAny,
25 data::{
26 Bar, CustomData, DataType, FundingRateUpdate, InstrumentClose, QuoteTick, TradeTick,
27 greeks::{GreeksData, YieldCurveData},
28 },
29 events::{OrderEventAny, OrderSnapshot, position::snapshot::PositionSnapshot},
30 identifiers::{
31 AccountId, ActorId, ClientId, ClientOrderId, InstrumentId, PositionId, StrategyId,
32 TraderId, VenueOrderId,
33 },
34 instruments::{InstrumentAny, SyntheticInstrument},
35 orderbook::OrderBook,
36 orders::OrderAny,
37 position::Position,
38 types::{Currency, Money},
39};
40use ustr::Ustr;
41
42use super::config::CacheConfig;
43use crate::signal::Signal;
44
45#[derive(Debug, Default)]
46pub struct CacheMap {
47 pub currencies: AHashMap<Ustr, Currency>,
48 pub instruments: AHashMap<InstrumentId, InstrumentAny>,
49 pub instrument_closes: AHashMap<InstrumentId, InstrumentClose>,
50 pub synthetics: AHashMap<InstrumentId, SyntheticInstrument>,
51 pub accounts: AHashMap<AccountId, AccountAny>,
52 pub orders: AHashMap<ClientOrderId, OrderAny>,
53 pub positions: AHashMap<PositionId, Position>,
54 pub greeks: AHashMap<InstrumentId, GreeksData>,
55 pub yield_curves: AHashMap<String, YieldCurveData>,
56}
57
58/// Factory for constructing cache database adapters at runtime.
59///
60/// Implementations own the concrete database configuration and return the transport-neutral
61/// [`CacheDatabaseAdapter`] surface used by the cache.
62#[async_trait::async_trait]
63pub trait CacheDatabaseFactory: Debug + Send + Sync {
64 /// Creates a cache database adapter for the given cache runtime.
65 ///
66 /// # Errors
67 ///
68 /// Returns an error if adapter construction or connection setup fails.
69 async fn create(
70 &self,
71 trader_id: TraderId,
72 instance_id: UUID4,
73 config: CacheConfig,
74 ) -> anyhow::Result<Box<dyn CacheDatabaseAdapter>>;
75}
76
77#[async_trait::async_trait]
78pub trait CacheDatabaseAdapter {
79 /// Closes the cache database connection.
80 ///
81 /// # Errors
82 ///
83 /// Returns an error if the database fails to close properly.
84 fn close(&mut self) -> anyhow::Result<()>;
85
86 /// Flushes any pending changes to the database.
87 ///
88 /// # Errors
89 ///
90 /// Returns an error if flushing changes fails.
91 fn flush(&mut self) -> anyhow::Result<()>;
92
93 /// Loads all cached data into memory.
94 ///
95 /// # Errors
96 ///
97 /// Returns an error if loading data from the database fails.
98 async fn load_all(&self) -> anyhow::Result<CacheMap>;
99
100 /// Loads raw key-value data from the database.
101 ///
102 /// # Errors
103 ///
104 /// Returns an error if the load operation fails.
105 fn load(&self) -> anyhow::Result<AHashMap<String, Bytes>>;
106
107 /// Loads all currencies from the cache.
108 ///
109 /// # Errors
110 ///
111 /// Returns an error if loading currencies fails.
112 async fn load_currencies(&self) -> anyhow::Result<AHashMap<Ustr, Currency>>;
113
114 /// Loads all instruments from the cache.
115 ///
116 /// # Errors
117 ///
118 /// Returns an error if loading instruments fails.
119 async fn load_instruments(&self) -> anyhow::Result<AHashMap<InstrumentId, InstrumentAny>>;
120
121 /// Loads all instrument closes from the cache.
122 ///
123 /// # Errors
124 ///
125 /// Returns an error if loading instrument closes fails.
126 async fn load_instrument_closes(
127 &self,
128 ) -> anyhow::Result<AHashMap<InstrumentId, InstrumentClose>>;
129
130 /// Loads all synthetic instruments from the cache.
131 ///
132 /// # Errors
133 ///
134 /// Returns an error if loading synthetic instruments fails.
135 async fn load_synthetics(&self) -> anyhow::Result<AHashMap<InstrumentId, SyntheticInstrument>>;
136
137 /// Loads all accounts from the cache.
138 ///
139 /// # Errors
140 ///
141 /// Returns an error if loading accounts fails.
142 async fn load_accounts(&self) -> anyhow::Result<AHashMap<AccountId, AccountAny>>;
143
144 /// Loads all orders from the cache.
145 ///
146 /// # Errors
147 ///
148 /// Returns an error if loading orders fails.
149 async fn load_orders(&self) -> anyhow::Result<AHashMap<ClientOrderId, OrderAny>>;
150
151 /// Loads all positions from the cache.
152 ///
153 /// # Errors
154 ///
155 /// Returns an error if loading positions fails.
156 async fn load_positions(&self) -> anyhow::Result<AHashMap<PositionId, Position>>;
157
158 /// Loads all [`GreeksData`] from the cache.
159 ///
160 /// # Errors
161 ///
162 /// Returns an error if loading greeks data fails.
163 async fn load_greeks(&self) -> anyhow::Result<AHashMap<InstrumentId, GreeksData>> {
164 Ok(AHashMap::new())
165 }
166
167 /// Loads all [`YieldCurveData`] from the cache.
168 ///
169 /// # Errors
170 ///
171 /// Returns an error if loading yield curve data fails.
172 async fn load_yield_curves(&self) -> anyhow::Result<AHashMap<String, YieldCurveData>> {
173 Ok(AHashMap::new())
174 }
175
176 /// Loads mapping from order IDs to position IDs.
177 ///
178 /// # Errors
179 ///
180 /// Returns an error if loading the index order-position mapping fails.
181 fn load_index_order_position(&self) -> anyhow::Result<AHashMap<ClientOrderId, PositionId>>;
182
183 /// Loads mapping from order IDs to client IDs.
184 ///
185 /// # Errors
186 ///
187 /// Returns an error if loading the index order-client mapping fails.
188 fn load_index_order_client(&self) -> anyhow::Result<AHashMap<ClientOrderId, ClientId>>;
189
190 /// Loads a single currency by code.
191 ///
192 /// # Errors
193 ///
194 /// Returns an error if loading a single currency fails.
195 async fn load_currency(&self, code: &Ustr) -> anyhow::Result<Option<Currency>>;
196
197 /// Loads a single instrument by ID.
198 ///
199 /// # Errors
200 ///
201 /// Returns an error if loading a single instrument fails.
202 async fn load_instrument(
203 &self,
204 instrument_id: &InstrumentId,
205 ) -> anyhow::Result<Option<InstrumentAny>>;
206
207 /// Loads a single synthetic instrument by ID.
208 ///
209 /// # Errors
210 ///
211 /// Returns an error if loading a single synthetic instrument fails.
212 async fn load_synthetic(
213 &self,
214 instrument_id: &InstrumentId,
215 ) -> anyhow::Result<Option<SyntheticInstrument>>;
216
217 /// Loads a single account by ID.
218 ///
219 /// # Errors
220 ///
221 /// Returns an error if loading a single account fails.
222 async fn load_account(&self, account_id: &AccountId) -> anyhow::Result<Option<AccountAny>>;
223
224 /// Loads a single order by client order ID.
225 ///
226 /// # Errors
227 ///
228 /// Returns an error if loading a single order fails.
229 async fn load_order(&self, client_order_id: &ClientOrderId)
230 -> anyhow::Result<Option<OrderAny>>;
231
232 /// Loads a single position by position ID.
233 ///
234 /// # Errors
235 ///
236 /// Returns an error if loading a single position fails.
237 async fn load_position(&self, position_id: &PositionId) -> anyhow::Result<Option<Position>>;
238
239 /// Loads actor state by actor ID.
240 ///
241 /// # Errors
242 ///
243 /// Returns an error if loading actor state fails.
244 fn load_actor(&self, actor_id: &ActorId) -> anyhow::Result<AHashMap<String, Bytes>>;
245
246 /// Loads strategy state by strategy ID.
247 ///
248 /// # Errors
249 ///
250 /// Returns an error if loading strategy state fails.
251 fn load_strategy(&self, strategy_id: &StrategyId) -> anyhow::Result<AHashMap<String, Bytes>>;
252
253 /// Loads signals by name.
254 ///
255 /// # Errors
256 ///
257 /// Returns an error if loading signals fails.
258 fn load_signals(&self, name: &str) -> anyhow::Result<Vec<Signal>>;
259
260 /// Loads custom data by data type.
261 ///
262 /// # Errors
263 ///
264 /// Returns an error if loading custom data fails.
265 fn load_custom_data(&self, data_type: &DataType) -> anyhow::Result<Vec<CustomData>>;
266
267 /// Loads an order snapshot by client order ID.
268 ///
269 /// # Errors
270 ///
271 /// Returns an error if loading the order snapshot fails.
272 fn load_order_snapshot(
273 &self,
274 client_order_id: &ClientOrderId,
275 ) -> anyhow::Result<Option<OrderSnapshot>>;
276
277 /// Loads a position snapshot by position ID.
278 ///
279 /// # Errors
280 ///
281 /// Returns an error if loading the position snapshot fails.
282 fn load_position_snapshot(
283 &self,
284 position_id: &PositionId,
285 ) -> anyhow::Result<Option<PositionSnapshot>>;
286
287 /// Loads quote ticks by instrument ID.
288 ///
289 /// # Errors
290 ///
291 /// Returns an error if loading quotes fails.
292 fn load_quotes(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<QuoteTick>>;
293
294 /// Loads trade ticks by instrument ID.
295 ///
296 /// # Errors
297 ///
298 /// Returns an error if loading trades fails.
299 fn load_trades(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<TradeTick>>;
300
301 /// Loads funding rate updates by instrument ID.
302 ///
303 /// # Errors
304 ///
305 /// Returns an error if loading funding rates fails.
306 fn load_funding_rates(
307 &self,
308 instrument_id: &InstrumentId,
309 ) -> anyhow::Result<Vec<FundingRateUpdate>>;
310
311 /// Loads bars by instrument ID.
312 ///
313 /// # Errors
314 ///
315 /// Returns an error if loading bars fails.
316 fn load_bars(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<Bar>>;
317
318 /// Adds a generic key-value pair to the cache.
319 ///
320 /// # Errors
321 ///
322 /// Returns an error if adding a generic key/value fails.
323 fn add(&self, key: String, value: Bytes) -> anyhow::Result<()>;
324
325 /// Adds a currency to the cache.
326 ///
327 /// # Errors
328 ///
329 /// Returns an error if adding a currency fails.
330 fn add_currency(&self, currency: &Currency) -> anyhow::Result<()>;
331
332 /// Adds an instrument to the cache.
333 ///
334 /// # Errors
335 ///
336 /// Returns an error if adding an instrument fails.
337 fn add_instrument(&self, instrument: &InstrumentAny) -> anyhow::Result<()>;
338
339 /// Adds an instrument close to the cache, replacing any existing value for the instrument.
340 /// Implementations must queue persistence without waiting for the database operation to
341 /// complete.
342 ///
343 /// # Errors
344 ///
345 /// Returns an error if the instrument close cannot be queued for persistence.
346 fn add_instrument_close(&self, close: &InstrumentClose) -> anyhow::Result<()>;
347
348 /// Adds a synthetic instrument to the cache.
349 ///
350 /// # Errors
351 ///
352 /// Returns an error if adding a synthetic instrument fails.
353 fn add_synthetic(&self, synthetic: &SyntheticInstrument) -> anyhow::Result<()>;
354
355 /// Adds an account to the cache.
356 ///
357 /// # Errors
358 ///
359 /// Returns an error if adding an account fails.
360 fn add_account(&self, account: &AccountAny) -> anyhow::Result<()>;
361
362 /// Adds an order to the cache.
363 ///
364 /// # Errors
365 ///
366 /// Returns an error if adding an order fails.
367 fn add_order(&self, order: &OrderAny, client_id: Option<ClientId>) -> anyhow::Result<()>;
368
369 /// Adds an order snapshot to the cache.
370 ///
371 /// # Errors
372 ///
373 /// Returns an error if adding an order snapshot fails.
374 fn add_order_snapshot(&self, snapshot: &OrderSnapshot) -> anyhow::Result<()>;
375
376 /// Adds a position to the cache.
377 ///
378 /// # Errors
379 ///
380 /// Returns an error if adding a position fails.
381 fn add_position(&self, position: &Position) -> anyhow::Result<()>;
382
383 /// Adds a position snapshot to the cache.
384 ///
385 /// # Errors
386 ///
387 /// Returns an error if adding a position snapshot fails.
388 fn add_position_snapshot(&self, snapshot: &PositionSnapshot) -> anyhow::Result<()>;
389
390 /// Adds an order book to the cache.
391 ///
392 /// # Errors
393 ///
394 /// Returns an error if adding an order book fails.
395 fn add_order_book(&self, order_book: &OrderBook) -> anyhow::Result<()>;
396
397 /// Adds a signal to the cache.
398 ///
399 /// # Errors
400 ///
401 /// Returns an error if adding a signal fails.
402 fn add_signal(&self, signal: &Signal) -> anyhow::Result<()>;
403
404 /// Adds custom data to the cache.
405 ///
406 /// # Errors
407 ///
408 /// Returns an error if adding custom data fails.
409 fn add_custom_data(&self, data: &CustomData) -> anyhow::Result<()>;
410
411 /// Adds a quote tick to the cache.
412 ///
413 /// # Errors
414 ///
415 /// Returns an error if adding a quote tick fails.
416 fn add_quote(&self, quote: &QuoteTick) -> anyhow::Result<()>;
417
418 /// Adds a trade tick to the cache.
419 ///
420 /// # Errors
421 ///
422 /// Returns an error if adding a trade tick fails.
423 fn add_trade(&self, trade: &TradeTick) -> anyhow::Result<()>;
424
425 /// Adds a funding rate update to the cache.
426 ///
427 /// # Errors
428 ///
429 /// Returns an error if adding a funding rate update fails.
430 fn add_funding_rate(&self, funding_rate: &FundingRateUpdate) -> anyhow::Result<()>;
431
432 /// Adds a bar to the cache.
433 ///
434 /// # Errors
435 ///
436 /// Returns an error if adding a bar fails.
437 fn add_bar(&self, bar: &Bar) -> anyhow::Result<()>;
438
439 /// Adds greeks data to the cache.
440 ///
441 /// # Errors
442 ///
443 /// Returns an error if adding greeks data fails.
444 fn add_greeks(&self, _greeks: &GreeksData) -> anyhow::Result<()> {
445 Ok(())
446 }
447
448 /// Adds yield curve data to the cache.
449 ///
450 /// # Errors
451 ///
452 /// Returns an error if adding yield curve data fails.
453 fn add_yield_curve(&self, _yield_curve: &YieldCurveData) -> anyhow::Result<()> {
454 Ok(())
455 }
456
457 /// Deletes actor state from the cache.
458 ///
459 /// # Errors
460 ///
461 /// Returns an error if deleting actor state fails.
462 fn delete_actor(&self, actor_id: &ActorId) -> anyhow::Result<()>;
463
464 /// Deletes strategy state from the cache.
465 ///
466 /// # Errors
467 ///
468 /// Returns an error if deleting strategy state fails.
469 fn delete_strategy(&self, component_id: &StrategyId) -> anyhow::Result<()>;
470
471 /// Deletes an order from the cache.
472 ///
473 /// # Errors
474 ///
475 /// Returns an error if deleting an order fails.
476 fn delete_order(&self, client_order_id: &ClientOrderId) -> anyhow::Result<()>;
477
478 /// Deletes a position from the cache.
479 ///
480 /// # Errors
481 ///
482 /// Returns an error if deleting a position fails.
483 fn delete_position(&self, position_id: &PositionId) -> anyhow::Result<()>;
484
485 /// Deletes an account event from the cache.
486 ///
487 /// # Errors
488 ///
489 /// Returns an error if deleting account events fails.
490 fn delete_account_event(&self, account_id: &AccountId, event_id: &str) -> anyhow::Result<()>;
491
492 /// Indexes a venue order ID with its client order ID.
493 ///
494 /// # Errors
495 ///
496 /// Returns an error if indexing venue order ID fails.
497 fn index_venue_order_id(
498 &self,
499 client_order_id: ClientOrderId,
500 venue_order_id: VenueOrderId,
501 ) -> anyhow::Result<()>;
502
503 /// Indexes an order-position mapping.
504 ///
505 /// # Errors
506 ///
507 /// Returns an error if indexing order-position mapping fails.
508 fn index_order_position(
509 &self,
510 client_order_id: ClientOrderId,
511 position_id: PositionId,
512 ) -> anyhow::Result<()>;
513
514 /// Indexes order-client mappings as one batch operation.
515 ///
516 /// # Errors
517 ///
518 /// Returns an error if batch order-client indexing is unsupported or cannot be enqueued.
519 fn index_order_clients(&self, claims: &[(ClientOrderId, ClientId)]) -> anyhow::Result<()> {
520 if claims.is_empty() {
521 return Ok(());
522 }
523
524 anyhow::bail!("Batch order-client indexing is not supported by this cache database")
525 }
526
527 /// Updates actor state in the cache.
528 ///
529 /// # Errors
530 ///
531 /// Returns an error if updating actor state fails.
532 fn update_actor(
533 &self,
534 actor_id: &ActorId,
535 state: &AHashMap<String, Bytes>,
536 ) -> anyhow::Result<()>;
537
538 /// Updates strategy state in the cache.
539 ///
540 /// # Errors
541 ///
542 /// Returns an error if updating strategy state fails.
543 fn update_strategy(
544 &self,
545 strategy_id: &StrategyId,
546 state: &AHashMap<String, Bytes>,
547 ) -> anyhow::Result<()>;
548
549 /// Updates an account in the cache.
550 ///
551 /// # Errors
552 ///
553 /// Returns an error if updating an account fails.
554 fn update_account(&self, account: &AccountAny) -> anyhow::Result<()>;
555
556 /// Updates an order in the cache with an order event.
557 ///
558 /// # Errors
559 ///
560 /// Returns an error if updating an order fails.
561 fn update_order(&self, order_event: &OrderEventAny) -> anyhow::Result<()>;
562
563 /// Updates a position in the cache.
564 ///
565 /// # Errors
566 ///
567 /// Returns an error if updating a position fails.
568 fn update_position(&self, position: &Position) -> anyhow::Result<()>;
569
570 /// Creates a snapshot of order state.
571 ///
572 /// # Errors
573 ///
574 /// Returns an error if snapshotting order state fails.
575 fn snapshot_order_state(&self, order: &OrderAny) -> anyhow::Result<()>;
576
577 /// Creates a snapshot of position state.
578 ///
579 /// # Errors
580 ///
581 /// Returns an error if snapshotting position state fails.
582 fn snapshot_position_state(
583 &self,
584 position: &Position,
585 ts_snapshot: UnixNanos,
586 unrealized_pnl: Option<Money>,
587 ) -> anyhow::Result<()>;
588
589 /// Records a heartbeat timestamp.
590 ///
591 /// # Errors
592 ///
593 /// Returns an error if heartbeat recording fails.
594 fn heartbeat(&self, timestamp: UnixNanos) -> anyhow::Result<()>;
595}