Skip to main content

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}