1use std::{collections::VecDeque, fmt::Debug, ops::ControlFlow, pin::Pin, time::Duration};
17
18use ahash::AHashMap;
19use bytes::Bytes;
20use nautilus_common::{
21 cache::{
22 CacheConfig,
23 database::{CacheDatabaseAdapter, CacheDatabaseFactory, CacheMap},
24 },
25 live::get_runtime,
26 logging::{log_task_awaiting, log_task_started, log_task_stopped},
27 signal::Signal,
28};
29use nautilus_core::{UUID4, UnixNanos};
30use nautilus_model::{
31 accounts::AccountAny,
32 data::{Bar, CustomData, DataType, FundingRateUpdate, InstrumentClose, QuoteTick, TradeTick},
33 events::{
34 AccountState, OrderEventAny, OrderFilled, OrderInitialized, OrderSnapshot,
35 position::snapshot::PositionSnapshot,
36 },
37 identifiers::{
38 AccountId, ActorId, ClientId, ClientOrderId, InstrumentId, PositionId, StrategyId,
39 TraderId, VenueOrderId,
40 },
41 instruments::{Instrument, InstrumentAny, SyntheticInstrument},
42 orderbook::OrderBook,
43 orders::{Order, OrderAny},
44 position::Position,
45 types::{Currency, Money},
46};
47use serde::{Deserialize, Serialize};
48use sqlx::{PgPool, postgres::PgConnectOptions};
49use tokio::{time::Instant, try_join};
50use ustr::Ustr;
51
52use crate::sql::{
53 pg::{connect_pg, get_postgres_connect_options},
54 queries::DatabaseQueries,
55};
56
57const CACHE_PROCESS: &str = "cache-process";
59const SCHEMA_MIGRATION_COMMAND: &str = "run `nautilus database init` to migrate";
60
61#[cfg_attr(
65 feature = "python",
66 expect(
67 clippy::unsafe_derive_deserialize,
68 reason = "config deserializes plain fields; unsafe methods come from generated PyO3 integration"
69 )
70)]
71#[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(default, deny_unknown_fields)]
73#[cfg_attr(
74 feature = "python",
75 pyo3::pyclass(module = "nautilus_trader.infrastructure", from_py_object)
76)]
77#[cfg_attr(
78 feature = "python",
79 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.infrastructure")
80)]
81pub struct PostgresCacheConfig {
82 pub host: Option<String>,
84 pub port: Option<u16>,
86 pub username: Option<String>,
88 pub password: Option<String>,
90 pub database: Option<String>,
92}
93
94impl Debug for PostgresCacheConfig {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 let redacted = self.password.as_ref().map(|_| "***");
97 f.debug_struct(stringify!(PostgresCacheConfig))
98 .field("host", &self.host)
99 .field("port", &self.port)
100 .field("username", &self.username)
101 .field("password", &redacted)
102 .field("database", &self.database)
103 .finish()
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use rstest::rstest;
110 use serde_json::json;
111
112 use super::*;
113
114 #[rstest]
115 fn test_default_postgres_cache_config() {
116 let config = PostgresCacheConfig::default();
117
118 assert_eq!(config.host, None);
119 assert_eq!(config.port, None);
120 assert_eq!(config.username, None);
121 assert_eq!(config.password, None);
122 assert_eq!(config.database, None);
123 }
124
125 #[rstest]
126 fn test_deserialize_postgres_cache_config() {
127 let config_json = json!({
128 "host": "localhost",
129 "port": 5432,
130 "username": "user",
131 "password": "pass",
132 "database": "nautilus"
133 });
134
135 let config: PostgresCacheConfig = serde_json::from_value(config_json).unwrap();
136
137 assert_eq!(config.host, Some("localhost".to_string()));
138 assert_eq!(config.port, Some(5432));
139 assert_eq!(config.username, Some("user".to_string()));
140 assert_eq!(config.password, Some("pass".to_string()));
141 assert_eq!(config.database, Some("nautilus".to_string()));
142 }
143
144 #[rstest]
145 fn test_deserialize_postgres_cache_config_rejects_type_selector() {
146 let config_json = json!({
147 "type": "postgres",
148 });
149
150 let error = serde_json::from_value::<PostgresCacheConfig>(config_json).unwrap_err();
151
152 assert!(error.to_string().contains("unknown field `type`"));
153 }
154}
155
156#[async_trait::async_trait]
157impl CacheDatabaseFactory for PostgresCacheConfig {
158 async fn create(
159 &self,
160 _trader_id: TraderId,
161 _instance_id: UUID4,
162 _config: CacheConfig,
163 ) -> anyhow::Result<Box<dyn CacheDatabaseAdapter>> {
164 let database = PostgresCacheDatabase::connect(
165 self.host.clone(),
166 self.port,
167 self.username.clone(),
168 self.password.clone(),
169 self.database.clone(),
170 )
171 .await?;
172 Ok(Box::new(database))
173 }
174}
175
176#[derive(Debug)]
177#[cfg_attr(
178 feature = "python",
179 pyo3::pyclass(module = "nautilus_trader.infrastructure")
180)]
181pub struct PostgresCacheDatabase {
182 pub pool: PgPool,
183 tx: tokio::sync::mpsc::UnboundedSender<DatabaseQuery>,
184 handle: tokio::task::JoinHandle<()>,
185}
186
187#[allow(
188 clippy::large_enum_variant,
189 reason = "variant sizes vary with feature unification; allow stays silent when the lint does not fire"
190)]
191#[derive(Debug, Clone)]
192pub enum DatabaseQuery {
193 Close,
194 Add(String, Vec<u8>),
195 AddCurrency(Currency),
196 AddInstrument(InstrumentAny),
197 AddInstrumentClose(InstrumentClose),
198 AddOrder(OrderInitialized, Option<ClientId>),
199 AddOrderSnapshot(OrderSnapshot),
200 AddPosition(PositionId, OrderFilled),
201 AddPositionSnapshot(PositionSnapshot),
202 AddAccount(AccountState, bool),
203 AddSignal(Signal),
204 AddCustom(CustomData),
205 AddQuote(QuoteTick),
206 AddTrade(TradeTick),
207 AddBar(Bar),
208 UpdateOrder(OrderEventAny),
209 UpdatePosition(OrderFilled),
210 IndexOrderPosition(ClientOrderId, PositionId),
211 IndexOrderClients(Vec<(ClientOrderId, ClientId)>),
212}
213
214impl PostgresCacheDatabase {
215 pub async fn connect(
225 host: Option<String>,
226 port: Option<u16>,
227 username: Option<String>,
228 password: Option<String>,
229 database: Option<String>,
230 ) -> Result<Self, sqlx::Error> {
231 let pg_connect_options =
232 get_postgres_connect_options(host, port, username, password, database);
233 let pool = connect_pg(pg_connect_options.clone().into()).await.unwrap();
234 check_schema_migrated(&pool).await?;
235 let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<DatabaseQuery>();
236
237 let handle = get_runtime().spawn(async move {
238 Box::pin(Self::process_commands(
239 rx,
240 pg_connect_options.clone().into(),
241 ))
242 .await;
243 });
244 Ok(Self { pool, tx, handle })
245 }
246
247 async fn process_commands(
248 mut rx: tokio::sync::mpsc::UnboundedReceiver<DatabaseQuery>,
249 pg_connect_options: PgConnectOptions,
250 ) {
251 log_task_started(CACHE_PROCESS);
252
253 let pool = connect_pg(pg_connect_options).await.unwrap();
254
255 let mut buffer: VecDeque<DatabaseQuery> = VecDeque::new();
257
258 let buffer_interval = Duration::from_millis(0);
260
261 let flush_timer = tokio::time::sleep(buffer_interval);
265 tokio::pin!(flush_timer);
266
267 loop {
269 tokio::select! {
270 maybe_msg = rx.recv() => {
271 let result = Box::pin(handle_query(
272 maybe_msg,
273 &mut buffer,
274 buffer_interval,
275 &pool,
276 ))
277 .await;
278
279 if result.is_break() {
280 break;
281 }
282 }
283 () = &mut flush_timer, if !buffer_interval.is_zero() => {
284 flush_buffer(&mut buffer, &pool, &mut flush_timer, buffer_interval).await;
285 }
286 }
287 }
288
289 if !buffer.is_empty() {
290 drain_buffer(&pool, &mut buffer).await;
291 }
292
293 log_task_stopped(CACHE_PROCESS);
294 }
295}
296
297async fn check_schema_migrated(pool: &PgPool) -> Result<(), sqlx::Error> {
306 let has_instrument_close: bool = sqlx::query_scalar(
307 "SELECT EXISTS (
308 SELECT 1
309 FROM information_schema.tables
310 WHERE table_schema = current_schema()
311 AND table_name = 'instrument_close'
312 )",
313 )
314 .fetch_one(pool)
315 .await?;
316
317 if !has_instrument_close {
318 return Err(sqlx::Error::Configuration(
319 format!(
320 "Postgres schema is out of date, missing `instrument_close` table: {SCHEMA_MIGRATION_COMMAND}"
321 )
322 .into(),
323 ));
324 }
325
326 let stale: Vec<String> = sqlx::query_scalar(
327 "SELECT table_name || '.' || column_name || ' (' || data_type || ')'
328 FROM information_schema.columns
329 WHERE table_schema = current_schema()
330 AND (table_name, column_name) IN (('order', 'avg_px'), ('order', 'slippage'))
331 AND data_type <> 'numeric'
332 ORDER BY table_name, column_name",
333 )
334 .fetch_all(pool)
335 .await?;
336
337 if !stale.is_empty() {
338 return Err(sqlx::Error::Configuration(
339 format!(
340 "Postgres schema is out of date, {} should be `numeric`: {SCHEMA_MIGRATION_COMMAND}",
341 stale.join(", "),
342 )
343 .into(),
344 ));
345 }
346
347 let missing: Vec<String> = sqlx::query_scalar(
348 "SELECT required.table_name || '.' || required.column_name
349 FROM (VALUES
350 ('instrument', 'info'),
351 ('order_event', 'released_price'),
352 ('order_event', 'protection_price'),
353 ('order_event', 'due_post_only'),
354 ('order_event', 'correction_id'),
355 ('order_event', 'is_reopened'),
356 ('order_event', 'info'),
357 ('order_event', 'causation_id'),
358 ('position_event', 'reconciliation'),
359 ('position_event', 'info'),
360 ('position_event', 'causation_id')
361 ) AS required(table_name, column_name)
362 WHERE NOT EXISTS (
363 SELECT 1
364 FROM information_schema.columns
365 WHERE table_schema = current_schema()
366 AND table_name = required.table_name
367 AND column_name = required.column_name
368 )
369 ORDER BY 1",
370 )
371 .fetch_all(pool)
372 .await?;
373
374 if missing.is_empty() {
375 return Ok(());
376 }
377
378 Err(sqlx::Error::Configuration(
379 format!(
380 "Postgres schema is out of date, missing cache columns {}: {SCHEMA_MIGRATION_COMMAND}",
381 missing.join(", "),
382 )
383 .into(),
384 ))
385}
386
387async fn handle_query(
388 maybe_msg: Option<DatabaseQuery>,
389 buffer: &mut VecDeque<DatabaseQuery>,
390 buffer_interval: Duration,
391 pool: &PgPool,
392) -> ControlFlow<()> {
393 let Some(msg) = maybe_msg else {
394 log::debug!("Command channel closed");
395 return ControlFlow::Break(());
396 };
397
398 if matches!(msg, DatabaseQuery::Close) {
399 if !buffer.is_empty() {
400 drain_buffer(pool, buffer).await;
401 }
402 return ControlFlow::Break(());
403 }
404
405 buffer.push_back(msg);
406
407 if buffer_interval.is_zero() {
408 drain_buffer(pool, buffer).await;
409 }
410
411 ControlFlow::Continue(())
412}
413
414async fn flush_buffer(
415 buffer: &mut VecDeque<DatabaseQuery>,
416 pool: &PgPool,
417 flush_timer: &mut Pin<&mut tokio::time::Sleep>,
418 buffer_interval: Duration,
419) {
420 if !buffer.is_empty() {
421 drain_buffer(pool, buffer).await;
422 }
423 flush_timer.as_mut().reset(Instant::now() + buffer_interval);
424}
425
426pub async fn get_pg_cache_database() -> anyhow::Result<PostgresCacheDatabase> {
432 let connect_options = get_postgres_connect_options(None, None, None, None, None);
433 Ok(PostgresCacheDatabase::connect(
434 Some(connect_options.host),
435 Some(connect_options.port),
436 Some(connect_options.username),
437 Some(connect_options.password),
438 Some(connect_options.database),
439 )
440 .await?)
441}
442
443#[async_trait::async_trait]
444impl CacheDatabaseAdapter for PostgresCacheDatabase {
445 fn close(&mut self) -> anyhow::Result<()> {
446 let pool = self.pool.clone();
447 let (tx, rx) = std::sync::mpsc::channel();
448
449 if let Err(e) = self.tx.send(DatabaseQuery::Close) {
451 log::warn!("Error sending close: {e:?}");
452 }
453
454 log_task_awaiting("cache-write");
455
456 tokio::task::block_in_place(|| {
457 if let Err(e) = get_runtime().block_on(&mut self.handle) {
458 log::error!("Error awaiting task 'cache-write': {e:?}");
459 }
460 });
461
462 log::debug!("Closing connection pool");
463
464 tokio::task::block_in_place(|| {
465 get_runtime().block_on(async {
466 pool.close().await;
467
468 if let Err(e) = tx.send(()) {
469 log::error!("Error closing pool: {e:?}");
470 }
471 });
472 });
473
474 log::debug!("Closed");
475
476 Ok(rx.recv()?)
477 }
478
479 fn flush(&mut self) -> anyhow::Result<()> {
480 let pool = self.pool.clone();
481 let (tx, rx) = std::sync::mpsc::channel();
482
483 tokio::task::block_in_place(|| {
484 get_runtime().block_on(async {
485 if let Err(e) = DatabaseQueries::truncate(&pool).await {
486 log::error!("Error flushing pool: {e:?}");
487 }
488
489 if let Err(e) = tx.send(()) {
490 log::error!("Error sending flush result: {e:?}");
491 }
492 });
493 });
494
495 Ok(rx.recv()?)
496 }
497
498 async fn load_all(&self) -> anyhow::Result<CacheMap> {
499 let currencies = self.load_currencies().await?;
500 for currency in currencies.values() {
501 Currency::register(*currency, false)?;
502 }
503
504 let (instruments, instrument_closes, synthetics, accounts, orders, positions) = try_join!(
505 self.load_instruments(),
506 self.load_instrument_closes(),
507 self.load_synthetics(),
508 self.load_accounts(),
509 self.load_orders(),
510 self.load_positions()
511 )
512 .map_err(|e| anyhow::anyhow!("Error loading cache data: {e}"))?;
513
514 let greeks = AHashMap::new();
517 let yield_curves = AHashMap::new();
518
519 Ok(CacheMap {
520 currencies,
521 instruments,
522 instrument_closes,
523 synthetics,
524 accounts,
525 orders,
526 positions,
527 greeks,
528 yield_curves,
529 })
530 }
531
532 fn load(&self) -> anyhow::Result<AHashMap<String, Bytes>> {
533 let pool = self.pool.clone();
534 let (tx, rx) = std::sync::mpsc::channel();
535
536 tokio::spawn(async move {
537 let result = DatabaseQueries::load(&pool).await;
538 match result {
539 Ok(items) => {
540 let mapping = items
541 .into_iter()
542 .map(|(k, v)| (k, Bytes::from(v)))
543 .collect();
544
545 if let Err(e) = tx.send(mapping) {
546 log::error!("Failed to send general items: {e:?}");
547 }
548 }
549 Err(e) => {
550 log::error!("Failed to load general items: {e:?}");
551 if let Err(e) = tx.send(AHashMap::new()) {
552 log::error!("Failed to send empty general items: {e:?}");
553 }
554 }
555 }
556 });
557 Ok(rx.recv()?)
558 }
559
560 async fn load_currencies(&self) -> anyhow::Result<AHashMap<Ustr, Currency>> {
561 let pool = self.pool.clone();
562 let (tx, rx) = std::sync::mpsc::channel();
563
564 tokio::spawn(async move {
565 let result = DatabaseQueries::load_currencies(&pool).await;
566 match result {
567 Ok(currencies) => {
568 let mapping = currencies
569 .into_iter()
570 .map(|currency| (currency.code, currency))
571 .collect();
572
573 if let Err(e) = tx.send(mapping) {
574 log::error!("Failed to send currencies: {e:?}");
575 }
576 }
577 Err(e) => {
578 log::error!("Failed to load currencies: {e:?}");
579 if let Err(e) = tx.send(AHashMap::new()) {
580 log::error!("Failed to send empty currencies: {e:?}");
581 }
582 }
583 }
584 });
585 Ok(rx.recv()?)
586 }
587
588 async fn load_instruments(&self) -> anyhow::Result<AHashMap<InstrumentId, InstrumentAny>> {
589 let pool = self.pool.clone();
590 let (tx, rx) = std::sync::mpsc::channel();
591
592 tokio::spawn(async move {
593 let result = DatabaseQueries::load_instruments(&pool).await;
594 match result {
595 Ok(instruments) => {
596 let mapping = instruments
597 .into_iter()
598 .map(|instrument| (instrument.id(), instrument))
599 .collect();
600
601 if let Err(e) = tx.send(mapping) {
602 log::error!("Failed to send instruments: {e:?}");
603 }
604 }
605 Err(e) => {
606 log::error!("Failed to load instruments: {e:?}");
607 if let Err(e) = tx.send(AHashMap::new()) {
608 log::error!("Failed to send empty instruments: {e:?}");
609 }
610 }
611 }
612 });
613 Ok(rx.recv()?)
614 }
615
616 async fn load_instrument_closes(
617 &self,
618 ) -> anyhow::Result<AHashMap<InstrumentId, InstrumentClose>> {
619 Ok(DatabaseQueries::load_instrument_closes(&self.pool)
620 .await?
621 .into_iter()
622 .map(|close| (close.instrument_id, close))
623 .collect())
624 }
625
626 async fn load_synthetics(&self) -> anyhow::Result<AHashMap<InstrumentId, SyntheticInstrument>> {
627 Ok(AHashMap::new())
628 }
629
630 async fn load_accounts(&self) -> anyhow::Result<AHashMap<AccountId, AccountAny>> {
631 let pool = self.pool.clone();
632 let (tx, rx) = std::sync::mpsc::channel();
633
634 tokio::spawn(async move {
635 let result = DatabaseQueries::load_accounts(&pool).await;
636 match result {
637 Ok(accounts) => {
638 let mapping = accounts
639 .into_iter()
640 .map(|account| (account.id(), account))
641 .collect();
642
643 if let Err(e) = tx.send(mapping) {
644 log::error!("Failed to send accounts: {e:?}");
645 }
646 }
647 Err(e) => {
648 log::error!("Failed to load accounts: {e:?}");
649 if let Err(e) = tx.send(AHashMap::new()) {
650 log::error!("Failed to send empty accounts: {e:?}");
651 }
652 }
653 }
654 });
655 Ok(rx.recv()?)
656 }
657
658 async fn load_orders(&self) -> anyhow::Result<AHashMap<ClientOrderId, OrderAny>> {
659 let pool = self.pool.clone();
660 let (tx, rx) = std::sync::mpsc::channel();
661
662 tokio::spawn(async move {
663 let result = DatabaseQueries::load_orders(&pool).await;
664 match result {
665 Ok(orders) => {
666 let mapping = orders
667 .into_iter()
668 .map(|order| (order.client_order_id(), order))
669 .collect();
670
671 if let Err(e) = tx.send(mapping) {
672 log::error!("Failed to send orders: {e:?}");
673 }
674 }
675 Err(e) => {
676 log::error!("Failed to load orders: {e:?}");
677 if let Err(e) = tx.send(AHashMap::new()) {
678 log::error!("Failed to send empty orders: {e:?}");
679 }
680 }
681 }
682 });
683 Ok(rx.recv()?)
684 }
685
686 async fn load_positions(&self) -> anyhow::Result<AHashMap<PositionId, Position>> {
687 let pool = self.pool.clone();
688 let (tx, rx) = std::sync::mpsc::channel();
689
690 tokio::spawn(async move {
691 let result = DatabaseQueries::load_positions(&pool)
692 .await
693 .map(|positions| {
694 positions
695 .into_iter()
696 .map(|position| (position.id, position))
697 .collect()
698 });
699
700 if let Err(e) = tx.send(result) {
701 log::error!("Failed to send positions: {e:?}");
702 }
703 });
704 rx.recv()?
705 }
706
707 fn load_index_order_position(&self) -> anyhow::Result<AHashMap<ClientOrderId, PositionId>> {
708 let pool = self.pool.clone();
709 let (tx, rx) = std::sync::mpsc::channel();
710
711 tokio::spawn(async move {
712 let result = DatabaseQueries::load_index_order_position(&pool).await;
713 match result {
714 Ok(index) => {
715 if let Err(e) = tx.send(index) {
716 log::error!("Failed to send load_index_order_position result: {e:?}");
717 }
718 }
719 Err(e) => {
720 log::error!("Failed to run query load_index_order_position: {e:?}");
721 if let Err(e) = tx.send(AHashMap::new()) {
722 log::error!("Failed to send empty load_index_order_position result: {e:?}");
723 }
724 }
725 }
726 });
727 Ok(rx.recv()?)
728 }
729
730 fn load_index_order_client(&self) -> anyhow::Result<AHashMap<ClientOrderId, ClientId>> {
731 let pool = self.pool.clone();
732 let (tx, rx) = std::sync::mpsc::channel();
733
734 tokio::spawn(async move {
735 let result = DatabaseQueries::load_distinct_order_event_client_ids(&pool).await;
736 match result {
737 Ok(currency) => {
738 if let Err(e) = tx.send(currency) {
739 log::error!("Failed to send load_index_order_client result: {e:?}");
740 }
741 }
742 Err(e) => {
743 log::error!("Failed to run query load_distinct_order_event_client_ids: {e:?}");
744 if let Err(e) = tx.send(AHashMap::new()) {
745 log::error!("Failed to send empty load_index_order_client result: {e:?}");
746 }
747 }
748 }
749 });
750 Ok(rx.recv()?)
751 }
752
753 async fn load_currency(&self, code: &Ustr) -> anyhow::Result<Option<Currency>> {
754 let pool = self.pool.clone();
755 let code = code.to_owned(); let (tx, rx) = std::sync::mpsc::channel();
757
758 tokio::spawn(async move {
759 let result = DatabaseQueries::load_currency(&pool, &code).await;
760 match result {
761 Ok(currency) => {
762 if let Err(e) = tx.send(currency) {
763 log::error!("Failed to send currency {code}: {e:?}");
764 }
765 }
766 Err(e) => {
767 log::error!("Failed to load currency {code}: {e:?}");
768 if let Err(e) = tx.send(None) {
769 log::error!("Failed to send None for currency {code}: {e:?}");
770 }
771 }
772 }
773 });
774 Ok(rx.recv()?)
775 }
776
777 async fn load_instrument(
778 &self,
779 instrument_id: &InstrumentId,
780 ) -> anyhow::Result<Option<InstrumentAny>> {
781 let pool = self.pool.clone();
782 let instrument_id = instrument_id.to_owned(); let (tx, rx) = std::sync::mpsc::channel();
784
785 tokio::spawn(async move {
786 let result = DatabaseQueries::load_instrument(&pool, &instrument_id).await;
787 match result {
788 Ok(instrument) => {
789 if let Err(e) = tx.send(instrument) {
790 log::error!("Failed to send instrument {instrument_id}: {e:?}");
791 }
792 }
793 Err(e) => {
794 log::error!("Failed to load instrument {instrument_id}: {e:?}");
795 if let Err(e) = tx.send(None) {
796 log::error!("Failed to send None for instrument {instrument_id}: {e:?}");
797 }
798 }
799 }
800 });
801 Ok(rx.recv()?)
802 }
803
804 async fn load_synthetic(
805 &self,
806 instrument_id: &InstrumentId,
807 ) -> anyhow::Result<Option<SyntheticInstrument>> {
808 anyhow::bail!(
809 "load_synthetic not implemented for PostgreSQL cache adapter: {instrument_id}"
810 )
811 }
812
813 async fn load_account(&self, account_id: &AccountId) -> anyhow::Result<Option<AccountAny>> {
814 let pool = self.pool.clone();
815 let account_id = account_id.to_owned();
816 let (tx, rx) = std::sync::mpsc::channel();
817
818 tokio::spawn(async move {
819 let result = DatabaseQueries::load_account(&pool, &account_id).await;
820 match result {
821 Ok(account) => {
822 if let Err(e) = tx.send(account) {
823 log::error!("Failed to send account {account_id}: {e:?}");
824 }
825 }
826 Err(e) => {
827 log::error!("Failed to load account {account_id}: {e:?}");
828 if let Err(e) = tx.send(None) {
829 log::error!("Failed to send None for account {account_id}: {e:?}");
830 }
831 }
832 }
833 });
834 Ok(rx.recv()?)
835 }
836
837 async fn load_order(
838 &self,
839 client_order_id: &ClientOrderId,
840 ) -> anyhow::Result<Option<OrderAny>> {
841 let pool = self.pool.clone();
842 let client_order_id = client_order_id.to_owned();
843 let (tx, rx) = std::sync::mpsc::channel();
844
845 tokio::spawn(async move {
846 let result = DatabaseQueries::load_order(&pool, &client_order_id).await;
847 match result {
848 Ok(order) => {
849 if let Err(e) = tx.send(order) {
850 log::error!("Failed to send order {client_order_id}: {e:?}");
851 }
852 }
853 Err(e) => {
854 log::error!("Failed to load order {client_order_id}: {e:?}");
855 let _ = tx.send(None);
856 }
857 }
858 });
859 Ok(rx.recv()?)
860 }
861
862 async fn load_position(&self, position_id: &PositionId) -> anyhow::Result<Option<Position>> {
863 let pool = self.pool.clone();
864 let position_id = position_id.to_owned();
865 let (tx, rx) = std::sync::mpsc::channel();
866
867 tokio::spawn(async move {
868 let result = DatabaseQueries::load_position(&pool, &position_id).await;
869 if let Err(e) = tx.send(result) {
870 log::error!("Failed to send position {position_id}: {e:?}");
871 }
872 });
873 rx.recv()?
874 }
875
876 fn load_actor(&self, actor_id: &ActorId) -> anyhow::Result<AHashMap<String, Bytes>> {
877 anyhow::bail!("load_actor not implemented for PostgreSQL cache adapter: {actor_id}")
878 }
879
880 fn delete_actor(&self, _actor_id: &ActorId) -> anyhow::Result<()> {
881 todo!()
882 }
883
884 fn load_strategy(&self, strategy_id: &StrategyId) -> anyhow::Result<AHashMap<String, Bytes>> {
885 anyhow::bail!("load_strategy not implemented for PostgreSQL cache adapter: {strategy_id}")
886 }
887
888 fn delete_strategy(&self, _strategy_id: &StrategyId) -> anyhow::Result<()> {
889 todo!()
890 }
891
892 fn delete_order(&self, client_order_id: &ClientOrderId) -> anyhow::Result<()> {
893 anyhow::bail!(
894 "delete_order not implemented for PostgreSQL cache adapter: {client_order_id}"
895 )
896 }
897
898 fn delete_position(&self, position_id: &PositionId) -> anyhow::Result<()> {
899 anyhow::bail!("delete_position not implemented for PostgreSQL cache adapter: {position_id}")
900 }
901
902 fn delete_account_event(&self, account_id: &AccountId, event_id: &str) -> anyhow::Result<()> {
903 anyhow::bail!(
904 "delete_account_event not implemented for PostgreSQL cache adapter: {account_id}, {event_id}"
905 )
906 }
907
908 fn add(&self, key: String, value: Bytes) -> anyhow::Result<()> {
909 let query = DatabaseQuery::Add(key, value.into());
910 self.tx
911 .send(query)
912 .map_err(|e| anyhow::anyhow!("Failed to send query to database message handler: {e}"))
913 }
914
915 fn add_currency(&self, currency: &Currency) -> anyhow::Result<()> {
916 let query = DatabaseQuery::AddCurrency(*currency);
917 self.tx.send(query).map_err(|e| {
918 anyhow::anyhow!("Failed to query add_currency to database message handler: {e}")
919 })
920 }
921
922 fn add_instrument(&self, instrument: &InstrumentAny) -> anyhow::Result<()> {
923 let query = DatabaseQuery::AddInstrument(instrument.clone());
924 self.tx.send(query).map_err(|e| {
925 anyhow::anyhow!("Failed to send query add_instrument to database message handler: {e}")
926 })
927 }
928
929 fn add_instrument_close(&self, close: &InstrumentClose) -> anyhow::Result<()> {
930 self.tx
931 .send(DatabaseQuery::AddInstrumentClose(*close))
932 .map_err(|e| {
933 anyhow::anyhow!(
934 "Failed to send query add_instrument_close to database message handler: {e}"
935 )
936 })
937 }
938
939 fn add_synthetic(&self, _synthetic: &SyntheticInstrument) -> anyhow::Result<()> {
940 todo!()
941 }
942
943 fn add_account(&self, account: &AccountAny) -> anyhow::Result<()> {
944 let query = DatabaseQuery::AddAccount(account_last_event(account)?, false);
945 self.tx.send(query).map_err(|e| {
946 anyhow::anyhow!("Failed to send query add_account to database message handler: {e}")
947 })
948 }
949
950 fn add_order(&self, order: &OrderAny, client_id: Option<ClientId>) -> anyhow::Result<()> {
951 let query = DatabaseQuery::AddOrder(order_initialized_event(order), client_id);
952 self.tx.send(query).map_err(|e| {
953 anyhow::anyhow!("Failed to send query add_order to database message handler: {e}")
954 })
955 }
956
957 fn add_order_snapshot(&self, snapshot: &OrderSnapshot) -> anyhow::Result<()> {
958 let query = DatabaseQuery::AddOrderSnapshot(snapshot.to_owned());
959 self.tx.send(query).map_err(|e| {
960 anyhow::anyhow!(
961 "Failed to send query add_order_snapshot to database message handler: {e}"
962 )
963 })
964 }
965
966 fn add_position(&self, position: &Position) -> anyhow::Result<()> {
967 let event = position_last_event(position)?;
968 let query = DatabaseQuery::AddPosition(position.id, event);
969 self.tx.send(query).map_err(|e| {
970 anyhow::anyhow!("Failed to send query add_position to database message handler: {e}")
971 })
972 }
973
974 fn add_position_snapshot(&self, snapshot: &PositionSnapshot) -> anyhow::Result<()> {
975 let query = DatabaseQuery::AddPositionSnapshot(snapshot.to_owned());
976 self.tx.send(query).map_err(|e| {
977 anyhow::anyhow!(
978 "Failed to send query add_position_snapshot to database message handler: {e}"
979 )
980 })
981 }
982
983 fn add_order_book(&self, _order_book: &OrderBook) -> anyhow::Result<()> {
984 todo!()
985 }
986
987 fn add_quote(&self, quote: &QuoteTick) -> anyhow::Result<()> {
988 let query = DatabaseQuery::AddQuote(quote.to_owned());
989 self.tx.send(query).map_err(|e| {
990 anyhow::anyhow!("Failed to send query add_quote to database message handler: {e}")
991 })
992 }
993
994 fn load_quotes(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<QuoteTick>> {
995 let pool = self.pool.clone();
996 let instrument_id = instrument_id.to_owned();
997 let (tx, rx) = std::sync::mpsc::channel();
998
999 tokio::spawn(async move {
1000 let result = DatabaseQueries::load_quotes(&pool, &instrument_id).await;
1001 match result {
1002 Ok(quotes) => {
1003 if let Err(e) = tx.send(quotes) {
1004 log::error!("Failed to send quotes for instrument {instrument_id}: {e:?}");
1005 }
1006 }
1007 Err(e) => {
1008 log::error!("Failed to load quotes for instrument {instrument_id}: {e:?}");
1009 if let Err(e) = tx.send(Vec::new()) {
1010 log::error!(
1011 "Failed to send empty quotes for instrument {instrument_id}: {e:?}"
1012 );
1013 }
1014 }
1015 }
1016 });
1017 Ok(rx.recv()?)
1018 }
1019
1020 fn add_trade(&self, trade: &TradeTick) -> anyhow::Result<()> {
1021 let query = DatabaseQuery::AddTrade(trade.to_owned());
1022 self.tx.send(query).map_err(|e| {
1023 anyhow::anyhow!("Failed to send query add_trade to database message handler: {e}")
1024 })
1025 }
1026
1027 fn load_trades(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<TradeTick>> {
1028 let pool = self.pool.clone();
1029 let instrument_id = instrument_id.to_owned();
1030 let (tx, rx) = std::sync::mpsc::channel();
1031
1032 tokio::spawn(async move {
1033 let result = DatabaseQueries::load_trades(&pool, &instrument_id).await;
1034 match result {
1035 Ok(trades) => {
1036 if let Err(e) = tx.send(trades) {
1037 log::error!("Failed to send trades for instrument {instrument_id}: {e:?}");
1038 }
1039 }
1040 Err(e) => {
1041 log::error!("Failed to load trades for instrument {instrument_id}: {e:?}");
1042 if let Err(e) = tx.send(Vec::new()) {
1043 log::error!(
1044 "Failed to send empty trades for instrument {instrument_id}: {e:?}"
1045 );
1046 }
1047 }
1048 }
1049 });
1050 Ok(rx.recv()?)
1051 }
1052
1053 fn add_funding_rate(&self, _funding_rate: &FundingRateUpdate) -> anyhow::Result<()> {
1054 anyhow::bail!("add_funding_rate not implemented for PostgreSQL cache adapter")
1055 }
1056
1057 fn load_funding_rates(
1058 &self,
1059 _instrument_id: &InstrumentId,
1060 ) -> anyhow::Result<Vec<FundingRateUpdate>> {
1061 anyhow::bail!("load_funding_rates not implemented for PostgreSQL cache adapter")
1062 }
1063
1064 fn add_bar(&self, bar: &Bar) -> anyhow::Result<()> {
1065 let query = DatabaseQuery::AddBar(bar.to_owned());
1066 self.tx.send(query).map_err(|e| {
1067 anyhow::anyhow!("Failed to send query add_bar to database message handler: {e}")
1068 })
1069 }
1070
1071 fn load_bars(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<Bar>> {
1072 let pool = self.pool.clone();
1073 let instrument_id = instrument_id.to_owned();
1074 let (tx, rx) = std::sync::mpsc::channel();
1075
1076 tokio::spawn(async move {
1077 let result = DatabaseQueries::load_bars(&pool, &instrument_id).await;
1078 match result {
1079 Ok(bars) => {
1080 if let Err(e) = tx.send(bars) {
1081 log::error!("Failed to send bars for instrument {instrument_id}: {e:?}");
1082 }
1083 }
1084 Err(e) => {
1085 log::error!("Failed to load bars for instrument {instrument_id}: {e:?}");
1086 if let Err(e) = tx.send(Vec::new()) {
1087 log::error!(
1088 "Failed to send empty bars for instrument {instrument_id}: {e:?}"
1089 );
1090 }
1091 }
1092 }
1093 });
1094 Ok(rx.recv()?)
1095 }
1096
1097 fn add_signal(&self, signal: &Signal) -> anyhow::Result<()> {
1098 let query = DatabaseQuery::AddSignal(signal.to_owned());
1099 self.tx.send(query).map_err(|e| {
1100 anyhow::anyhow!("Failed to send query add_signal to database message handler: {e}")
1101 })
1102 }
1103
1104 fn load_signals(&self, name: &str) -> anyhow::Result<Vec<Signal>> {
1105 let pool = self.pool.clone();
1106 let name = name.to_owned();
1107 let (tx, rx) = std::sync::mpsc::channel();
1108
1109 tokio::spawn(async move {
1110 let result = DatabaseQueries::load_signals(&pool, &name).await;
1111 match result {
1112 Ok(signals) => {
1113 if let Err(e) = tx.send(signals) {
1114 log::error!("Failed to send signals for '{name}': {e:?}");
1115 }
1116 }
1117 Err(e) => {
1118 log::error!("Failed to load signals for '{name}': {e:?}");
1119 if let Err(e) = tx.send(Vec::new()) {
1120 log::error!("Failed to send empty signals for '{name}': {e:?}");
1121 }
1122 }
1123 }
1124 });
1125 Ok(rx.recv()?)
1126 }
1127
1128 fn add_custom_data(&self, data: &CustomData) -> anyhow::Result<()> {
1129 let query = DatabaseQuery::AddCustom(data.to_owned());
1130 self.tx.send(query).map_err(|e| {
1131 anyhow::anyhow!("Failed to send query add_signal to database message handler: {e}")
1132 })
1133 }
1134
1135 fn load_custom_data(&self, data_type: &DataType) -> anyhow::Result<Vec<CustomData>> {
1136 let pool = self.pool.clone();
1137 let data_type = data_type.to_owned();
1138 let (tx, rx) = std::sync::mpsc::channel();
1139
1140 tokio::spawn(async move {
1141 let result = DatabaseQueries::load_custom_data(&pool, &data_type).await;
1142 match result {
1143 Ok(signals) => {
1144 if let Err(e) = tx.send(signals) {
1145 log::error!("Failed to send custom data for '{data_type}': {e:?}");
1146 }
1147 }
1148 Err(e) => {
1149 log::error!("Failed to load custom data for '{data_type}': {e:?}");
1150 if let Err(e) = tx.send(Vec::new()) {
1151 log::error!("Failed to send empty custom data for '{data_type}': {e:?}");
1152 }
1153 }
1154 }
1155 });
1156 Ok(rx.recv()?)
1157 }
1158
1159 fn load_order_snapshot(
1160 &self,
1161 client_order_id: &ClientOrderId,
1162 ) -> anyhow::Result<Option<OrderSnapshot>> {
1163 let pool = self.pool.clone();
1164 let client_order_id = client_order_id.to_owned();
1165 let (tx, rx) = std::sync::mpsc::channel();
1166
1167 tokio::spawn(async move {
1168 let result = DatabaseQueries::load_order_snapshot(&pool, &client_order_id).await;
1169 match result {
1170 Ok(snapshot) => {
1171 if let Err(e) = tx.send(snapshot) {
1172 log::error!("Failed to send order snapshot {client_order_id}: {e:?}");
1173 }
1174 }
1175 Err(e) => {
1176 log::error!("Failed to load order snapshot {client_order_id}: {e:?}");
1177 if let Err(e) = tx.send(None) {
1178 log::error!(
1179 "Failed to send None for order snapshot {client_order_id}: {e:?}"
1180 );
1181 }
1182 }
1183 }
1184 });
1185 Ok(rx.recv()?)
1186 }
1187
1188 fn load_position_snapshot(
1189 &self,
1190 position_id: &PositionId,
1191 ) -> anyhow::Result<Option<PositionSnapshot>> {
1192 let pool = self.pool.clone();
1193 let position_id = position_id.to_owned();
1194 let (tx, rx) = std::sync::mpsc::channel();
1195
1196 tokio::spawn(async move {
1197 let result = DatabaseQueries::load_position_snapshot(&pool, &position_id).await;
1198 match result {
1199 Ok(snapshot) => {
1200 if let Err(e) = tx.send(snapshot) {
1201 log::error!("Failed to send position snapshot {position_id}: {e:?}");
1202 }
1203 }
1204 Err(e) => {
1205 log::error!("Failed to load position snapshot {position_id}: {e:?}");
1206 if let Err(e) = tx.send(None) {
1207 log::error!(
1208 "Failed to send None for position snapshot {position_id}: {e:?}"
1209 );
1210 }
1211 }
1212 }
1213 });
1214 Ok(rx.recv()?)
1215 }
1216
1217 fn index_venue_order_id(
1218 &self,
1219 _client_order_id: ClientOrderId,
1220 _venue_order_id: VenueOrderId,
1221 ) -> anyhow::Result<()> {
1222 todo!()
1223 }
1224
1225 fn index_order_position(
1226 &self,
1227 client_order_id: ClientOrderId,
1228 position_id: PositionId,
1229 ) -> anyhow::Result<()> {
1230 let query = DatabaseQuery::IndexOrderPosition(client_order_id, position_id);
1231 self.tx.send(query).map_err(|e| {
1232 anyhow::anyhow!(
1233 "Failed to send query index_order_position to database message handler: {e}"
1234 )
1235 })
1236 }
1237
1238 fn index_order_clients(&self, claims: &[(ClientOrderId, ClientId)]) -> anyhow::Result<()> {
1239 if claims.is_empty() {
1240 return Ok(());
1241 }
1242
1243 let query = DatabaseQuery::IndexOrderClients(claims.to_vec());
1244 self.tx.send(query).map_err(|e| {
1245 anyhow::anyhow!(
1246 "Failed to send query index_order_clients to database message handler: {e}"
1247 )
1248 })
1249 }
1250
1251 fn update_actor(
1252 &self,
1253 actor_id: &ActorId,
1254 _state: &AHashMap<String, Bytes>,
1255 ) -> anyhow::Result<()> {
1256 anyhow::bail!("update_actor not implemented for PostgreSQL cache adapter: {actor_id}")
1257 }
1258
1259 fn update_strategy(
1260 &self,
1261 strategy_id: &StrategyId,
1262 _state: &AHashMap<String, Bytes>,
1263 ) -> anyhow::Result<()> {
1264 anyhow::bail!("update_strategy not implemented for PostgreSQL cache adapter: {strategy_id}")
1265 }
1266
1267 fn update_account(&self, account: &AccountAny) -> anyhow::Result<()> {
1268 let query = DatabaseQuery::AddAccount(account_last_event(account)?, true);
1269 self.tx.send(query).map_err(|e| {
1270 anyhow::anyhow!("Failed to send query add_account to database message handler: {e}")
1271 })
1272 }
1273
1274 fn update_order(&self, event: &OrderEventAny) -> anyhow::Result<()> {
1275 let query = DatabaseQuery::UpdateOrder(event.clone());
1276 self.tx.send(query).map_err(|e| {
1277 anyhow::anyhow!("Failed to send query update_order to database message handler: {e}")
1278 })
1279 }
1280
1281 fn update_position(&self, position: &Position) -> anyhow::Result<()> {
1282 let query = if position.fill_voids.is_empty() {
1283 DatabaseQuery::UpdatePosition(position_last_event(position)?)
1284 } else {
1285 DatabaseQuery::AddPositionSnapshot(PositionSnapshot::from_replay_state(position, None))
1286 };
1287 self.tx.send(query).map_err(|e| {
1288 anyhow::anyhow!("Failed to send query update_position to database message handler: {e}")
1289 })
1290 }
1291
1292 fn snapshot_order_state(&self, order: &OrderAny) -> anyhow::Result<()> {
1293 let snapshot = OrderSnapshot::from(order.clone());
1294 self.add_order_snapshot(&snapshot)
1295 }
1296
1297 fn snapshot_position_state(
1298 &self,
1299 position: &Position,
1300 ts_snapshot: UnixNanos,
1301 unrealized_pnl: Option<Money>,
1302 ) -> anyhow::Result<()> {
1303 let mut snapshot = if position.fill_voids.is_empty() {
1304 PositionSnapshot::from(position, unrealized_pnl)
1305 } else {
1306 PositionSnapshot::from_replay_state(position, unrealized_pnl)
1307 };
1308 snapshot.ts_init = ts_snapshot;
1309 self.add_position_snapshot(&snapshot)
1310 }
1311
1312 fn heartbeat(&self, _timestamp: UnixNanos) -> anyhow::Result<()> {
1313 todo!()
1314 }
1315}
1316
1317fn account_last_event(account: &AccountAny) -> anyhow::Result<AccountState> {
1318 account
1319 .last_event()
1320 .ok_or_else(|| anyhow::anyhow!("Cannot persist account with no events: {}", account.id()))
1321}
1322
1323fn order_initialized_event(order: &OrderAny) -> OrderInitialized {
1324 order.init_event().clone()
1325}
1326
1327fn position_last_event(position: &Position) -> anyhow::Result<OrderFilled> {
1328 position
1329 .last_event()
1330 .ok_or_else(|| anyhow::anyhow!("Cannot persist position with no events: {}", position.id))
1331}
1332
1333#[expect(
1334 clippy::too_many_lines,
1335 reason = "database command dispatch enumerates each cache query variant explicitly"
1336)]
1337async fn drain_buffer(pool: &PgPool, buffer: &mut VecDeque<DatabaseQuery>) {
1338 for cmd in buffer.drain(..) {
1339 let result: anyhow::Result<()> = match cmd {
1340 DatabaseQuery::Close => Ok(()),
1341 DatabaseQuery::Add(key, value) => DatabaseQueries::add(pool, key, value).await,
1342 DatabaseQuery::AddCurrency(currency) => {
1343 DatabaseQueries::add_currency(pool, currency).await
1344 }
1345 DatabaseQuery::AddInstrument(instrument_any) => match instrument_any {
1346 InstrumentAny::Betting(instrument) => {
1347 DatabaseQueries::add_instrument(pool, "BETTING", Box::new(instrument)).await
1348 }
1349 InstrumentAny::BinaryOption(instrument) => {
1350 DatabaseQueries::add_instrument(pool, "BINARY_OPTION", Box::new(instrument))
1351 .await
1352 }
1353 InstrumentAny::CryptoFuture(instrument) => {
1354 DatabaseQueries::add_instrument(pool, "CRYPTO_FUTURE", Box::new(instrument))
1355 .await
1356 }
1357 InstrumentAny::CryptoFuturesSpread(instrument) => {
1358 DatabaseQueries::add_instrument(
1359 pool,
1360 "CRYPTO_FUTURES_SPREAD",
1361 Box::new(instrument),
1362 )
1363 .await
1364 }
1365 InstrumentAny::CryptoOption(instrument) => {
1366 DatabaseQueries::add_instrument(pool, "CRYPTO_OPTION", Box::new(instrument))
1367 .await
1368 }
1369 InstrumentAny::CryptoOptionSpread(instrument) => {
1370 DatabaseQueries::add_instrument(
1371 pool,
1372 "CRYPTO_OPTION_SPREAD",
1373 Box::new(instrument),
1374 )
1375 .await
1376 }
1377 InstrumentAny::CryptoPerpetual(instrument) => {
1378 DatabaseQueries::add_instrument(pool, "CRYPTO_PERPETUAL", Box::new(instrument))
1379 .await
1380 }
1381 InstrumentAny::CurrencyPair(instrument) => {
1382 DatabaseQueries::add_instrument(pool, "CURRENCY_PAIR", Box::new(instrument))
1383 .await
1384 }
1385 InstrumentAny::Equity(equity) => {
1386 DatabaseQueries::add_instrument(pool, "EQUITY", Box::new(equity)).await
1387 }
1388 InstrumentAny::FuturesContract(instrument) => {
1389 DatabaseQueries::add_instrument(pool, "FUTURES_CONTRACT", Box::new(instrument))
1390 .await
1391 }
1392 InstrumentAny::FuturesSpread(instrument) => {
1393 DatabaseQueries::add_instrument(pool, "FUTURES_SPREAD", Box::new(instrument))
1394 .await
1395 }
1396 InstrumentAny::OptionContract(instrument) => {
1397 DatabaseQueries::add_instrument(pool, "OPTION_CONTRACT", Box::new(instrument))
1398 .await
1399 }
1400 InstrumentAny::Commodity(instrument) => {
1401 DatabaseQueries::add_instrument(pool, "COMMODITY", Box::new(instrument)).await
1402 }
1403 InstrumentAny::IndexInstrument(instrument) => {
1404 DatabaseQueries::add_instrument(pool, "INDEX_INSTRUMENT", Box::new(instrument))
1405 .await
1406 }
1407 InstrumentAny::Cfd(instrument) => {
1408 DatabaseQueries::add_instrument(pool, "CFD", Box::new(instrument)).await
1409 }
1410 InstrumentAny::OptionSpread(instrument) => {
1411 DatabaseQueries::add_instrument(pool, "OPTION_SPREAD", Box::new(instrument))
1412 .await
1413 }
1414 InstrumentAny::PerpetualContract(instrument) => {
1415 DatabaseQueries::add_instrument(
1416 pool,
1417 "PERPETUAL_CONTRACT",
1418 Box::new(instrument),
1419 )
1420 .await
1421 }
1422 InstrumentAny::TokenizedAsset(instrument) => {
1423 DatabaseQueries::add_instrument(pool, "TOKENIZED_ASSET", Box::new(instrument))
1424 .await
1425 }
1426 },
1427 DatabaseQuery::AddInstrumentClose(close) => {
1428 DatabaseQueries::add_instrument_close(pool, &close).await
1429 }
1430 DatabaseQuery::AddOrder(event, client_id) => {
1431 DatabaseQueries::add_order(pool, event, client_id).await
1432 }
1433 DatabaseQuery::AddOrderSnapshot(snapshot) => {
1434 DatabaseQueries::add_order_snapshot(pool, snapshot).await
1435 }
1436 DatabaseQuery::AddPosition(position_id, event) => {
1437 DatabaseQueries::add_position(pool, position_id, &event).await
1438 }
1439 DatabaseQuery::AddPositionSnapshot(snapshot) => {
1440 DatabaseQueries::add_position_snapshot(pool, snapshot).await
1441 }
1442 DatabaseQuery::AddAccount(event, updated) => {
1443 DatabaseQueries::add_account(pool, updated, event).await
1444 }
1445 DatabaseQuery::AddSignal(signal) => DatabaseQueries::add_signal(pool, &signal).await,
1446 DatabaseQuery::AddCustom(data) => DatabaseQueries::add_custom_data(pool, &data).await,
1447 DatabaseQuery::AddQuote(quote) => DatabaseQueries::add_quote(pool, "e).await,
1448 DatabaseQuery::AddTrade(trade) => DatabaseQueries::add_trade(pool, &trade).await,
1449 DatabaseQuery::AddBar(bar) => DatabaseQueries::add_bar(pool, &bar).await,
1450 DatabaseQuery::UpdateOrder(event) => {
1451 DatabaseQueries::add_order_event(pool, event.into_boxed(), None).await
1452 }
1453 DatabaseQuery::UpdatePosition(event) => {
1454 DatabaseQueries::update_position(pool, &event).await
1455 }
1456 DatabaseQuery::IndexOrderPosition(client_order_id, position_id) => {
1457 DatabaseQueries::index_order_position(pool, client_order_id, position_id).await
1458 }
1459 DatabaseQuery::IndexOrderClients(claims) => {
1460 DatabaseQueries::index_order_clients(pool, &claims).await
1461 }
1462 };
1463
1464 if let Err(e) = result {
1465 log::error!("Error on query: {e:?}");
1466 }
1467 }
1468}