1pub mod config;
21pub mod database;
22pub mod fifo;
23pub mod quote;
24pub mod refs;
25
26mod api;
27mod bounded;
28mod error;
29mod filter;
30mod index;
31mod position;
32mod view;
33
34#[cfg(test)]
35mod tests;
36
37use std::{
38 borrow::Cow,
39 cell::RefCell,
40 cmp::{Ordering, Reverse},
41 fmt::{Debug, Display},
42 rc::Rc,
43 time::{SystemTime, UNIX_EPOCH},
44};
45
46use ahash::{AHashMap, AHashSet};
47pub use api::CacheApi; use bounded::BoundedVecDeque;
49use bytes::Bytes;
50pub use config::CacheConfig; use database::{CacheDatabaseAdapter, CacheMap, register_loaded_currencies};
52pub use error::{
53 ACCOUNT_NOT_FOUND, AccountLookupError, CURRENCY_NOT_FOUND, CurrencyLookupError,
54 INSTRUMENT_NOT_FOUND, InstrumentLookupError, ORDER_BOOK_NOT_FOUND, ORDER_LIST_NOT_FOUND,
55 ORDER_NOT_FOUND, OWN_ORDER_BOOK_NOT_FOUND, OrderBookLookupError, OrderListLookupError,
56 OrderLookupError, OwnOrderBookLookupError, POSITION_NOT_FOUND, PositionLookupError,
57 SYNTHETIC_INSTRUMENT_NOT_FOUND, SyntheticInstrumentLookupError, VenueOrderIdOwnershipError,
58};
59use filter::{FilterSources, intersect_pair_or_many};
60use index::CacheIndex;
61use indexmap::IndexMap;
62use nautilus_core::{
63 DurationNanos, SharedCell, UnixNanos,
64 correctness::{
65 check_key_not_in_map, check_predicate_false, check_slice_not_empty, check_valid_string_utf8,
66 },
67};
68use nautilus_model::{
69 accounts::{Account, AccountAny},
70 data::{
71 Bar, BarType, FundingRateUpdate, GreeksData, IndexPriceUpdate, InstrumentClose,
72 InstrumentStatus, MarkPriceUpdate, QuoteTick, TradeTick, YieldCurve,
73 option_chain::OptionGreeks,
74 },
75 enums::{
76 AggregationSource, ContingencyType, InstrumentClass, OmsType, OrderSide, PositionSide,
77 PriceType,
78 },
79 events::{AccountState, OrderEventAny, OrderFilled, PositionAdjusted},
80 identifiers::{
81 AccountId, ActorId, ClientId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId,
82 PositionId, StrategyId, Venue, VenueOrderId,
83 },
84 instruments::{Instrument, InstrumentAny, SyntheticInstrument},
85 orderbook::{
86 OrderBook,
87 own::{OwnOrderBook, should_handle_own_book_order},
88 },
89 orders::{Order, OrderAny, OrderError, OrderList},
90 position::{Position, PositionReplayEvent},
91 types::{
92 Currency, Money, Price, Quantity,
93 fixed::{FIXED_PRECISION, MAX_FLOAT_PRECISION, check_fixed_precision},
94 price::PriceRaw,
95 },
96};
97pub use position::CacheSnapshotRef;
98use position::PositionSnapshotFrame;
99pub use refs::{AccountRef, AccountRefMut, OrderRef, OrderRefMut, PositionRef, PositionRefMut};
100use rust_decimal::Decimal;
101use ustr::Ustr;
102pub use view::CacheView; use crate::xrate::get_exchange_rate;
105
106#[cfg_attr(
108 feature = "python",
109 pyo3::pyclass(module = "nautilus_trader.common", unsendable)
110)]
111pub struct Cache {
112 config: CacheConfig,
113 index: CacheIndex,
114 database: Option<Box<dyn CacheDatabaseAdapter>>,
115 general: AHashMap<String, Bytes>,
116 currencies: AHashMap<Ustr, Currency>,
117 instruments: AHashMap<InstrumentId, InstrumentAny>,
118 instrument_closes: AHashMap<InstrumentId, InstrumentClose>,
119 synthetics: AHashMap<InstrumentId, SyntheticInstrument>,
120 books: AHashMap<InstrumentId, OrderBook>,
121 own_books: AHashMap<InstrumentId, OwnOrderBook>,
122 quotes: AHashMap<InstrumentId, BoundedVecDeque<QuoteTick>>,
123 trades: AHashMap<InstrumentId, BoundedVecDeque<TradeTick>>,
124 mark_xrates: AHashMap<(Currency, Currency), f64>,
125 mark_prices: AHashMap<InstrumentId, BoundedVecDeque<MarkPriceUpdate>>,
126 index_prices: AHashMap<InstrumentId, BoundedVecDeque<IndexPriceUpdate>>,
127 funding_rates: AHashMap<InstrumentId, BoundedVecDeque<FundingRateUpdate>>,
128 instrument_statuses: AHashMap<InstrumentId, BoundedVecDeque<InstrumentStatus>>,
129 bars: AHashMap<BarType, BoundedVecDeque<Bar>>,
130 greeks: AHashMap<InstrumentId, GreeksData>,
131 option_greeks: AHashMap<InstrumentId, OptionGreeks>,
132 yield_curves: AHashMap<String, YieldCurve>,
133 external_order_claims: AHashMap<InstrumentId, StrategyId>,
134 client_accounts: AHashMap<ClientId, AccountId>,
135 client_routes: AHashMap<Venue, ClientId>,
136 default_client_id: Option<ClientId>,
137 external_data_clients: AHashSet<ClientId>,
138 external_exec_clients: AHashSet<ClientId>,
139 accounts: AHashMap<AccountId, SharedCell<AccountAny>>,
140 orders: AHashMap<ClientOrderId, SharedCell<OrderAny>>,
141 orders_dispatched: AHashSet<ClientOrderId>,
142 order_lists: AHashMap<OrderListId, OrderList>,
143 positions: AHashMap<PositionId, SharedCell<Position>>,
144 position_snapshots: AHashMap<PositionId, Vec<PositionSnapshotFrame>>,
145 position_snapshot_revisions: AHashMap<PositionId, u64>,
146 #[cfg(feature = "defi")]
147 pub(crate) defi: crate::defi::cache::DefiCache,
148}
149
150impl Debug for Cache {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 f.debug_struct(stringify!(Cache))
153 .field("config", &self.config)
154 .field("index", &self.index)
155 .field("general", &self.general)
156 .field("currencies", &self.currencies)
157 .field("instruments", &self.instruments)
158 .field("synthetics", &self.synthetics)
159 .field("books", &self.books)
160 .field("own_books", &self.own_books)
161 .field("quotes", &self.quotes)
162 .field("trades", &self.trades)
163 .field("mark_xrates", &self.mark_xrates)
164 .field("mark_prices", &self.mark_prices)
165 .field("index_prices", &self.index_prices)
166 .field("funding_rates", &self.funding_rates)
167 .field("instrument_statuses", &self.instrument_statuses)
168 .field("instrument_closes", &self.instrument_closes)
169 .field("bars", &self.bars)
170 .field("greeks", &self.greeks)
171 .field("option_greeks", &self.option_greeks)
172 .field("yield_curves", &self.yield_curves)
173 .field("external_order_claims", &self.external_order_claims)
174 .field("client_accounts", &self.client_accounts)
175 .field("client_routes", &self.client_routes)
176 .field("default_client_id", &self.default_client_id)
177 .field("external_data_clients", &self.external_data_clients)
178 .field("external_exec_clients", &self.external_exec_clients)
179 .field("accounts", &self.accounts)
180 .field("orders", &self.orders)
181 .field("orders_dispatched", &self.orders_dispatched)
182 .field("order_lists", &self.order_lists)
183 .field("positions", &self.positions)
184 .field("position_snapshots", &self.position_snapshots)
185 .finish()
186 }
187}
188
189impl Default for Cache {
190 fn default() -> Self {
192 Self::new(Some(CacheConfig::default()), None)
193 }
194}
195
196impl Cache {
197 #[must_use]
199 pub fn new(
207 config: Option<CacheConfig>,
208 database: Option<Box<dyn CacheDatabaseAdapter>>,
209 ) -> Self {
210 Self::try_new(config, database).expect("invalid `CacheConfig`")
211 }
212
213 pub fn try_new(
219 config: Option<CacheConfig>,
220 database: Option<Box<dyn CacheDatabaseAdapter>>,
221 ) -> crate::config::ConfigResult<Self> {
222 let config = config.unwrap_or_default();
223 config.validate()?;
224
225 Ok(Self {
226 config,
227 index: CacheIndex::default(),
228 database,
229 general: AHashMap::new(),
230 currencies: AHashMap::new(),
231 instruments: AHashMap::new(),
232 instrument_closes: AHashMap::new(),
233 synthetics: AHashMap::new(),
234 books: AHashMap::new(),
235 own_books: AHashMap::new(),
236 quotes: AHashMap::new(),
237 trades: AHashMap::new(),
238 mark_xrates: AHashMap::new(),
239 mark_prices: AHashMap::new(),
240 index_prices: AHashMap::new(),
241 funding_rates: AHashMap::new(),
242 instrument_statuses: AHashMap::new(),
243 bars: AHashMap::new(),
244 greeks: AHashMap::new(),
245 option_greeks: AHashMap::new(),
246 yield_curves: AHashMap::new(),
247 external_order_claims: AHashMap::new(),
248 client_accounts: AHashMap::new(),
249 client_routes: AHashMap::new(),
250 default_client_id: None,
251 external_data_clients: AHashSet::new(),
252 external_exec_clients: AHashSet::new(),
253 accounts: AHashMap::new(),
254 orders: AHashMap::new(),
255 orders_dispatched: AHashSet::new(),
256 order_lists: AHashMap::new(),
257 positions: AHashMap::new(),
258 position_snapshots: AHashMap::new(),
259 position_snapshot_revisions: AHashMap::new(),
260 #[cfg(feature = "defi")]
261 defi: crate::defi::cache::DefiCache::default(),
262 })
263 }
264
265 #[must_use]
267 pub fn memory_address(&self) -> String {
268 format!("{:?}", std::ptr::from_ref(self))
269 }
270
271 #[must_use]
273 pub fn external_order_claim(&self, instrument_id: &InstrumentId) -> Option<StrategyId> {
274 self.external_order_claims.get(instrument_id).copied()
275 }
276
277 #[must_use]
282 pub fn external_order_claim_instrument_ids(
283 &self,
284 strategy_id: Option<StrategyId>,
285 ) -> AHashSet<InstrumentId> {
286 self.external_order_claims
287 .iter()
288 .filter_map(|(instrument_id, owner)| {
289 strategy_id
290 .is_none_or(|strategy_id| *owner == strategy_id)
291 .then_some(*instrument_id)
292 })
293 .collect()
294 }
295
296 pub fn set_external_order_claims(
308 &mut self,
309 strategy_id: StrategyId,
310 instrument_ids: &[InstrumentId],
311 ) -> anyhow::Result<()> {
312 let mut requested = AHashSet::with_capacity(instrument_ids.len());
313
314 for instrument_id in instrument_ids {
315 if !requested.insert(*instrument_id) {
316 anyhow::bail!(
317 "External order claim for {instrument_id} appears more than once for {strategy_id}"
318 );
319 }
320
321 if let Some(existing) = self.external_order_claims.get(instrument_id)
322 && *existing != strategy_id
323 {
324 anyhow::bail!(
325 "External order claim for {instrument_id} already exists for {existing}"
326 );
327 }
328 }
329
330 self.external_order_claims
331 .retain(|_, owner| *owner != strategy_id);
332 self.external_order_claims.extend(
333 requested
334 .into_iter()
335 .map(|instrument_id| (instrument_id, strategy_id)),
336 );
337
338 Ok(())
339 }
340
341 pub fn register_external_order_claims(
347 &mut self,
348 strategy_id: StrategyId,
349 instrument_ids: &[InstrumentId],
350 ) -> anyhow::Result<()> {
351 let mut requested = AHashSet::with_capacity(instrument_ids.len());
352
353 for instrument_id in instrument_ids {
354 if !requested.insert(*instrument_id) {
355 anyhow::bail!(
356 "External order claim for {instrument_id} appears more than once for {strategy_id}"
357 );
358 }
359
360 if let Some(existing) = self.external_order_claims.get(instrument_id) {
361 anyhow::bail!(
362 "External order claim for {instrument_id} already exists for {existing}"
363 );
364 }
365 }
366
367 self.external_order_claims.extend(
368 requested
369 .into_iter()
370 .map(|instrument_id| (instrument_id, strategy_id)),
371 );
372
373 Ok(())
374 }
375
376 #[must_use]
381 pub fn account_id_for_client(&self, client_id: &ClientId) -> Option<&AccountId> {
382 self.client_accounts.get(client_id)
383 }
384
385 pub fn add_client_account(&mut self, client_id: ClientId, account_id: AccountId) {
390 self.client_accounts.insert(client_id, account_id);
391 }
392
393 pub fn remove_client_account(&mut self, client_id: &ClientId) {
395 self.client_accounts.remove(client_id);
396 }
397
398 #[must_use]
400 pub fn client_id_for_venue(&self, venue: &Venue) -> Option<&ClientId> {
401 self.client_routes
402 .get(venue)
403 .or(self.default_client_id.as_ref())
404 }
405
406 pub fn add_client_route(&mut self, client_id: ClientId, venue: Venue) {
411 self.client_routes.insert(venue, client_id);
412 }
413
414 pub fn set_default_client(&mut self, client_id: ClientId) {
417 self.default_client_id = Some(client_id);
418 }
419
420 pub fn remove_client_routes(&mut self, client_id: &ClientId) {
422 self.client_routes.retain(|_, routed| routed != client_id);
423
424 if self.default_client_id.as_ref() == Some(client_id) {
425 self.default_client_id = None;
426 }
427 }
428
429 #[must_use]
432 pub fn is_external_data_client(&self, client_id: &ClientId) -> bool {
433 self.external_data_clients.contains(client_id)
434 }
435
436 pub fn add_external_data_client(&mut self, client_id: ClientId) {
440 self.external_data_clients.insert(client_id);
441 }
442
443 #[must_use]
446 pub fn is_external_exec_client(&self, client_id: &ClientId) -> bool {
447 self.external_exec_clients.contains(client_id)
448 }
449
450 pub fn add_external_exec_client(&mut self, client_id: ClientId) {
454 self.external_exec_clients.insert(client_id);
455 }
456
457 pub fn set_database(&mut self, database: Box<dyn CacheDatabaseAdapter>) {
461 let type_name = std::any::type_name_of_val(&*database);
462 log::info!("Cache database adapter set: {type_name}");
463 self.database = Some(database);
464 }
465
466 pub fn cache_general(&mut self) -> anyhow::Result<()> {
474 self.general = match &mut self.database {
475 Some(db) => db.load()?,
476 None => AHashMap::new(),
477 };
478
479 log::info!(
480 "Cached {} general object(s) from database",
481 self.general.len()
482 );
483 Ok(())
484 }
485
486 pub async fn cache_all(&mut self) -> anyhow::Result<()> {
495 let mut cache_map = match &self.database {
496 Some(db) => db.load_all().await?,
497 None => CacheMap::default(),
498 };
499
500 register_loaded_currencies(&mut cache_map.currencies)?;
503 self.currencies = cache_map.currencies;
504 self.instruments = cache_map.instruments;
505 self.instrument_closes = cache_map.instrument_closes;
506 self.synthetics = cache_map.synthetics;
507 self.accounts = cache_map
508 .accounts
509 .into_iter()
510 .map(|(id, account)| (id, SharedCell::new(account)))
511 .collect();
512 self.orders = cache_map
513 .orders
514 .into_iter()
515 .map(|(id, order)| (id, SharedCell::new(order)))
516 .collect();
517 self.positions = cache_map
518 .positions
519 .into_iter()
520 .map(|(id, position)| (id, SharedCell::new(position)))
521 .collect();
522
523 if let Some(db) = &self.database {
524 let order_position = db.load_index_order_position()?;
525 self.index.order_position = self.sanitize_order_position_index(order_position);
526 self.index.order_client = db.load_index_order_client()?;
527 }
528
529 self.cache_position_oms()?;
530 self.assign_position_ids_to_contingencies();
531 Ok(())
532 }
533
534 pub async fn cache_currencies(&mut self) -> anyhow::Result<()> {
540 self.currencies = match &mut self.database {
541 Some(db) => db.load_currencies().await?,
542 None => AHashMap::new(),
543 };
544
545 register_loaded_currencies(&mut self.currencies)?;
546
547 log::info!("Cached {} currencies from database", self.currencies.len());
548 Ok(())
549 }
550
551 pub async fn cache_instruments(&mut self) -> anyhow::Result<()> {
557 self.instruments = match &mut self.database {
558 Some(db) => db.load_instruments().await?,
559 None => AHashMap::new(),
560 };
561
562 log::info!("Cached {} instruments from database", self.general.len());
563 Ok(())
564 }
565
566 pub async fn cache_synthetics(&mut self) -> anyhow::Result<()> {
572 self.synthetics = match &mut self.database {
573 Some(db) => db.load_synthetics().await?,
574 None => AHashMap::new(),
575 };
576
577 log::info!(
578 "Cached {} synthetic instruments from database",
579 self.general.len()
580 );
581 Ok(())
582 }
583
584 pub async fn cache_accounts(&mut self) -> anyhow::Result<()> {
590 self.accounts = match &mut self.database {
591 Some(db) => db
592 .load_accounts()
593 .await?
594 .into_iter()
595 .map(|(id, account)| (id, SharedCell::new(account)))
596 .collect(),
597 None => AHashMap::new(),
598 };
599
600 log::info!(
601 "Cached {} synthetic instruments from database",
602 self.general.len()
603 );
604 Ok(())
605 }
606
607 pub async fn cache_orders(&mut self) -> anyhow::Result<()> {
613 self.orders = match &mut self.database {
614 Some(db) => db
615 .load_orders()
616 .await?
617 .into_iter()
618 .map(|(id, order)| (id, SharedCell::new(order)))
619 .collect(),
620 None => AHashMap::new(),
621 };
622
623 if let Some(db) = &self.database {
624 let order_position = db.load_index_order_position()?;
625 self.index.order_position = self.sanitize_order_position_index(order_position);
626 self.index.order_client = db.load_index_order_client()?;
627 }
628
629 log::info!("Cached {} orders from database", self.general.len());
630
631 self.assign_position_ids_to_contingencies();
632 Ok(())
633 }
634
635 fn sanitize_order_position_index(
636 &self,
637 mut order_position: AHashMap<ClientOrderId, PositionId>,
638 ) -> AHashMap<ClientOrderId, PositionId> {
639 let original_len = order_position.len();
640 order_position.retain(|client_order_id, _| self.orders.contains_key(client_order_id));
641 let removed = original_len - order_position.len();
642
643 if removed > 0 {
644 log::warn!(
645 "Filtered {removed} stale order-position index entries without backing orders during cache load"
646 );
647 }
648
649 order_position
650 }
651
652 pub async fn cache_positions(&mut self) -> anyhow::Result<()> {
658 self.positions = match &mut self.database {
659 Some(db) => db
660 .load_positions()
661 .await?
662 .into_iter()
663 .map(|(id, position)| (id, SharedCell::new(position)))
664 .collect(),
665 None => AHashMap::new(),
666 };
667
668 self.cache_position_oms()?;
669 log::info!("Cached {} positions from database", self.general.len());
670 Ok(())
671 }
672
673 fn cache_position_oms(&mut self) -> anyhow::Result<()> {
674 let persisted = match &self.database {
675 Some(database) => database.load()?,
676 None => self.general.clone(),
677 };
678
679 self.general
680 .retain(|key, _| !key.starts_with(POSITION_OMS_KEY_PREFIX));
681
682 for (key, value) in persisted {
683 if !key.starts_with(POSITION_OMS_KEY_PREFIX) {
684 continue;
685 }
686 self.general.insert(key, value);
687 }
688
689 self.index_position_oms();
690 Ok(())
691 }
692
693 pub fn build_index(&mut self) {
702 log::debug!("Building index");
703
704 self.index.clear_entities();
705 self.index
706 .venue_order_ids
707 .retain(|_, client_order_id| self.orders.contains_key(client_order_id));
708 self.index
709 .client_order_ids
710 .retain(|client_order_id, _| self.orders.contains_key(client_order_id));
711 self.index
712 .order_position
713 .retain(|client_order_id, _| self.orders.contains_key(client_order_id));
714 self.index
715 .order_client
716 .retain(|client_order_id, _| self.orders.contains_key(client_order_id));
717 self.index
718 .orders_pending_cancel
719 .retain(|client_order_id| self.orders.contains_key(client_order_id));
720
721 for account_id in self.accounts.keys() {
723 self.index.add_venue_account(*account_id);
724 }
725
726 for (client_order_id, order_cell) in &self.orders {
728 let order = order_cell.borrow();
729 let instrument_id = order.instrument_id();
730 let venue = instrument_id.venue;
731 let strategy_id = order.strategy_id();
732
733 self.index
735 .venue_orders
736 .entry(venue)
737 .or_default()
738 .insert(*client_order_id);
739
740 for venue_order_id in order.venue_order_ids() {
743 self.index
744 .venue_order_ids
745 .insert(*venue_order_id, *client_order_id);
746 }
747
748 if let Some(venue_order_id) = order.venue_order_id() {
749 self.index
750 .client_order_ids
751 .insert(*client_order_id, venue_order_id);
752 }
753
754 self.index.order_position.extend(
756 order
757 .position_id()
758 .map(|position_id| (*client_order_id, position_id)),
759 );
760
761 self.index
763 .order_strategy
764 .insert(*client_order_id, strategy_id);
765
766 self.index
768 .instrument_orders
769 .entry(instrument_id)
770 .or_default()
771 .insert(*client_order_id);
772
773 self.index
775 .strategy_orders
776 .entry(strategy_id)
777 .or_default()
778 .insert(*client_order_id);
779
780 if let Some(account_id) = order.account_id() {
782 self.index
783 .account_orders
784 .entry(account_id)
785 .or_default()
786 .insert(*client_order_id);
787 }
788
789 if let Some(exec_algorithm_id) = order.exec_algorithm_id() {
791 self.index
792 .exec_algorithm_orders
793 .entry(exec_algorithm_id)
794 .or_default()
795 .insert(*client_order_id);
796 self.index.exec_algorithms.insert(exec_algorithm_id);
797 }
798
799 if let Some(exec_spawn_id) = order.exec_spawn_id() {
801 self.index
802 .exec_spawn_orders
803 .entry(exec_spawn_id)
804 .or_default()
805 .insert(*client_order_id);
806 }
807
808 self.index.orders.insert(*client_order_id);
810
811 if order.is_active_local() {
813 self.index.orders_active_local.insert(*client_order_id);
814 }
815
816 if order.is_open() {
818 self.index.orders_open.insert(*client_order_id);
819 }
820
821 if order.is_closed() {
823 self.index.orders_closed.insert(*client_order_id);
824 }
825
826 if order.emulation_trigger().is_some() && !order.is_closed() {
828 self.index.orders_emulated.insert(*client_order_id);
829 }
830
831 if order.is_inflight() {
833 self.index.orders_inflight.insert(*client_order_id);
834 }
835
836 self.index.strategies.insert(strategy_id);
838 }
839
840 for (position_id, position_cell) in &self.positions {
842 let position = position_cell.borrow();
843 let instrument_id = position.instrument_id;
844 let venue = instrument_id.venue;
845 let strategy_id = position.strategy_id;
846
847 self.index
849 .venue_positions
850 .entry(venue)
851 .or_default()
852 .insert(*position_id);
853
854 self.index
856 .position_strategy
857 .insert(*position_id, strategy_id);
858
859 let position_orders = self.index.position_orders.entry(*position_id).or_default();
861 position_orders.extend(
862 position
863 .client_order_ids()
864 .into_iter()
865 .filter(|client_order_id| self.orders.contains_key(client_order_id)),
866 );
867
868 self.index
870 .instrument_positions
871 .entry(instrument_id)
872 .or_default()
873 .insert(*position_id);
874 self.index
875 .instrument_orders
876 .entry(instrument_id)
877 .or_default();
878
879 self.index
881 .strategy_positions
882 .entry(strategy_id)
883 .or_default()
884 .insert(*position_id);
885 self.index.strategy_orders.entry(strategy_id).or_default();
886
887 self.index
889 .account_positions
890 .entry(position.account_id)
891 .or_default()
892 .insert(*position_id);
893
894 self.index.positions.insert(*position_id);
896
897 if position.is_open() {
899 self.index.positions_open.insert(*position_id);
900 }
901
902 if position.is_closed() {
904 self.index.positions_closed.insert(*position_id);
905 }
906
907 self.index.strategies.insert(strategy_id);
909 }
910
911 self.index_position_oms();
912 let client_order_ids: Vec<_> = self.orders.keys().copied().collect();
913 for client_order_id in client_order_ids {
914 self.reindex_hedging_position(client_order_id);
915 }
916 }
917
918 fn index_position_oms(&mut self) {
919 self.index.position_oms.clear();
920
921 for (key, value) in &self.general {
922 let Some(position_id) = key.strip_prefix(POSITION_OMS_KEY_PREFIX) else {
923 continue;
924 };
925 let position_id = PositionId::new(position_id);
926 if !self.positions.contains_key(&position_id) {
927 continue;
928 }
929
930 match serde_json::from_slice::<OmsType>(value) {
931 Ok(oms_type) => {
932 self.index.position_oms.insert(position_id, oms_type);
933 }
934 Err(e) => {
935 log::error!("Failed to decode position OMS for {position_id}: {e}");
936 }
937 }
938 }
939
940 for position in self.positions.values().map(|cell| cell.borrow()) {
941 if !self.index.position_oms.contains_key(&position.id)
942 && resolve_position_oms(&position, OmsType::Unspecified) == OmsType::Netting
943 {
944 self.index
945 .position_oms
946 .insert(position.id, OmsType::Netting);
947 }
948 }
949 }
950
951 #[must_use]
953 pub const fn has_backing(&self) -> bool {
954 self.database.is_some()
955 }
956
957 pub fn load_actor_state(
965 &self,
966 actor_id: &ActorId,
967 ) -> anyhow::Result<Option<IndexMap<String, Vec<u8>>>> {
968 self.database
969 .as_ref()
970 .map(|database| database.load_actor(actor_id))
971 .transpose()
972 .map(|state| state.map(Self::decode_component_state))
973 }
974
975 pub fn load_strategy_state(
983 &self,
984 strategy_id: &StrategyId,
985 ) -> anyhow::Result<Option<IndexMap<String, Vec<u8>>>> {
986 self.database
987 .as_ref()
988 .map(|database| database.load_strategy(strategy_id))
989 .transpose()
990 .map(|state| state.map(Self::decode_component_state))
991 }
992
993 pub fn update_actor_state(
999 &self,
1000 actor_id: &ActorId,
1001 state: &IndexMap<String, Vec<u8>>,
1002 ) -> anyhow::Result<()> {
1003 if let Some(database) = &self.database {
1004 database.update_actor(actor_id, &Self::encode_component_state(state))?;
1005 }
1006 Ok(())
1007 }
1008
1009 pub fn update_strategy_state(
1015 &self,
1016 strategy_id: &StrategyId,
1017 state: &IndexMap<String, Vec<u8>>,
1018 ) -> anyhow::Result<()> {
1019 if let Some(database) = &self.database {
1020 database.update_strategy(strategy_id, &Self::encode_component_state(state))?;
1021 }
1022 Ok(())
1023 }
1024
1025 fn decode_component_state(state: AHashMap<String, Bytes>) -> IndexMap<String, Vec<u8>> {
1026 state
1027 .into_iter()
1028 .map(|(key, value)| (key, value.to_vec()))
1029 .collect()
1030 }
1031
1032 fn encode_component_state(state: &IndexMap<String, Vec<u8>>) -> AHashMap<String, Bytes> {
1033 state
1034 .iter()
1035 .map(|(key, value)| (key.clone(), Bytes::copy_from_slice(value)))
1036 .collect()
1037 }
1038
1039 #[must_use]
1041 pub fn calculate_unrealized_pnl(&self, position: &Position) -> Option<Money> {
1042 let Some(quote) = self.quote(&position.instrument_id) else {
1043 log::warn!(
1044 "Cannot calculate unrealized PnL for {}, no quotes for {}",
1045 position.id,
1046 position.instrument_id
1047 );
1048 return None;
1049 };
1050
1051 let last = match position.side {
1053 PositionSide::Flat => {
1054 return Some(Money::zero(position.settlement_currency));
1055 }
1056 PositionSide::Long => quote.bid_price,
1057 PositionSide::Short => quote.ask_price,
1058 };
1059
1060 position
1061 .try_unrealized_pnl(last)
1062 .inspect_err(|e| {
1063 log::error!("Cannot calculate unrealized PnL for {}: {e}", position.id);
1064 })
1065 .ok()
1066 }
1067
1068 #[must_use]
1077 pub fn check_integrity(&mut self) -> bool {
1078 let mut error_count = 0;
1079 let failure = "Integrity failure";
1080
1081 let timestamp_us = SystemTime::now()
1083 .duration_since(UNIX_EPOCH)
1084 .expect("Time went backwards")
1085 .as_micros();
1086
1087 log::info!("Checking data integrity");
1088
1089 for account_id in self.accounts.keys() {
1091 if !self
1092 .index
1093 .venue_accounts
1094 .get(&account_id.get_issuer())
1095 .is_some_and(|account_ids| account_ids.contains(account_id))
1096 {
1097 log::error!(
1098 "{failure} in accounts: {account_id} not found in `self.index.venue_accounts`",
1099 );
1100 error_count += 1;
1101 }
1102 }
1103
1104 for (client_order_id, order_cell) in &self.orders {
1105 let order = order_cell.borrow();
1106
1107 if !self.index.order_strategy.contains_key(client_order_id) {
1108 log::error!(
1109 "{failure} in orders: {client_order_id} not found in `self.index.order_strategy`"
1110 );
1111 error_count += 1;
1112 }
1113
1114 if !self.index.orders.contains(client_order_id) {
1115 log::error!(
1116 "{failure} in orders: {client_order_id} not found in `self.index.orders`",
1117 );
1118 error_count += 1;
1119 }
1120
1121 if order.is_inflight() && !self.index.orders_inflight.contains(client_order_id) {
1122 log::error!(
1123 "{failure} in orders: {client_order_id} not found in `self.index.orders_inflight`",
1124 );
1125 error_count += 1;
1126 }
1127
1128 if order.is_active_local() && !self.index.orders_active_local.contains(client_order_id)
1129 {
1130 log::error!(
1131 "{failure} in orders: {client_order_id} not found in `self.index.orders_active_local`",
1132 );
1133 error_count += 1;
1134 }
1135
1136 if order.is_open() && !self.index.orders_open.contains(client_order_id) {
1137 log::error!(
1138 "{failure} in orders: {client_order_id} not found in `self.index.orders_open`",
1139 );
1140 error_count += 1;
1141 }
1142
1143 if order.is_closed() && !self.index.orders_closed.contains(client_order_id) {
1144 log::error!(
1145 "{failure} in orders: {client_order_id} not found in `self.index.orders_closed`",
1146 );
1147 error_count += 1;
1148 }
1149
1150 if let Some(exec_algorithm_id) = order.exec_algorithm_id() {
1151 if !self
1152 .index
1153 .exec_algorithm_orders
1154 .contains_key(&exec_algorithm_id)
1155 {
1156 log::error!(
1157 "{failure} in orders: {client_order_id} not found in `self.index.exec_algorithm_orders`",
1158 );
1159 error_count += 1;
1160 }
1161
1162 if order.exec_spawn_id().is_none()
1163 && !self.index.exec_spawn_orders.contains_key(client_order_id)
1164 {
1165 log::error!(
1166 "{failure} in orders: {client_order_id} not found in `self.index.exec_spawn_orders`",
1167 );
1168 error_count += 1;
1169 }
1170 }
1171 }
1172
1173 for (position_id, position_cell) in &self.positions {
1174 let position = position_cell.borrow();
1175
1176 if !self.index.position_strategy.contains_key(position_id) {
1177 log::error!(
1178 "{failure} in positions: {position_id} not found in `self.index.position_strategy`",
1179 );
1180 error_count += 1;
1181 }
1182
1183 if !self.index.position_orders.contains_key(position_id) {
1184 log::error!(
1185 "{failure} in positions: {position_id} not found in `self.index.position_orders`",
1186 );
1187 error_count += 1;
1188 }
1189
1190 if !self.index.positions.contains(position_id) {
1191 log::error!(
1192 "{failure} in positions: {position_id} not found in `self.index.positions`",
1193 );
1194 error_count += 1;
1195 }
1196
1197 if position.is_open() && !self.index.positions_open.contains(position_id) {
1198 log::error!(
1199 "{failure} in positions: {position_id} not found in `self.index.positions_open`",
1200 );
1201 error_count += 1;
1202 }
1203
1204 if position.is_closed() && !self.index.positions_closed.contains(position_id) {
1205 log::error!(
1206 "{failure} in positions: {position_id} not found in `self.index.positions_closed`",
1207 );
1208 error_count += 1;
1209 }
1210 }
1211
1212 for account_id in self.index.venue_accounts.values().flatten() {
1214 if !self.accounts.contains_key(account_id) {
1215 log::error!(
1216 "{failure} in `index.venue_accounts`: {account_id} not found in `self.accounts`",
1217 );
1218 error_count += 1;
1219 }
1220 }
1221
1222 for client_order_id in self.index.venue_order_ids.values() {
1223 if !self.orders.contains_key(client_order_id) {
1224 log::error!(
1225 "{failure} in `index.venue_order_ids`: {client_order_id} not found in `self.orders`",
1226 );
1227 error_count += 1;
1228 }
1229 }
1230
1231 for client_order_id in self.index.client_order_ids.keys() {
1232 if !self.orders.contains_key(client_order_id) {
1233 log::error!(
1234 "{failure} in `index.client_order_ids`: {client_order_id} not found in `self.orders`",
1235 );
1236 error_count += 1;
1237 }
1238 }
1239
1240 for client_order_id in self.index.order_position.keys() {
1241 if !self.orders.contains_key(client_order_id) {
1242 log::error!(
1243 "{failure} in `index.order_position`: {client_order_id} not found in `self.orders`",
1244 );
1245 error_count += 1;
1246 }
1247 }
1248
1249 for client_order_id in self.index.order_strategy.keys() {
1251 if !self.orders.contains_key(client_order_id) {
1252 log::error!(
1253 "{failure} in `index.order_strategy`: {client_order_id} not found in `self.orders`",
1254 );
1255 error_count += 1;
1256 }
1257 }
1258
1259 for position_id in self.index.position_strategy.keys() {
1260 if !self.positions.contains_key(position_id) {
1261 log::error!(
1262 "{failure} in `index.position_strategy`: {position_id} not found in `self.positions`",
1263 );
1264 error_count += 1;
1265 }
1266 }
1267
1268 for position_id in self.index.position_orders.keys() {
1269 if !self.positions.contains_key(position_id) {
1270 log::error!(
1271 "{failure} in `index.position_orders`: {position_id} not found in `self.positions`",
1272 );
1273 error_count += 1;
1274 }
1275 }
1276
1277 for (instrument_id, client_order_ids) in &self.index.instrument_orders {
1278 for client_order_id in client_order_ids {
1279 if !self.orders.contains_key(client_order_id) {
1280 log::error!(
1281 "{failure} in `index.instrument_orders`: {instrument_id} not found in `self.orders`",
1282 );
1283 error_count += 1;
1284 }
1285 }
1286 }
1287
1288 for instrument_id in self.index.instrument_positions.keys() {
1289 if !self.index.instrument_orders.contains_key(instrument_id) {
1290 log::error!(
1291 "{failure} in `index.instrument_positions`: {instrument_id} not found in `index.instrument_orders`",
1292 );
1293 error_count += 1;
1294 }
1295 }
1296
1297 for client_order_ids in self.index.strategy_orders.values() {
1298 for client_order_id in client_order_ids {
1299 if !self.orders.contains_key(client_order_id) {
1300 log::error!(
1301 "{failure} in `index.strategy_orders`: {client_order_id} not found in `self.orders`",
1302 );
1303 error_count += 1;
1304 }
1305 }
1306 }
1307
1308 for position_ids in self.index.strategy_positions.values() {
1309 for position_id in position_ids {
1310 if !self.positions.contains_key(position_id) {
1311 log::error!(
1312 "{failure} in `index.strategy_positions`: {position_id} not found in `self.positions`",
1313 );
1314 error_count += 1;
1315 }
1316 }
1317 }
1318
1319 for client_order_id in &self.index.orders {
1320 if !self.orders.contains_key(client_order_id) {
1321 log::error!(
1322 "{failure} in `index.orders`: {client_order_id} not found in `self.orders`",
1323 );
1324 error_count += 1;
1325 }
1326 }
1327
1328 for client_order_id in &self.index.orders_emulated {
1329 if !self.orders.contains_key(client_order_id) {
1330 log::error!(
1331 "{failure} in `index.orders_emulated`: {client_order_id} not found in `self.orders`",
1332 );
1333 error_count += 1;
1334 }
1335 }
1336
1337 for client_order_id in &self.index.orders_active_local {
1338 if !self.orders.contains_key(client_order_id) {
1339 log::error!(
1340 "{failure} in `index.orders_active_local`: {client_order_id} not found in `self.orders`",
1341 );
1342 error_count += 1;
1343 }
1344 }
1345
1346 for client_order_id in &self.index.orders_inflight {
1347 if !self.orders.contains_key(client_order_id) {
1348 log::error!(
1349 "{failure} in `index.orders_inflight`: {client_order_id} not found in `self.orders`",
1350 );
1351 error_count += 1;
1352 }
1353 }
1354
1355 for client_order_id in &self.index.orders_open {
1356 if !self.orders.contains_key(client_order_id) {
1357 log::error!(
1358 "{failure} in `index.orders_open`: {client_order_id} not found in `self.orders`",
1359 );
1360 error_count += 1;
1361 }
1362 }
1363
1364 for client_order_id in &self.index.orders_closed {
1365 if !self.orders.contains_key(client_order_id) {
1366 log::error!(
1367 "{failure} in `index.orders_closed`: {client_order_id} not found in `self.orders`",
1368 );
1369 error_count += 1;
1370 }
1371 }
1372
1373 for position_id in &self.index.positions {
1374 if !self.positions.contains_key(position_id) {
1375 log::error!(
1376 "{failure} in `index.positions`: {position_id} not found in `self.positions`",
1377 );
1378 error_count += 1;
1379 }
1380 }
1381
1382 for position_id in &self.index.positions_open {
1383 if !self.positions.contains_key(position_id) {
1384 log::error!(
1385 "{failure} in `index.positions_open`: {position_id} not found in `self.positions`",
1386 );
1387 error_count += 1;
1388 }
1389 }
1390
1391 for position_id in &self.index.positions_closed {
1392 if !self.positions.contains_key(position_id) {
1393 log::error!(
1394 "{failure} in `index.positions_closed`: {position_id} not found in `self.positions`",
1395 );
1396 error_count += 1;
1397 }
1398 }
1399
1400 for strategy_id in &self.index.strategies {
1401 if !self.index.strategy_orders.contains_key(strategy_id) {
1402 log::error!(
1403 "{failure} in `index.strategies`: {strategy_id} not found in `index.strategy_orders`",
1404 );
1405 error_count += 1;
1406 }
1407 }
1408
1409 for exec_algorithm_id in &self.index.exec_algorithms {
1410 if !self
1411 .index
1412 .exec_algorithm_orders
1413 .contains_key(exec_algorithm_id)
1414 {
1415 log::error!(
1416 "{failure} in `index.exec_algorithms`: {exec_algorithm_id} not found in `index.exec_algorithm_orders`",
1417 );
1418 error_count += 1;
1419 }
1420 }
1421
1422 let total_us = SystemTime::now()
1423 .duration_since(UNIX_EPOCH)
1424 .expect("Time went backwards")
1425 .as_micros()
1426 - timestamp_us;
1427
1428 if error_count == 0 {
1429 log::info!("Integrity check passed in {total_us}μs");
1430 true
1431 } else {
1432 log::error!(
1433 "Integrity check failed with {error_count} error{} in {total_us}μs",
1434 if error_count == 1 { "" } else { "s" },
1435 );
1436 false
1437 }
1438 }
1439
1440 #[must_use]
1444 pub fn check_residuals(&self) -> bool {
1445 log::debug!("Checking residuals");
1446
1447 let mut residuals = false;
1448
1449 for order in self.orders_open(None, None, None, None, None) {
1451 residuals = true;
1452 log::warn!("Residual {order}");
1453 }
1454
1455 for position in self.positions_open(None, None, None, None, None) {
1457 residuals = true;
1458 log::warn!("Residual {position}");
1459 }
1460
1461 residuals
1462 }
1463
1464 pub fn purge_closed_orders(&mut self, ts_now: UnixNanos, buffer_secs: u64) {
1470 log::debug!(
1471 "Purging closed orders{}",
1472 if buffer_secs > 0 {
1473 format!(" with buffer_secs={buffer_secs}")
1474 } else {
1475 String::new()
1476 }
1477 );
1478
1479 let Ok(buffer_ns) = DurationNanos::try_from_secs(buffer_secs) else {
1480 log::warn!(
1481 "Cannot purge closed orders: buffer_secs {buffer_secs} is not representable in `u64` nanoseconds"
1482 );
1483 return;
1484 };
1485 let purge_cutoff = ts_now.checked_sub(buffer_ns);
1486
1487 let mut affected_order_list_ids: AHashSet<OrderListId> = AHashSet::new();
1488 let mut purged_client_order_ids: AHashSet<ClientOrderId> = AHashSet::new();
1489
1490 'outer: for client_order_id in self.index.orders_closed.clone() {
1491 let purge_target = self.orders.get(&client_order_id).and_then(|order_cell| {
1492 let order = order_cell.borrow();
1493 if order.is_closed()
1494 && let Some(ts_closed) = order.ts_closed()
1495 && purge_cutoff.is_some_and(|cutoff| ts_closed <= cutoff)
1496 {
1497 let linked = order.linked_order_ids().map(<[_]>::to_vec);
1498 let order_list_id = order.order_list_id();
1499 Some((linked, order_list_id))
1500 } else {
1501 None
1502 }
1503 });
1504
1505 let Some((linked, order_list_id)) = purge_target else {
1506 continue;
1507 };
1508
1509 if let Some(linked_order_ids) = linked {
1511 for linked_order_id in &linked_order_ids {
1512 if let Some(linked_order_cell) = self.orders.get(linked_order_id)
1513 && linked_order_cell.borrow().is_open()
1514 {
1515 continue 'outer;
1517 }
1518 }
1519 }
1520
1521 if let Some(order_list_id) = order_list_id {
1522 affected_order_list_ids.insert(order_list_id);
1523 }
1524
1525 if self.purge_order_except_aliases(client_order_id) {
1526 purged_client_order_ids.insert(client_order_id);
1527 }
1528 }
1529
1530 if !purged_client_order_ids.is_empty() {
1531 self.index
1532 .venue_order_ids
1533 .retain(|_, owner| !purged_client_order_ids.contains(owner));
1534 }
1535
1536 for order_list_id in affected_order_list_ids {
1537 if let Some(order_list) = self.order_lists.get(&order_list_id) {
1538 let all_purged = order_list
1539 .client_order_ids
1540 .iter()
1541 .all(|id| !self.orders.contains_key(id));
1542
1543 if all_purged {
1544 self.order_lists.remove(&order_list_id);
1545 log::info!("Purged {order_list_id}");
1546 }
1547 }
1548 }
1549 }
1550
1551 pub fn purge_closed_positions(&mut self, ts_now: UnixNanos, buffer_secs: u64) {
1553 log::debug!(
1554 "Purging closed positions{}",
1555 if buffer_secs > 0 {
1556 format!(" with buffer_secs={buffer_secs}")
1557 } else {
1558 String::new()
1559 }
1560 );
1561
1562 let Ok(buffer_ns) = DurationNanos::try_from_secs(buffer_secs) else {
1563 log::warn!(
1564 "Cannot purge closed positions: buffer_secs {buffer_secs} is not representable in `u64` nanoseconds"
1565 );
1566 return;
1567 };
1568 let purge_cutoff = ts_now.checked_sub(buffer_ns);
1569
1570 for position_id in self.index.positions_closed.clone() {
1571 let should_purge = self.positions.get(&position_id).is_some_and(|cell| {
1572 let position = cell.borrow();
1573 position.is_closed()
1574 && position.ts_closed.is_some_and(|ts_closed| {
1575 purge_cutoff.is_some_and(|cutoff| ts_closed <= cutoff)
1576 })
1577 });
1578
1579 if should_purge {
1580 self.purge_position(position_id);
1581 }
1582 }
1583 }
1584
1585 pub fn purge_order(&mut self, client_order_id: ClientOrderId) {
1589 if self.purge_order_except_aliases(client_order_id) {
1590 self.index
1591 .venue_order_ids
1592 .retain(|_, owner| owner != &client_order_id);
1593 }
1594 }
1595
1596 fn purge_order_except_aliases(&mut self, client_order_id: ClientOrderId) -> bool {
1601 struct OrderDetails {
1602 is_open: bool,
1603 instrument_id: InstrumentId,
1604 strategy_id: StrategyId,
1605 account_id: Option<AccountId>,
1606 exec_algorithm_id: Option<ExecAlgorithmId>,
1607 exec_spawn_id: Option<ClientOrderId>,
1608 position_id: Option<PositionId>,
1609 }
1610
1611 let order_cell = self.orders.get(&client_order_id).cloned();
1612 let order_details = order_cell.as_ref().map(|cell| {
1613 let order = cell.borrow();
1614 OrderDetails {
1615 is_open: order.is_open(),
1616 instrument_id: order.instrument_id(),
1617 strategy_id: order.strategy_id(),
1618 account_id: order.account_id(),
1619 exec_algorithm_id: order.exec_algorithm_id(),
1620 exec_spawn_id: order.exec_spawn_id(),
1621 position_id: order.position_id(),
1622 }
1623 });
1624
1625 if order_details
1626 .as_ref()
1627 .is_some_and(|details| details.is_open)
1628 {
1629 log::warn!("Order {client_order_id} found open when purging, skipping purge");
1630 return false;
1631 }
1632
1633 if order_details.is_some() {
1634 self.orders.remove(&client_order_id);
1635 } else {
1636 log::warn!("Order {client_order_id} not found when purging");
1637 }
1638
1639 let indexed_position_id = self.index.order_position.remove(&client_order_id);
1640 let indexed_strategy_id = self.index.order_strategy.remove(&client_order_id);
1641 self.index.order_client.remove(&client_order_id);
1642 self.index.client_order_ids.remove(&client_order_id);
1643
1644 if let Some(details) = &order_details {
1645 if let Some(venue_orders) = self
1646 .index
1647 .venue_orders
1648 .get_mut(&details.instrument_id.venue)
1649 {
1650 venue_orders.remove(&client_order_id);
1651 if venue_orders.is_empty() {
1652 self.index.venue_orders.remove(&details.instrument_id.venue);
1653 }
1654 }
1655
1656 let instrument_orders_became_empty = self
1661 .index
1662 .instrument_orders
1663 .get_mut(&details.instrument_id)
1664 .is_some_and(|instrument_orders| {
1665 instrument_orders.remove(&client_order_id);
1666 instrument_orders.is_empty()
1667 });
1668
1669 let has_instrument_positions = self
1670 .index
1671 .instrument_positions
1672 .get(&details.instrument_id)
1673 .is_some_and(|positions| !positions.is_empty());
1674
1675 if instrument_orders_became_empty && !has_instrument_positions {
1676 self.index.instrument_orders.remove(&details.instrument_id);
1677 }
1678
1679 if let Some(exec_algorithm_id) = details.exec_algorithm_id {
1680 let became_empty = self
1681 .index
1682 .exec_algorithm_orders
1683 .get_mut(&exec_algorithm_id)
1684 .is_some_and(|orders| {
1685 orders.remove(&client_order_id);
1686 orders.is_empty()
1687 });
1688
1689 if became_empty {
1690 self.index.exec_algorithm_orders.remove(&exec_algorithm_id);
1691 self.index.exec_algorithms.remove(&exec_algorithm_id);
1692 }
1693 }
1694
1695 if let Some(account_id) = details.account_id
1696 && let Some(account_orders) = self.index.account_orders.get_mut(&account_id)
1697 {
1698 account_orders.remove(&client_order_id);
1699 if account_orders.is_empty() {
1700 self.index.account_orders.remove(&account_id);
1701 }
1702 }
1703
1704 if let Some(exec_spawn_id) = details.exec_spawn_id
1705 && let Some(spawn_orders) = self.index.exec_spawn_orders.get_mut(&exec_spawn_id)
1706 {
1707 spawn_orders.remove(&client_order_id);
1708 if spawn_orders.is_empty() {
1709 self.index.exec_spawn_orders.remove(&exec_spawn_id);
1710 }
1711 }
1712 }
1713
1714 let mut position_ids = AHashSet::new();
1715 if let Some(position_id) = indexed_position_id {
1716 position_ids.insert(position_id);
1717 }
1718
1719 if let Some(position_id) = order_details
1720 .as_ref()
1721 .and_then(|details| details.position_id)
1722 {
1723 position_ids.insert(position_id);
1724 }
1725
1726 let mut strategy_ids = AHashSet::new();
1727 if let Some(strategy_id) = indexed_strategy_id {
1728 strategy_ids.insert(strategy_id);
1729 }
1730
1731 if let Some(details) = &order_details {
1732 strategy_ids.insert(details.strategy_id);
1733 }
1734
1735 for position_id in position_ids {
1736 if self.positions.contains_key(&position_id) {
1737 if let Some(position_orders) = self.index.position_orders.get_mut(&position_id) {
1738 position_orders.remove(&client_order_id);
1739 }
1740 continue;
1741 }
1742
1743 let has_other_orders =
1744 if let Some(position_orders) = self.index.position_orders.get_mut(&position_id) {
1745 position_orders.remove(&client_order_id);
1746 !position_orders.is_empty()
1747 } else {
1748 self.index
1749 .order_position
1750 .values()
1751 .any(|candidate| *candidate == position_id)
1752 };
1753
1754 if has_other_orders {
1755 continue;
1756 }
1757
1758 self.index.position_orders.remove(&position_id);
1759 if let Some(strategy_id) = self.index.position_strategy.remove(&position_id) {
1760 strategy_ids.insert(strategy_id);
1761 if let Some(strategy_positions) =
1762 self.index.strategy_positions.get_mut(&strategy_id)
1763 {
1764 strategy_positions.remove(&position_id);
1765 if strategy_positions.is_empty() {
1766 self.index.strategy_positions.remove(&strategy_id);
1767 }
1768 }
1769 }
1770
1771 if let Some(details) = &order_details
1772 && let Some(venue_positions) = self
1773 .index
1774 .venue_positions
1775 .get_mut(&details.instrument_id.venue)
1776 {
1777 venue_positions.remove(&position_id);
1778 if venue_positions.is_empty() {
1779 self.index
1780 .venue_positions
1781 .remove(&details.instrument_id.venue);
1782 }
1783 }
1784 }
1785
1786 for strategy_id in strategy_ids {
1787 let strategy_orders_became_empty = self
1793 .index
1794 .strategy_orders
1795 .get_mut(&strategy_id)
1796 .is_some_and(|strategy_orders| {
1797 strategy_orders.remove(&client_order_id);
1798 strategy_orders.is_empty()
1799 });
1800
1801 let has_positions = self
1802 .index
1803 .strategy_positions
1804 .get(&strategy_id)
1805 .is_some_and(|strategy_positions| !strategy_positions.is_empty());
1806
1807 if strategy_orders_became_empty && !has_positions {
1808 self.index.strategy_orders.remove(&strategy_id);
1809 self.index.strategies.remove(&strategy_id);
1810 }
1811 }
1812
1813 self.index.exec_spawn_orders.remove(&client_order_id);
1814
1815 self.index.orders.remove(&client_order_id);
1816 self.index.orders_active_local.remove(&client_order_id);
1817 self.index.orders_open.remove(&client_order_id);
1818 self.index.orders_closed.remove(&client_order_id);
1819 self.index.orders_emulated.remove(&client_order_id);
1820 self.index.orders_inflight.remove(&client_order_id);
1821 self.index.orders_pending_cancel.remove(&client_order_id);
1822
1823 if order_details.is_some() {
1824 log::info!("Purged order {client_order_id}");
1825 }
1826
1827 true
1828 }
1829
1830 pub fn purge_position(&mut self, position_id: PositionId) {
1834 let position_cell = self.positions.get(&position_id).cloned();
1836 let position = position_cell.as_ref().map(|cell| cell.borrow());
1837
1838 if let Some(ref pos) = position
1840 && pos.is_open()
1841 {
1842 log::warn!("Position {position_id} found open when purging, skipping purge");
1843 return;
1844 }
1845
1846 if let Some(ref pos) = position {
1848 self.positions.remove(&position_id);
1849
1850 if let Some(venue_positions) =
1852 self.index.venue_positions.get_mut(&pos.instrument_id.venue)
1853 {
1854 venue_positions.remove(&position_id);
1855 if venue_positions.is_empty() {
1856 self.index.venue_positions.remove(&pos.instrument_id.venue);
1857 }
1858 }
1859
1860 let instrument_positions_became_empty = self
1862 .index
1863 .instrument_positions
1864 .get_mut(&pos.instrument_id)
1865 .is_some_and(|positions| {
1866 positions.remove(&position_id);
1867 positions.is_empty()
1868 });
1869
1870 if instrument_positions_became_empty {
1871 self.index.instrument_positions.remove(&pos.instrument_id);
1872 let instrument_orders_empty = self
1873 .index
1874 .instrument_orders
1875 .get(&pos.instrument_id)
1876 .is_some_and(|orders| orders.is_empty());
1877
1878 if instrument_orders_empty {
1879 self.index.instrument_orders.remove(&pos.instrument_id);
1880 }
1881 }
1882
1883 let strategy_positions_became_empty = self
1885 .index
1886 .strategy_positions
1887 .get_mut(&pos.strategy_id)
1888 .is_some_and(|positions| {
1889 positions.remove(&position_id);
1890 positions.is_empty()
1891 });
1892
1893 if strategy_positions_became_empty {
1894 self.index.strategy_positions.remove(&pos.strategy_id);
1895 let strategy_orders_empty = self
1896 .index
1897 .strategy_orders
1898 .get(&pos.strategy_id)
1899 .is_some_and(|orders| orders.is_empty());
1900
1901 if strategy_orders_empty {
1902 self.index.strategy_orders.remove(&pos.strategy_id);
1903 self.index.strategies.remove(&pos.strategy_id);
1904 }
1905 }
1906
1907 if let Some(account_positions) = self.index.account_positions.get_mut(&pos.account_id) {
1909 account_positions.remove(&position_id);
1910 if account_positions.is_empty() {
1911 self.index.account_positions.remove(&pos.account_id);
1912 }
1913 }
1914
1915 for client_order_id in pos.client_order_ids() {
1917 self.index.order_position.remove(&client_order_id);
1918 }
1919
1920 log::info!("Purged position {position_id}");
1921 } else {
1922 log::warn!("Position {position_id} not found when purging");
1923 }
1924
1925 self.index.position_strategy.remove(&position_id);
1927 self.index.position_oms.remove(&position_id);
1928 self.index.position_orders.remove(&position_id);
1929 self.index.positions.remove(&position_id);
1930 self.index.positions_open.remove(&position_id);
1931 self.index.positions_closed.remove(&position_id);
1932
1933 self.position_snapshots.remove(&position_id);
1935 self.bump_position_snapshot_revision(position_id);
1936 }
1937
1938 fn purge_instrument_inner(&mut self, instrument_id: InstrumentId, skip_order_guard: bool) {
1962 #[cfg(feature = "defi")]
1963 let defi_found = self.defi.pools.contains_key(&instrument_id)
1964 || self.defi.pool_profilers.contains_key(&instrument_id);
1965 #[cfg(not(feature = "defi"))]
1966 let defi_found = false;
1967
1968 let found = self.instruments.contains_key(&instrument_id)
1969 || self.synthetics.contains_key(&instrument_id)
1970 || defi_found;
1971
1972 if !found {
1973 log::warn!("Instrument {instrument_id} not found when purging");
1974 return;
1975 }
1976
1977 if !skip_order_guard && let Some(orders) = self.index.instrument_orders.get(&instrument_id)
1978 {
1979 let has_non_terminal = orders
1980 .iter()
1981 .any(|client_order_id| !self.index.orders_closed.contains(client_order_id));
1982
1983 if has_non_terminal {
1984 log::warn!(
1985 "Instrument {instrument_id} has non-terminal orders when purging, skipping purge"
1986 );
1987 return;
1988 }
1989 }
1990
1991 if let Some(positions) = self.index.instrument_positions.get(&instrument_id) {
1992 let has_non_closed = positions
1993 .iter()
1994 .any(|position_id| !self.index.positions_closed.contains(position_id));
1995
1996 if has_non_closed {
1997 log::warn!(
1998 "Instrument {instrument_id} has non-closed positions when purging, skipping purge"
1999 );
2000 return;
2001 }
2002 }
2003
2004 self.instruments.remove(&instrument_id);
2005 self.synthetics.remove(&instrument_id);
2006 self.books.remove(&instrument_id);
2007 self.own_books.remove(&instrument_id);
2008 self.quotes.remove(&instrument_id);
2009 self.trades.remove(&instrument_id);
2010 self.mark_prices.remove(&instrument_id);
2011 self.index_prices.remove(&instrument_id);
2012 self.funding_rates.remove(&instrument_id);
2013 self.instrument_statuses.remove(&instrument_id);
2014 self.instrument_closes.remove(&instrument_id);
2015 self.greeks.remove(&instrument_id);
2016 self.option_greeks.remove(&instrument_id);
2017
2018 self.bars
2019 .retain(|bar_type, _| bar_type.instrument_id() != instrument_id);
2020
2021 #[cfg(feature = "defi")]
2022 {
2023 self.defi.pools.remove(&instrument_id);
2024 self.defi.pool_profilers.remove(&instrument_id);
2025 }
2026
2027 log::info!("Purged instrument {instrument_id}");
2028 }
2029
2030 pub fn purge_instrument(&mut self, instrument_id: InstrumentId) {
2035 self.purge_instrument_inner(instrument_id, false);
2036 }
2037
2038 pub fn purge_instrument_skip_order_guard(&mut self, instrument_id: InstrumentId) {
2047 self.purge_instrument_inner(instrument_id, true);
2048 }
2049
2050 pub fn purge_account_events(&mut self, ts_now: UnixNanos, lookback_secs: u64) {
2055 log::debug!(
2056 "Purging account events{}",
2057 if lookback_secs > 0 {
2058 format!(" with lookback_secs={lookback_secs}")
2059 } else {
2060 String::new()
2061 }
2062 );
2063
2064 for account_cell in self.accounts.values() {
2065 let mut account = account_cell.borrow_mut();
2066 let event_count = account.event_count();
2067 account.purge_account_events(ts_now, lookback_secs);
2068 let count_diff = event_count - account.event_count();
2069 if count_diff > 0 {
2070 log::info!(
2071 "Purged {} event(s) from account {}",
2072 count_diff,
2073 account.id()
2074 );
2075 }
2076 }
2077 }
2078
2079 pub fn clear_index(&mut self) {
2081 self.index.clear();
2082 log::debug!("Cleared index");
2083 }
2084
2085 pub fn reset(&mut self) {
2093 log::debug!("Resetting cache");
2094
2095 self.general.clear();
2096 self.books.clear();
2097 self.own_books.clear();
2098 self.quotes.clear();
2099 self.trades.clear();
2100 self.mark_xrates.clear();
2101 self.mark_prices.clear();
2102 self.index_prices.clear();
2103 self.funding_rates.clear();
2104 self.instrument_statuses.clear();
2105 self.instrument_closes.clear();
2106 self.bars.clear();
2107 self.accounts.clear();
2108 self.orders.clear();
2109 self.orders_dispatched.clear();
2110 self.order_lists.clear();
2111 self.positions.clear();
2112 self.position_snapshots.clear();
2113 self.position_snapshot_revisions.clear();
2114 self.greeks.clear();
2115 self.option_greeks.clear();
2116 self.yield_curves.clear();
2117
2118 if self.config.drop_instruments_on_reset {
2119 self.currencies.clear();
2120 self.instruments.clear();
2121 self.synthetics.clear();
2122 }
2123
2124 #[cfg(feature = "defi")]
2125 {
2126 self.defi.pools.clear();
2127 self.defi.pool_profilers.clear();
2128 }
2129
2130 self.clear_index();
2131
2132 log::info!("Reset cache");
2133 }
2134
2135 pub fn dispose(&mut self) {
2139 self.reset();
2140
2141 if let Some(database) = &mut self.database
2142 && let Err(e) = database.close()
2143 {
2144 log::error!("Failed to close database during dispose: {e}");
2145 }
2146 }
2147
2148 pub fn flush_db(&mut self) -> anyhow::Result<()> {
2156 match &mut self.database {
2157 Some(database) => database.flush(),
2158 None => Ok(()),
2159 }
2160 }
2161
2162 pub fn add(&mut self, key: &str, value: Bytes) -> anyhow::Result<()> {
2170 check_valid_string_utf8(key, stringify!(key))?;
2172 check_predicate_false(value.is_empty(), stringify!(value))?;
2173
2174 log::debug!("Adding general {key}");
2175 self.general.insert(key.to_string(), value.clone());
2176
2177 if let Some(database) = &mut self.database {
2178 database.add(key.to_string(), value)?;
2179 }
2180 Ok(())
2181 }
2182
2183 pub fn add_order_book(&mut self, book: OrderBook) -> anyhow::Result<()> {
2189 log::debug!("Adding `OrderBook` {}", book.instrument_id);
2190
2191 if self.config.save_market_data
2192 && let Some(database) = &mut self.database
2193 {
2194 database.add_order_book(&book)?;
2195 }
2196
2197 self.books.insert(book.instrument_id, book);
2198 Ok(())
2199 }
2200
2201 pub fn add_own_order_book(&mut self, own_book: OwnOrderBook) -> anyhow::Result<()> {
2207 log::debug!("Adding `OwnOrderBook` {}", own_book.instrument_id);
2208
2209 self.own_books.insert(own_book.instrument_id, own_book);
2210 Ok(())
2211 }
2212
2213 pub fn add_mark_price(&mut self, mark_price: MarkPriceUpdate) -> anyhow::Result<()> {
2219 log::debug!("Adding `MarkPriceUpdate` for {}", mark_price.instrument_id);
2220
2221 if self.config.save_market_data {
2222 }
2224
2225 let mark_prices_deque = self
2226 .mark_prices
2227 .entry(mark_price.instrument_id)
2228 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2229 mark_prices_deque.push_front(mark_price);
2230 Ok(())
2231 }
2232
2233 pub fn add_index_price(&mut self, index_price: IndexPriceUpdate) -> anyhow::Result<()> {
2239 log::debug!(
2240 "Adding `IndexPriceUpdate` for {}",
2241 index_price.instrument_id
2242 );
2243
2244 if self.config.save_market_data {
2245 }
2247
2248 let index_prices_deque = self
2249 .index_prices
2250 .entry(index_price.instrument_id)
2251 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2252 index_prices_deque.push_front(index_price);
2253 Ok(())
2254 }
2255
2256 pub fn add_funding_rate(&mut self, funding_rate: FundingRateUpdate) -> anyhow::Result<()> {
2262 log::debug!(
2263 "Adding `FundingRateUpdate` for {}",
2264 funding_rate.instrument_id
2265 );
2266
2267 if self.config.save_market_data {
2268 }
2270
2271 let funding_rates_deque = self
2272 .funding_rates
2273 .entry(funding_rate.instrument_id)
2274 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2275 funding_rates_deque.push_front(funding_rate);
2276 Ok(())
2277 }
2278
2279 pub fn add_funding_rates(&mut self, funding_rates: &[FundingRateUpdate]) -> anyhow::Result<()> {
2285 check_slice_not_empty(funding_rates, stringify!(funding_rates))?;
2286
2287 let instrument_id = funding_rates[0].instrument_id;
2288 log::debug!(
2289 "Adding `FundingRateUpdate`[{}] {instrument_id}",
2290 funding_rates.len()
2291 );
2292
2293 if self.config.save_market_data
2294 && let Some(database) = &mut self.database
2295 {
2296 for funding_rate in funding_rates {
2297 database.add_funding_rate(funding_rate)?;
2298 }
2299 }
2300
2301 let funding_rate_deque = self
2302 .funding_rates
2303 .entry(instrument_id)
2304 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2305
2306 for funding_rate in funding_rates {
2307 funding_rate_deque.push_front(*funding_rate);
2308 }
2309 Ok(())
2310 }
2311
2312 pub fn add_instrument_status(&mut self, status: InstrumentStatus) -> anyhow::Result<()> {
2318 log::debug!("Adding `InstrumentStatus` for {}", status.instrument_id);
2319
2320 if self.config.save_market_data {
2321 }
2323
2324 let statuses_deque = self
2325 .instrument_statuses
2326 .entry(status.instrument_id)
2327 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2328 statuses_deque.push_front(status);
2329 Ok(())
2330 }
2331
2332 pub fn add_instrument_close(&mut self, close: InstrumentClose) -> anyhow::Result<()> {
2342 log::debug!("Adding `InstrumentClose` for {}", close.instrument_id);
2343
2344 if let Some(database) = &self.database {
2345 database.add_instrument_close(&close)?;
2346 }
2347
2348 self.instrument_closes.insert(close.instrument_id, close);
2349 Ok(())
2350 }
2351
2352 pub fn add_quote(&mut self, quote: QuoteTick) -> anyhow::Result<()> {
2358 log::debug!("Adding `QuoteTick` {}", quote.instrument_id);
2359
2360 if self.config.save_market_data
2361 && let Some(database) = &mut self.database
2362 {
2363 database.add_quote("e)?;
2364 }
2365
2366 let quotes_deque = self
2367 .quotes
2368 .entry(quote.instrument_id)
2369 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2370 quotes_deque.push_front(quote);
2371 Ok(())
2372 }
2373
2374 pub fn add_quotes(&mut self, quotes: &[QuoteTick]) -> anyhow::Result<()> {
2380 check_slice_not_empty(quotes, stringify!(quotes))?;
2381
2382 let instrument_id = quotes[0].instrument_id;
2383 log::debug!("Adding `QuoteTick`[{}] {instrument_id}", quotes.len());
2384
2385 if self.config.save_market_data
2386 && let Some(database) = &mut self.database
2387 {
2388 for quote in quotes {
2389 database.add_quote(quote)?;
2390 }
2391 }
2392
2393 let quotes_deque = self
2394 .quotes
2395 .entry(instrument_id)
2396 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2397
2398 for quote in quotes {
2399 quotes_deque.push_front(*quote);
2400 }
2401 Ok(())
2402 }
2403
2404 pub fn add_trade(&mut self, trade: TradeTick) -> anyhow::Result<()> {
2410 log::debug!("Adding `TradeTick` {}", trade.instrument_id);
2411
2412 if self.config.save_market_data
2413 && let Some(database) = &mut self.database
2414 {
2415 database.add_trade(&trade)?;
2416 }
2417
2418 let trades_deque = self
2419 .trades
2420 .entry(trade.instrument_id)
2421 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2422 trades_deque.push_front(trade);
2423 Ok(())
2424 }
2425
2426 pub fn add_trades(&mut self, trades: &[TradeTick]) -> anyhow::Result<()> {
2432 check_slice_not_empty(trades, stringify!(trades))?;
2433
2434 let instrument_id = trades[0].instrument_id;
2435 log::debug!("Adding `TradeTick`[{}] {instrument_id}", trades.len());
2436
2437 if self.config.save_market_data
2438 && let Some(database) = &mut self.database
2439 {
2440 for trade in trades {
2441 database.add_trade(trade)?;
2442 }
2443 }
2444
2445 let trades_deque = self
2446 .trades
2447 .entry(instrument_id)
2448 .or_insert_with(|| BoundedVecDeque::new(self.config.tick_capacity));
2449
2450 for trade in trades {
2451 trades_deque.push_front(*trade);
2452 }
2453 Ok(())
2454 }
2455
2456 pub fn add_bar(&mut self, bar: Bar) -> anyhow::Result<()> {
2465 log::debug!("Adding `Bar` {}", bar.bar_type);
2466
2467 if self.config.save_market_data
2468 && let Some(database) = &mut self.database
2469 {
2470 database.add_bar(&bar)?;
2471 }
2472
2473 let bars = self
2474 .bars
2475 .entry(bar.bar_type)
2476 .or_insert_with(|| BoundedVecDeque::new(self.config.bar_capacity));
2477 insert_bar(bars, bar);
2478 Ok(())
2479 }
2480
2481 pub fn add_bars(&mut self, bars: &[Bar]) -> anyhow::Result<()> {
2487 check_slice_not_empty(bars, stringify!(bars))?;
2488
2489 let bar_type = bars[0].bar_type;
2490 log::debug!("Adding `Bar`[{}] {bar_type}", bars.len());
2491
2492 if self.config.save_market_data
2493 && let Some(database) = &mut self.database
2494 {
2495 for bar in bars {
2496 database.add_bar(bar)?;
2497 }
2498 }
2499
2500 let bars_deque = self
2501 .bars
2502 .entry(bar_type)
2503 .or_insert_with(|| BoundedVecDeque::new(self.config.bar_capacity));
2504
2505 for bar in bars {
2506 insert_bar(bars_deque, *bar);
2507 }
2508 Ok(())
2509 }
2510
2511 pub fn add_bar_historical(&mut self, bar: Bar) -> anyhow::Result<()> {
2520 log::debug!("Adding historical `Bar` {}", bar.bar_type);
2521
2522 if self.config.save_market_data
2523 && let Some(database) = &mut self.database
2524 {
2525 database.add_bar(&bar)?;
2526 }
2527
2528 let bars = self
2529 .bars
2530 .entry(bar.bar_type)
2531 .or_insert_with(|| BoundedVecDeque::new(self.config.bar_capacity));
2532 insert_bar_historical(bars, bar);
2533 Ok(())
2534 }
2535
2536 pub fn add_greeks(&mut self, greeks: GreeksData) -> anyhow::Result<()> {
2542 log::debug!("Adding `GreeksData` {}", greeks.instrument_id);
2543
2544 if self.config.save_market_data
2545 && let Some(_database) = &mut self.database
2546 {
2547 }
2549
2550 self.greeks.insert(greeks.instrument_id, greeks);
2551 Ok(())
2552 }
2553
2554 pub fn greeks(&self, instrument_id: &InstrumentId) -> Option<GreeksData> {
2556 self.greeks.get(instrument_id).cloned()
2557 }
2558
2559 pub fn add_option_greeks(&mut self, greeks: OptionGreeks) {
2561 log::debug!("Adding `OptionGreeks` {}", greeks.instrument_id);
2562 self.option_greeks.insert(greeks.instrument_id, greeks);
2563 }
2564
2565 #[must_use]
2567 pub fn option_greeks(&self, instrument_id: &InstrumentId) -> Option<&OptionGreeks> {
2568 self.option_greeks.get(instrument_id)
2569 }
2570
2571 pub fn add_yield_curve(&mut self, yield_curve: YieldCurve) -> anyhow::Result<()> {
2579 yield_curve.validate()?;
2580
2581 log::debug!("Adding `YieldCurve` {}", yield_curve.curve_name);
2582
2583 if self.config.save_market_data
2584 && let Some(_database) = &mut self.database
2585 {
2586 }
2588
2589 self.yield_curves
2590 .insert(yield_curve.curve_name.clone(), yield_curve);
2591 Ok(())
2592 }
2593
2594 pub fn yield_curve(&self, key: &str) -> Option<Box<dyn Fn(f64) -> f64>> {
2596 self.yield_curves.get(key).map(|curve| {
2597 let curve_clone = curve.clone();
2598 Box::new(move |expiry_in_years: f64| curve_clone.get_rate(expiry_in_years))
2599 as Box<dyn Fn(f64) -> f64>
2600 })
2601 }
2602
2603 pub fn add_currency(&mut self, currency: Currency) -> anyhow::Result<()> {
2609 if self.currencies.contains_key(¤cy.code) {
2610 return Ok(());
2611 }
2612 log::debug!("Adding `Currency` {}", currency.code);
2613
2614 if let Some(database) = &mut self.database {
2615 database.add_currency(¤cy)?;
2616 }
2617
2618 self.currencies.insert(currency.code, currency);
2619 Ok(())
2620 }
2621
2622 pub fn add_instrument(&mut self, instrument: InstrumentAny) -> anyhow::Result<()> {
2628 log::debug!("Adding `Instrument` {}", instrument.id());
2629
2630 if let Some(base_currency) = instrument.base_currency() {
2632 self.add_currency(base_currency)?;
2633 }
2634 self.add_currency(instrument.quote_currency())?;
2635 self.add_currency(instrument.settlement_currency())?;
2636
2637 if let Some(database) = &mut self.database {
2638 database.add_instrument(&instrument)?;
2639 }
2640
2641 self.instruments.insert(instrument.id(), instrument);
2642 Ok(())
2643 }
2644
2645 pub fn add_synthetic(&mut self, synthetic: SyntheticInstrument) -> anyhow::Result<()> {
2651 log::debug!("Adding `SyntheticInstrument` {}", synthetic.id);
2652
2653 if let Some(database) = &mut self.database {
2654 database.add_synthetic(&synthetic)?;
2655 }
2656
2657 self.synthetics.insert(synthetic.id, synthetic);
2658 Ok(())
2659 }
2660
2661 fn state_currencies(state: &AccountState) -> impl Iterator<Item = Currency> + '_ {
2663 state
2664 .balances
2665 .iter()
2666 .map(|balance| balance.currency)
2667 .chain(state.margins.iter().map(|margin| margin.currency))
2668 .chain(state.base_currency)
2669 }
2670
2671 fn add_account_currencies(&mut self, account: &AccountAny) -> anyhow::Result<()> {
2678 let Some(state) = account.last_event() else {
2679 return Ok(());
2680 };
2681
2682 for currency in Self::state_currencies(&state) {
2683 self.add_currency(currency)?;
2684 }
2685 Ok(())
2686 }
2687
2688 pub fn add_account(&mut self, account: AccountAny) -> anyhow::Result<()> {
2694 log::debug!("Adding `Account` {}", account.id());
2695
2696 self.add_account_currencies(&account)?;
2697
2698 if let Some(database) = &mut self.database {
2699 database.add_account(&account)?;
2700 }
2701
2702 let account_id = account.id();
2703 self.accounts.insert(account_id, SharedCell::new(account));
2704 self.index.add_venue_account(account_id);
2705 Ok(())
2706 }
2707
2708 pub fn add_venue_order_id(
2717 &mut self,
2718 client_order_id: &ClientOrderId,
2719 venue_order_id: &VenueOrderId,
2720 overwrite: bool,
2721 ) -> anyhow::Result<()> {
2722 self.validate_venue_order_id_claim(client_order_id, venue_order_id, overwrite)?;
2723
2724 self.index
2725 .client_order_ids
2726 .insert(*client_order_id, *venue_order_id);
2727 self.index
2728 .venue_order_ids
2729 .insert(*venue_order_id, *client_order_id);
2730
2731 Ok(())
2732 }
2733
2734 pub fn index_venue_order_id(
2745 &mut self,
2746 client_order_id: &ClientOrderId,
2747 venue_order_id: &VenueOrderId,
2748 ) -> anyhow::Result<()> {
2749 self.validate_venue_order_id_ownership(client_order_id, venue_order_id)?;
2750
2751 self.index
2752 .venue_order_ids
2753 .insert(*venue_order_id, *client_order_id);
2754 self.index
2755 .client_order_ids
2756 .entry(*client_order_id)
2757 .or_insert(*venue_order_id);
2758
2759 Ok(())
2760 }
2761
2762 fn validate_venue_order_id_claim(
2763 &self,
2764 client_order_id: &ClientOrderId,
2765 venue_order_id: &VenueOrderId,
2766 overwrite: bool,
2767 ) -> anyhow::Result<()> {
2768 self.validate_venue_order_id_ownership(client_order_id, venue_order_id)?;
2769
2770 if let Some(existing_venue_order_id) = self.index.client_order_ids.get(client_order_id)
2771 && !overwrite
2772 && existing_venue_order_id != venue_order_id
2773 {
2774 anyhow::bail!(
2775 "Existing {existing_venue_order_id} for {client_order_id}
2776 did not match the given {venue_order_id}.
2777 If you are writing a test then try a different `venue_order_id`,
2778 otherwise this is probably a bug."
2779 );
2780 }
2781
2782 Ok(())
2783 }
2784
2785 fn validate_venue_order_id_ownership(
2786 &self,
2787 client_order_id: &ClientOrderId,
2788 venue_order_id: &VenueOrderId,
2789 ) -> anyhow::Result<()> {
2790 if let Some(existing_client_order_id) = self.index.venue_order_ids.get(venue_order_id)
2791 && existing_client_order_id != client_order_id
2792 {
2793 return Err(VenueOrderIdOwnershipError {
2794 venue_order_id: *venue_order_id,
2795 existing_client_order_id: *existing_client_order_id,
2796 claimant_client_order_id: *client_order_id,
2797 }
2798 .into());
2799 }
2800
2801 Ok(())
2802 }
2803
2804 pub fn add_order(
2819 &mut self,
2820 order: OrderAny,
2821 position_id: Option<PositionId>,
2822 client_id: Option<ClientId>,
2823 replace_existing: bool,
2824 ) -> anyhow::Result<()> {
2825 let instrument_id = order.instrument_id();
2826 let venue = instrument_id.venue;
2827 let client_order_id = order.client_order_id();
2828 let strategy_id = order.strategy_id();
2829 let exec_algorithm_id = order.exec_algorithm_id();
2830 let exec_spawn_id = order.exec_spawn_id();
2831
2832 if !replace_existing {
2833 check_key_not_in_map(
2834 &client_order_id,
2835 &self.orders,
2836 stringify!(client_order_id),
2837 stringify!(orders),
2838 )?;
2839 }
2840
2841 log::debug!("Adding {order:?}");
2842
2843 self.index.orders.insert(client_order_id);
2844
2845 if order.is_active_local() {
2846 self.index.orders_active_local.insert(client_order_id);
2847 }
2848 self.index
2849 .order_strategy
2850 .insert(client_order_id, strategy_id);
2851 self.index.strategies.insert(strategy_id);
2852
2853 self.index
2855 .venue_orders
2856 .entry(venue)
2857 .or_default()
2858 .insert(client_order_id);
2859
2860 self.index
2862 .instrument_orders
2863 .entry(instrument_id)
2864 .or_default()
2865 .insert(client_order_id);
2866
2867 self.index
2869 .strategy_orders
2870 .entry(strategy_id)
2871 .or_default()
2872 .insert(client_order_id);
2873
2874 if let Some(account_id) = order.account_id() {
2876 self.index
2877 .account_orders
2878 .entry(account_id)
2879 .or_default()
2880 .insert(client_order_id);
2881 }
2882
2883 if let Some(exec_algorithm_id) = exec_algorithm_id {
2885 self.index.exec_algorithms.insert(exec_algorithm_id);
2886
2887 self.index
2888 .exec_algorithm_orders
2889 .entry(exec_algorithm_id)
2890 .or_default()
2891 .insert(client_order_id);
2892 }
2893
2894 if let Some(exec_spawn_id) = exec_spawn_id {
2896 self.index
2897 .exec_spawn_orders
2898 .entry(exec_spawn_id)
2899 .or_default()
2900 .insert(client_order_id);
2901 }
2902
2903 if order.emulation_trigger().is_some() {
2905 self.index.orders_emulated.insert(client_order_id);
2906 }
2907
2908 if let Some(position_id) = position_id {
2910 self.index_position_id_in_memory(&position_id, &venue, &client_order_id, &strategy_id);
2911 }
2912
2913 if let Some(client_id) = client_id {
2915 self.index.order_client.insert(client_order_id, client_id);
2916 log::debug!("Indexed {client_id:?}");
2917 }
2918
2919 let order_cell = if let Some(order_cell) = self.orders.get(&client_order_id) {
2922 *order_cell.borrow_mut() = order;
2923 order_cell.clone()
2924 } else {
2925 let order_cell = SharedCell::new(order);
2926 self.orders.insert(client_order_id, order_cell.clone());
2927 order_cell
2928 };
2929
2930 if let Some(position_id) = position_id {
2931 self.persist_position_id(&position_id, &client_order_id)?;
2932 }
2933
2934 if let Some(database) = &mut self.database {
2935 database.add_order(&order_cell.borrow(), client_id)?;
2936 }
2941
2942 Ok(())
2943 }
2944
2945 pub fn claim_order_clients(
2957 &mut self,
2958 claims: &[(ClientOrderId, ClientId)],
2959 ) -> anyhow::Result<()> {
2960 let mut requested = AHashMap::with_capacity(claims.len());
2961 let mut ordered_claims = Vec::with_capacity(claims.len());
2962
2963 for (client_order_id, client_id) in claims {
2964 if let Some(existing_client_id) = requested.get(client_order_id) {
2965 if existing_client_id != client_id {
2966 anyhow::bail!(
2967 "Conflicting execution client claims for {client_order_id}: \
2968 {existing_client_id} and {client_id}"
2969 );
2970 }
2971 continue;
2972 }
2973
2974 requested.insert(*client_order_id, *client_id);
2975 ordered_claims.push((*client_order_id, *client_id));
2976 }
2977
2978 let mut pending_claims = Vec::with_capacity(ordered_claims.len());
2979 for (client_order_id, client_id) in ordered_claims {
2980 if !self.orders.contains_key(&client_order_id) {
2981 return Err(OrderLookupError::not_found(client_order_id).into());
2982 }
2983
2984 match self.index.order_client.get(&client_order_id) {
2985 Some(existing_client_id) if *existing_client_id == client_id => {}
2986 Some(existing_client_id) => {
2987 anyhow::bail!(
2988 "Order {client_order_id} is already claimed by execution client \
2989 {existing_client_id} and cannot be claimed by {client_id}"
2990 );
2991 }
2992 None => pending_claims.push((client_order_id, client_id)),
2993 }
2994 }
2995
2996 if pending_claims.is_empty() {
2997 return Ok(());
2998 }
2999
3000 if let Some(database) = &self.database {
3001 database.index_order_clients(&pending_claims)?;
3002 }
3003
3004 for (client_order_id, client_id) in pending_claims {
3005 self.index.order_client.insert(client_order_id, client_id);
3006 log::debug!("Claimed {client_order_id} for execution client {client_id}");
3007 }
3008
3009 Ok(())
3010 }
3011
3012 pub fn add_order_list(&mut self, order_list: OrderList) -> anyhow::Result<()> {
3018 let order_list_id = order_list.id;
3019 check_key_not_in_map(
3020 &order_list_id,
3021 &self.order_lists,
3022 stringify!(order_list_id),
3023 stringify!(order_lists),
3024 )?;
3025
3026 log::debug!("Adding {order_list}");
3027 self.order_lists.insert(order_list_id, order_list);
3028 Ok(())
3029 }
3030
3031 pub fn add_position_id(
3042 &mut self,
3043 position_id: &PositionId,
3044 venue: &Venue,
3045 client_order_id: &ClientOrderId,
3046 strategy_id: &StrategyId,
3047 ) -> anyhow::Result<()> {
3048 self.index_position_id_in_memory(position_id, venue, client_order_id, strategy_id);
3049 self.persist_position_id(position_id, client_order_id)
3050 }
3051
3052 fn index_position_id_in_memory(
3053 &mut self,
3054 position_id: &PositionId,
3055 venue: &Venue,
3056 client_order_id: &ClientOrderId,
3057 strategy_id: &StrategyId,
3058 ) {
3059 self.index
3060 .order_position
3061 .insert(*client_order_id, *position_id);
3062 self.index_position(position_id, venue, strategy_id);
3063 self.index
3064 .position_orders
3065 .entry(*position_id)
3066 .or_default()
3067 .insert(*client_order_id);
3068 }
3069
3070 fn persist_position_id(
3071 &mut self,
3072 position_id: &PositionId,
3073 client_order_id: &ClientOrderId,
3074 ) -> anyhow::Result<()> {
3075 if let Some(database) = &mut self.database {
3076 database.index_order_position(*client_order_id, *position_id)?;
3077 }
3078
3079 Ok(())
3080 }
3081
3082 fn index_position(
3083 &mut self,
3084 position_id: &PositionId,
3085 venue: &Venue,
3086 strategy_id: &StrategyId,
3087 ) {
3088 let strategy_id = self
3089 .positions
3090 .get(position_id)
3091 .map(|position| position.borrow().strategy_id)
3092 .filter(StrategyId::is_external)
3093 .unwrap_or(*strategy_id);
3094
3095 self.index
3097 .position_strategy
3098 .insert(*position_id, strategy_id);
3099
3100 self.index.position_orders.entry(*position_id).or_default();
3102
3103 self.index
3105 .strategy_positions
3106 .entry(strategy_id)
3107 .or_default()
3108 .insert(*position_id);
3109
3110 self.index
3112 .venue_positions
3113 .entry(*venue)
3114 .or_default()
3115 .insert(*position_id);
3116 }
3117
3118 fn assign_position_ids_to_contingencies(&mut self) {
3126 let mut assignments: Vec<(PositionId, ClientOrderId)> = Vec::new();
3127
3128 for parent_order_cell in self.orders.values() {
3129 let parent = parent_order_cell.borrow();
3130 if parent.contingency_type() != Some(ContingencyType::Oto) {
3131 continue;
3132 }
3133 let Some(parent_position_id) = parent.position_id() else {
3134 continue;
3135 };
3136 let Some(linked_order_ids) = parent.linked_order_ids() else {
3137 continue;
3138 };
3139
3140 for client_order_id in linked_order_ids {
3141 match self.orders.get(client_order_id) {
3142 None => {
3143 log::error!("Contingency order {client_order_id} not found");
3144 }
3145 Some(contingent_order_cell) => {
3146 if contingent_order_cell.borrow().position_id().is_none() {
3147 assignments.push((parent_position_id, *client_order_id));
3148 }
3149 }
3150 }
3151 }
3152 }
3153
3154 for (position_id, client_order_id) in assignments {
3155 let Some((venue, strategy_id)) = self.orders.get(&client_order_id).map(|order_cell| {
3156 let mut contingent = order_cell.borrow_mut();
3157 contingent.set_position_id(Some(position_id));
3158 (contingent.instrument_id().venue, contingent.strategy_id())
3159 }) else {
3160 continue;
3161 };
3162
3163 if let Err(e) =
3166 self.add_position_id(&position_id, &venue, &client_order_id, &strategy_id)
3167 {
3168 log::error!("Failed to re-index {client_order_id} -> {position_id}: {e}");
3169 }
3170 }
3171 }
3172
3173 pub fn add_position(&mut self, position: &Position, oms_type: OmsType) -> anyhow::Result<()> {
3184 self.add_position_inner(position.clone(), oms_type, true, false)
3185 }
3186
3187 pub fn add_position_without_order(
3197 &mut self,
3198 position: &Position,
3199 oms_type: OmsType,
3200 ) -> anyhow::Result<()> {
3201 self.add_position_inner(position.clone(), oms_type, false, false)
3202 }
3203
3204 pub fn replace_position(
3217 &mut self,
3218 position: &Position,
3219 oms_type: OmsType,
3220 index_order: bool,
3221 carry_replay_state: bool,
3222 ) -> anyhow::Result<()> {
3223 self.add_position_inner(position.clone(), oms_type, index_order, carry_replay_state)
3224 }
3225
3226 fn add_position_inner(
3227 &mut self,
3228 mut position: Position,
3229 oms_type: OmsType,
3230 index_order: bool,
3231 carry_replay_state: bool,
3232 ) -> anyhow::Result<()> {
3233 let oms_type = resolve_position_oms(&position, oms_type);
3234
3235 let key = position_oms_key(position.id);
3240 check_valid_string_utf8(&key, stringify!(key))?;
3241 let value = Bytes::from(serde_json::to_vec(&oms_type)?);
3242 check_predicate_false(value.is_empty(), stringify!(value))?;
3243
3244 let position_id = position.id;
3245 let strategy_id = position.strategy_id;
3246 let instrument_id = position.instrument_id;
3247 let account_id = position.account_id;
3248 let opening_order_id = position.opening_order_id;
3249
3250 log::debug!("Adding {position}");
3251
3252 let position_cell = if let Some(position_cell) = self.positions.get(&position_id).cloned() {
3256 let mut prior = position_cell.borrow_mut();
3257 if carry_replay_state {
3258 position.transfer_replay_state_from(&mut prior);
3259 }
3260 *prior = position;
3261 drop(prior);
3262 position_cell
3263 } else {
3264 let position_cell = SharedCell::new(position);
3265 self.positions.insert(position_id, position_cell.clone());
3266 position_cell
3267 };
3268
3269 self.index.position_oms.insert(position_id, oms_type);
3270 self.index.positions.insert(position_id);
3271 self.index.positions_open.insert(position_id);
3272 self.index.positions_closed.remove(&position_id); self.index.strategies.insert(strategy_id);
3274 self.index.strategy_orders.entry(strategy_id).or_default();
3275
3276 if index_order {
3277 self.index_position_id_in_memory(
3278 &position_id,
3279 &instrument_id.venue,
3280 &opening_order_id,
3281 &strategy_id,
3282 );
3283 } else {
3284 self.index_position(&position_id, &instrument_id.venue, &strategy_id);
3285 }
3286
3287 let instrument_positions = self
3289 .index
3290 .instrument_positions
3291 .entry(instrument_id)
3292 .or_default();
3293 instrument_positions.insert(position_id);
3294 self.index
3295 .instrument_orders
3296 .entry(instrument_id)
3297 .or_default();
3298
3299 self.index
3301 .account_positions
3302 .entry(account_id)
3303 .or_default()
3304 .insert(position_id);
3305
3306 log::debug!("Adding general {key}");
3307 self.general.insert(key.clone(), value.clone());
3308
3309 if index_order {
3310 self.persist_position_id(&position_id, &opening_order_id)?;
3311 }
3312
3313 if let Some(database) = &mut self.database {
3314 database.add_position(&position_cell.borrow())?;
3315 database.add(key, value)?;
3324 }
3325
3326 Ok(())
3327 }
3328
3329 pub fn update_account(&mut self, account: &AccountAny) -> anyhow::Result<()> {
3338 let account_id = account.id();
3339 match self.accounts.get(&account_id) {
3340 Some(account_cell) => *account_cell.borrow_mut() = account.clone(),
3341 None => {
3342 self.accounts
3343 .insert(account_id, SharedCell::new(account.clone()));
3344 }
3345 }
3346
3347 self.add_account_currencies(account)?;
3348
3349 if let Some(database) = &mut self.database {
3350 database.update_account(account)?;
3351 }
3352 Ok(())
3353 }
3354
3355 #[must_use]
3366 pub fn take_account(&mut self, account_id: &AccountId) -> Option<AccountAny> {
3367 let cell = self.accounts.remove(account_id)?;
3368 let rc: Rc<RefCell<AccountAny>> = cell.into();
3369
3370 match Rc::try_unwrap(rc) {
3371 Ok(cell) => Some(cell.into_inner()),
3372 Err(rc) => {
3373 log::error!(
3374 "Cannot move account {account_id} out of cache: account cell has an outstanding owner"
3375 );
3376 self.accounts.insert(*account_id, rc.into());
3377 None
3378 }
3379 }
3380 }
3381
3382 pub fn cache_account_owned(&mut self, account: AccountAny) {
3384 let account_id = account.id();
3385 self.index.add_venue_account(account_id);
3386 match self.accounts.get(&account_id) {
3387 Some(account_cell) => *account_cell.borrow_mut() = account,
3388 None => {
3389 self.accounts.insert(account_id, SharedCell::new(account));
3390 }
3391 }
3392 }
3393
3394 pub fn update_account_owned(&mut self, account: AccountAny) -> anyhow::Result<()> {
3400 let account_id = account.id();
3401 let currencies: Vec<Currency> = account
3404 .last_event()
3405 .map(|state| Self::state_currencies(&state).collect())
3406 .unwrap_or_default();
3407 self.cache_account_owned(account);
3408
3409 for currency in currencies {
3410 self.add_currency(currency)?;
3411 }
3412
3413 if let Some(database) = &mut self.database {
3414 let Some(account_cell) = self.accounts.get(&account_id) else {
3415 anyhow::bail!("Account {account_id} not found after cache update");
3416 };
3417 database.update_account(&account_cell.borrow())?;
3418 }
3419 Ok(())
3420 }
3421
3422 pub fn update_account_state(&mut self, event: &AccountState) -> anyhow::Result<()> {
3432 let Some(cell) = self.accounts.get(&event.account_id) else {
3433 return self.add_account(AccountAny::from_events(std::slice::from_ref(event))?);
3434 };
3435
3436 cell.borrow_mut().apply(event.clone())?;
3439
3440 for currency in Self::state_currencies(event) {
3443 self.add_currency(currency)?;
3444 }
3445
3446 let Some(cell) = self.accounts.get(&event.account_id) else {
3447 anyhow::bail!("Account {} not found after apply", event.account_id);
3448 };
3449
3450 if let Some(database) = &mut self.database {
3451 database.update_account(&cell.borrow())?;
3452 }
3453 Ok(())
3454 }
3455
3456 pub fn replace_order(&mut self, order: &OrderAny) -> anyhow::Result<()> {
3467 let client_order_id = order.client_order_id();
3468 if let Some(venue_order_id) = order.venue_order_id() {
3469 self.validate_venue_order_id_ownership(&client_order_id, &venue_order_id)?;
3470 }
3471
3472 match self.orders.get(&client_order_id) {
3473 Some(order_cell) => *order_cell.borrow_mut() = order.clone(),
3476 None => {
3477 self.orders
3478 .insert(client_order_id, SharedCell::new(order.clone()));
3479 }
3480 }
3481
3482 self.refresh_order(order)
3483 }
3484
3485 pub fn update_order(&mut self, event: &OrderEventAny) -> anyhow::Result<OrderAny> {
3491 let event_client_order_id = event.client_order_id();
3492 let client_order_id = if self.order_exists(&event_client_order_id) {
3493 event_client_order_id
3494 } else if let Some(venue_order_id) = event.venue_order_id() {
3495 self.index
3496 .venue_order_ids
3497 .get(&venue_order_id)
3498 .copied()
3499 .ok_or(OrderError::NotFound(event_client_order_id))?
3500 } else {
3501 return Err(OrderError::NotFound(event_client_order_id).into());
3502 };
3503
3504 let order_cell = self
3505 .orders
3506 .get(&client_order_id)
3507 .cloned()
3508 .ok_or(OrderError::NotFound(client_order_id))?;
3509
3510 let mut snapshot = order_cell.borrow().clone();
3514 snapshot.apply(event.clone())?;
3515
3516 if let Some(venue_order_id) = snapshot.venue_order_id() {
3520 self.validate_venue_order_id_ownership(&client_order_id, &venue_order_id)?;
3521 }
3522
3523 *order_cell.borrow_mut() = snapshot.clone();
3524
3525 if matches!(event, OrderEventAny::FillVoided(_)) {
3526 self.reindex_hedging_position(client_order_id);
3527 }
3528
3529 if let Err(e) = self.refresh_order(&snapshot) {
3530 log::error!("Error updating order in cache: {e}");
3531 }
3532
3533 Ok(snapshot)
3534 }
3535
3536 fn refresh_order(&mut self, order: &OrderAny) -> anyhow::Result<()> {
3537 let client_order_id = order.client_order_id();
3538
3539 if let Some(venue_order_id) = order.venue_order_id() {
3542 let overwrite = matches!(order.last_event(), OrderEventAny::Updated(_));
3543 if let Err(e) = self.add_venue_order_id(&client_order_id, &venue_order_id, overwrite) {
3544 if e.is::<VenueOrderIdOwnershipError>() {
3545 return Err(e);
3546 }
3547 log::error!("Error indexing venue order ID in cache: {e}");
3548 }
3549 }
3550
3551 if order.is_active_local() {
3552 self.index.orders_active_local.insert(client_order_id);
3553 } else {
3554 self.index.orders_active_local.remove(&client_order_id);
3555 }
3556
3557 if order.is_inflight() {
3559 self.index.orders_inflight.insert(client_order_id);
3560 } else {
3561 self.index.orders_inflight.remove(&client_order_id);
3562 }
3563
3564 if order.is_open() {
3566 self.index.orders_closed.remove(&client_order_id);
3567 self.index.orders_open.insert(client_order_id);
3568 } else {
3569 self.index.orders_open.remove(&client_order_id);
3570 }
3571
3572 if order.is_closed() {
3573 self.index.orders_pending_cancel.remove(&client_order_id);
3574 self.index.orders_closed.insert(client_order_id);
3575 }
3576
3577 if matches!(order.last_event(), OrderEventAny::CancelRejected(_)) {
3579 self.index.orders_pending_cancel.remove(&client_order_id);
3580 }
3581
3582 if order.emulation_trigger().is_some() && !order.is_closed() {
3584 self.index.orders_emulated.insert(client_order_id);
3585 } else {
3586 self.index.orders_emulated.remove(&client_order_id);
3587 }
3588
3589 if let Some(account_id) = order.account_id() {
3591 self.index
3592 .account_orders
3593 .entry(account_id)
3594 .or_default()
3595 .insert(client_order_id);
3596 }
3597
3598 if !self.own_books.is_empty() {
3600 let own_book = self.own_order_book(&order.instrument_id());
3601 if (own_book.is_some() && order.is_closed()) || should_handle_own_book_order(order) {
3602 self.update_own_order_book(order);
3603 }
3604 }
3605
3606 if let Some(database) = &mut self.database {
3607 database.update_order(order.last_event())?;
3608 }
3613
3614 Ok(())
3615 }
3616
3617 pub fn update_order_pending_cancel_local(&mut self, order: &OrderAny) {
3619 self.index
3620 .orders_pending_cancel
3621 .insert(order.client_order_id());
3622 }
3623
3624 pub fn update_position(&mut self, position: &Position) -> anyhow::Result<()> {
3634 let Some(position_cell) = self.positions.get(&position.id).cloned() else {
3635 anyhow::bail!("Cannot update position {}: not found in cache", position.id);
3636 };
3637
3638 self.refresh_position_indexes(position);
3639
3640 *position_cell.borrow_mut() = position.clone();
3641
3642 if let Some(database) = &mut self.database {
3643 database.update_position(position)?;
3644 }
3649
3650 Ok(())
3651 }
3652
3653 pub fn update_position_from_fill(
3663 &mut self,
3664 position_id: PositionId,
3665 fill: &OrderFilled,
3666 ) -> anyhow::Result<Position> {
3667 let Some(position_cell) = self.positions.get(&position_id).cloned() else {
3668 anyhow::bail!("Cannot update position {position_id}: not found in cache");
3669 };
3670
3671 let mut resolved_fill;
3672 let fill = if fill.position_id.is_none() {
3673 resolved_fill = fill.clone();
3674 resolved_fill.position_id = Some(position_id);
3675 &resolved_fill
3676 } else {
3677 fill
3678 };
3679
3680 let position = {
3681 let mut position = position_cell.borrow_mut();
3682 position.try_apply(fill)?;
3683 position.clone_without_events()
3684 };
3685
3686 self.refresh_position_indexes(&position);
3687
3688 if let Some(database) = &mut self.database {
3689 database.update_position(&position_cell.borrow())?;
3690 }
3691
3692 Ok(position)
3693 }
3694
3695 pub fn update_position_from_adjustment(
3706 &mut self,
3707 position_id: PositionId,
3708 adjustment: PositionAdjusted,
3709 ) -> anyhow::Result<()> {
3710 let Some(position_cell) = self.positions.get(&position_id).cloned() else {
3711 anyhow::bail!("Cannot update position {position_id}: not found in cache");
3712 };
3713
3714 position_cell.borrow_mut().apply_adjustment(adjustment)?;
3715 self.refresh_position_indexes(&position_cell.borrow());
3716
3717 if let Some(database) = &mut self.database {
3718 database.update_position(&position_cell.borrow())?;
3719 }
3720
3721 Ok(())
3722 }
3723
3724 pub fn revert_position_adjustment(&mut self, prior: &Position) -> anyhow::Result<()> {
3735 let Some(position_cell) = self.positions.get(&prior.id).cloned() else {
3736 anyhow::bail!("Cannot revert position {}: not found in cache", prior.id);
3737 };
3738
3739 position_cell.borrow_mut().revert_last_adjustment(prior)?;
3740 self.refresh_position_indexes(&position_cell.borrow());
3741
3742 if let Some(database) = &mut self.database {
3743 database.update_position(&position_cell.borrow())?;
3744 }
3745
3746 Ok(())
3747 }
3748
3749 pub fn update_position_from_instrument_close(
3761 &mut self,
3762 position_id: PositionId,
3763 close: InstrumentClose,
3764 ) -> anyhow::Result<Position> {
3765 let Some(position_cell) = self.positions.get(&position_id).cloned() else {
3766 anyhow::bail!("Cannot update position {position_id}: not found in cache");
3767 };
3768
3769 let position = {
3770 let mut position = position_cell.borrow_mut();
3771 position.apply_instrument_close(close)?;
3772 position.clone_without_events()
3773 };
3774
3775 self.refresh_position_indexes(&position);
3776
3777 if let Some(database) = &mut self.database
3778 && let Err(e) = database.update_position(&position_cell.borrow())
3779 {
3780 log::error!("Failed to persist settled position {position_id}: {e}");
3781 }
3782
3783 Ok(position)
3784 }
3785
3786 fn refresh_position_indexes(&mut self, position: &Position) {
3787 if position.is_open() {
3788 self.index.positions_open.insert(position.id);
3789 self.index.positions_closed.remove(&position.id);
3790 } else {
3791 self.index.positions_closed.insert(position.id);
3792 self.index.positions_open.remove(&position.id);
3793 }
3794 }
3795
3796 #[must_use]
3798 pub fn oms_type(&self, position_id: &PositionId) -> Option<OmsType> {
3799 self.index.position_oms.get(position_id).copied()
3800 }
3801
3802 pub fn snapshot_order_state(&self, order: &OrderAny) -> anyhow::Result<()> {
3808 let Some(database) = &self.database else {
3809 log::warn!(
3810 "Cannot snapshot order state for {} (no database configured)",
3811 order.client_order_id()
3812 );
3813 return Ok(());
3814 };
3815
3816 database.snapshot_order_state(order)
3817 }
3818
3819 fn collect_order_filter_sources<'a>(
3830 &'a self,
3831 venue: Option<&Venue>,
3832 instrument_id: Option<&InstrumentId>,
3833 strategy_id: Option<&StrategyId>,
3834 account_id: Option<&AccountId>,
3835 ) -> FilterSources<'a, ClientOrderId> {
3836 let mut sources: Vec<&AHashSet<ClientOrderId>> = Vec::with_capacity(4);
3837
3838 if let Some(venue) = venue {
3839 match self.index.venue_orders.get(venue) {
3840 Some(set) => sources.push(set),
3841 None => return FilterSources::Empty,
3842 }
3843 }
3844
3845 if let Some(instrument_id) = instrument_id {
3846 match self.index.instrument_orders.get(instrument_id) {
3847 Some(set) => sources.push(set),
3848 None => return FilterSources::Empty,
3849 }
3850 }
3851
3852 if let Some(strategy_id) = strategy_id {
3853 match self.index.strategy_orders.get(strategy_id) {
3854 Some(set) => sources.push(set),
3855 None => return FilterSources::Empty,
3856 }
3857 }
3858
3859 if let Some(account_id) = account_id {
3860 match self.index.account_orders.get(account_id) {
3861 Some(set) => sources.push(set),
3862 None => return FilterSources::Empty,
3863 }
3864 }
3865
3866 if sources.is_empty() {
3867 FilterSources::Unfiltered
3868 } else {
3869 FilterSources::Sets(sources)
3870 }
3871 }
3872
3873 fn collect_position_filter_sources<'a>(
3874 &'a self,
3875 venue: Option<&Venue>,
3876 instrument_id: Option<&InstrumentId>,
3877 strategy_id: Option<&StrategyId>,
3878 account_id: Option<&AccountId>,
3879 ) -> FilterSources<'a, PositionId> {
3880 let mut sources: Vec<&AHashSet<PositionId>> = Vec::with_capacity(4);
3881
3882 if let Some(venue) = venue {
3883 match self.index.venue_positions.get(venue) {
3884 Some(set) => sources.push(set),
3885 None => return FilterSources::Empty,
3886 }
3887 }
3888
3889 if let Some(instrument_id) = instrument_id {
3890 match self.index.instrument_positions.get(instrument_id) {
3891 Some(set) => sources.push(set),
3892 None => return FilterSources::Empty,
3893 }
3894 }
3895
3896 if let Some(strategy_id) = strategy_id {
3897 match self.index.strategy_positions.get(strategy_id) {
3898 Some(set) => sources.push(set),
3899 None => return FilterSources::Empty,
3900 }
3901 }
3902
3903 if let Some(account_id) = account_id {
3904 match self.index.account_positions.get(account_id) {
3905 Some(set) => sources.push(set),
3906 None => return FilterSources::Empty,
3907 }
3908 }
3909
3910 if sources.is_empty() {
3911 FilterSources::Unfiltered
3912 } else {
3913 FilterSources::Sets(sources)
3914 }
3915 }
3916
3917 fn query_orders_in_bucket(
3923 &self,
3924 bucket: &AHashSet<ClientOrderId>,
3925 venue: Option<&Venue>,
3926 instrument_id: Option<&InstrumentId>,
3927 strategy_id: Option<&StrategyId>,
3928 account_id: Option<&AccountId>,
3929 ) -> AHashSet<ClientOrderId> {
3930 match self.collect_order_filter_sources(venue, instrument_id, strategy_id, account_id) {
3931 FilterSources::Empty => AHashSet::new(),
3932 FilterSources::Unfiltered => bucket.clone(),
3933 FilterSources::Sets(sources) => intersect_pair_or_many(bucket, sources),
3934 }
3935 }
3936
3937 fn query_positions_in_bucket(
3938 &self,
3939 bucket: &AHashSet<PositionId>,
3940 venue: Option<&Venue>,
3941 instrument_id: Option<&InstrumentId>,
3942 strategy_id: Option<&StrategyId>,
3943 account_id: Option<&AccountId>,
3944 ) -> AHashSet<PositionId> {
3945 match self.collect_position_filter_sources(venue, instrument_id, strategy_id, account_id) {
3946 FilterSources::Empty => AHashSet::new(),
3947 FilterSources::Unfiltered => bucket.clone(),
3948 FilterSources::Sets(sources) => intersect_pair_or_many(bucket, sources),
3949 }
3950 }
3951
3952 fn view_orders_in_bucket<'a>(
3955 &'a self,
3956 bucket: &'a AHashSet<ClientOrderId>,
3957 venue: Option<&Venue>,
3958 instrument_id: Option<&InstrumentId>,
3959 strategy_id: Option<&StrategyId>,
3960 account_id: Option<&AccountId>,
3961 ) -> Cow<'a, AHashSet<ClientOrderId>> {
3962 match self.collect_order_filter_sources(venue, instrument_id, strategy_id, account_id) {
3963 FilterSources::Empty => Cow::Owned(AHashSet::new()),
3964 FilterSources::Unfiltered => Cow::Borrowed(bucket),
3965 FilterSources::Sets(sources) => Cow::Owned(intersect_pair_or_many(bucket, sources)),
3966 }
3967 }
3968
3969 fn view_positions_in_bucket<'a>(
3970 &'a self,
3971 bucket: &'a AHashSet<PositionId>,
3972 venue: Option<&Venue>,
3973 instrument_id: Option<&InstrumentId>,
3974 strategy_id: Option<&StrategyId>,
3975 account_id: Option<&AccountId>,
3976 ) -> Cow<'a, AHashSet<PositionId>> {
3977 match self.collect_position_filter_sources(venue, instrument_id, strategy_id, account_id) {
3978 FilterSources::Empty => Cow::Owned(AHashSet::new()),
3979 FilterSources::Unfiltered => Cow::Borrowed(bucket),
3980 FilterSources::Sets(sources) => Cow::Owned(intersect_pair_or_many(bucket, sources)),
3981 }
3982 }
3983
3984 fn iter_orders_in_bucket<'a>(
3989 &'a self,
3990 bucket: &'a AHashSet<ClientOrderId>,
3991 venue: Option<&Venue>,
3992 instrument_id: Option<&InstrumentId>,
3993 strategy_id: Option<&StrategyId>,
3994 account_id: Option<&AccountId>,
3995 ) -> Box<dyn Iterator<Item = ClientOrderId> + 'a> {
3996 match self.collect_order_filter_sources(venue, instrument_id, strategy_id, account_id) {
3997 FilterSources::Empty => Box::new(std::iter::empty()),
3998 FilterSources::Unfiltered => Box::new(bucket.iter().copied()),
3999 FilterSources::Sets(mut sources) => {
4000 sources.push(bucket);
4001 sources.sort_unstable_by_key(|s| s.len());
4002 let driver = sources[0];
4003 let rest: Vec<&'a AHashSet<ClientOrderId>> = sources[1..].to_vec();
4004 Box::new(
4005 driver
4006 .iter()
4007 .copied()
4008 .filter(move |id| rest.iter().all(|s| s.contains(id))),
4009 )
4010 }
4011 }
4012 }
4013
4014 fn iter_positions_in_bucket<'a>(
4015 &'a self,
4016 bucket: &'a AHashSet<PositionId>,
4017 venue: Option<&Venue>,
4018 instrument_id: Option<&InstrumentId>,
4019 strategy_id: Option<&StrategyId>,
4020 account_id: Option<&AccountId>,
4021 ) -> Box<dyn Iterator<Item = PositionId> + 'a> {
4022 match self.collect_position_filter_sources(venue, instrument_id, strategy_id, account_id) {
4023 FilterSources::Empty => Box::new(std::iter::empty()),
4024 FilterSources::Unfiltered => Box::new(bucket.iter().copied()),
4025 FilterSources::Sets(mut sources) => {
4026 sources.push(bucket);
4027 sources.sort_unstable_by_key(|s| s.len());
4028 let driver = sources[0];
4029 let rest: Vec<&'a AHashSet<PositionId>> = sources[1..].to_vec();
4030 Box::new(
4031 driver
4032 .iter()
4033 .copied()
4034 .filter(move |id| rest.iter().all(|s| s.contains(id))),
4035 )
4036 }
4037 }
4038 }
4039
4040 fn count_orders_in_bucket(
4046 &self,
4047 bucket: &AHashSet<ClientOrderId>,
4048 venue: Option<&Venue>,
4049 instrument_id: Option<&InstrumentId>,
4050 strategy_id: Option<&StrategyId>,
4051 account_id: Option<&AccountId>,
4052 side: Option<OrderSide>,
4053 ) -> usize {
4054 match self.collect_order_filter_sources(venue, instrument_id, strategy_id, account_id) {
4055 FilterSources::Empty => 0,
4056 FilterSources::Unfiltered => side.map_or_else(
4057 || bucket.len(),
4058 |side| {
4059 bucket
4060 .iter()
4061 .filter(|id| self.order_side_matches(id, side))
4062 .count()
4063 },
4064 ),
4065 FilterSources::Sets(mut sources) => {
4066 sources.push(bucket);
4067 sources.sort_unstable_by_key(|s| s.len());
4068 let driver = sources[0];
4069 let rest = &sources[1..];
4070
4071 driver
4072 .iter()
4073 .filter(|id| rest.iter().all(|s| s.contains(id)))
4074 .filter(|id| side.is_none_or(|side| self.order_side_matches(id, side)))
4075 .count()
4076 }
4077 }
4078 }
4079
4080 fn count_positions_in_bucket(
4081 &self,
4082 bucket: &AHashSet<PositionId>,
4083 venue: Option<&Venue>,
4084 instrument_id: Option<&InstrumentId>,
4085 strategy_id: Option<&StrategyId>,
4086 account_id: Option<&AccountId>,
4087 side: Option<PositionSide>,
4088 ) -> usize {
4089 match self.collect_position_filter_sources(venue, instrument_id, strategy_id, account_id) {
4090 FilterSources::Empty => 0,
4091 FilterSources::Unfiltered => side.map_or_else(
4092 || bucket.len(),
4093 |side| {
4094 bucket
4095 .iter()
4096 .filter(|id| self.position_side_matches(id, side))
4097 .count()
4098 },
4099 ),
4100 FilterSources::Sets(mut sources) => {
4101 sources.push(bucket);
4102 sources.sort_unstable_by_key(|s| s.len());
4103 let driver = sources[0];
4104 let rest = &sources[1..];
4105
4106 driver
4107 .iter()
4108 .filter(|id| rest.iter().all(|s| s.contains(id)))
4109 .filter(|id| side.is_none_or(|side| self.position_side_matches(id, side)))
4110 .count()
4111 }
4112 }
4113 }
4114
4115 fn any_orders_in_bucket(
4121 &self,
4122 bucket: &AHashSet<ClientOrderId>,
4123 venue: Option<&Venue>,
4124 instrument_id: Option<&InstrumentId>,
4125 strategy_id: Option<&StrategyId>,
4126 account_id: Option<&AccountId>,
4127 side: Option<OrderSide>,
4128 ) -> bool {
4129 match self.collect_order_filter_sources(venue, instrument_id, strategy_id, account_id) {
4130 FilterSources::Empty => false,
4131 FilterSources::Unfiltered => side.map_or_else(
4132 || !bucket.is_empty(),
4133 |side| bucket.iter().any(|id| self.order_side_matches(id, side)),
4134 ),
4135 FilterSources::Sets(mut sources) => {
4136 sources.push(bucket);
4137 sources.sort_unstable_by_key(|s| s.len());
4138 let driver = sources[0];
4139 let rest = &sources[1..];
4140
4141 driver
4142 .iter()
4143 .filter(|id| rest.iter().all(|s| s.contains(id)))
4144 .any(|id| side.is_none_or(|side| self.order_side_matches(id, side)))
4145 }
4146 }
4147 }
4148
4149 fn any_positions_in_bucket(
4150 &self,
4151 bucket: &AHashSet<PositionId>,
4152 venue: Option<&Venue>,
4153 instrument_id: Option<&InstrumentId>,
4154 strategy_id: Option<&StrategyId>,
4155 account_id: Option<&AccountId>,
4156 side: Option<PositionSide>,
4157 ) -> bool {
4158 match self.collect_position_filter_sources(venue, instrument_id, strategy_id, account_id) {
4159 FilterSources::Empty => false,
4160 FilterSources::Unfiltered => side.map_or_else(
4161 || !bucket.is_empty(),
4162 |side| bucket.iter().any(|id| self.position_side_matches(id, side)),
4163 ),
4164 FilterSources::Sets(mut sources) => {
4165 sources.push(bucket);
4166 sources.sort_unstable_by_key(|s| s.len());
4167 let driver = sources[0];
4168 let rest = &sources[1..];
4169
4170 driver
4171 .iter()
4172 .filter(|id| rest.iter().all(|s| s.contains(id)))
4173 .any(|id| side.is_none_or(|side| self.position_side_matches(id, side)))
4174 }
4175 }
4176 }
4177
4178 fn order_side_matches(&self, client_order_id: &ClientOrderId, side: OrderSide) -> bool {
4179 self.orders
4180 .get(client_order_id)
4181 .is_some_and(|cell| cell.borrow().order_side() == side)
4182 }
4183
4184 fn position_side_matches(&self, position_id: &PositionId, side: PositionSide) -> bool {
4185 self.positions
4186 .get(position_id)
4187 .is_some_and(|cell| cell.borrow().side == side)
4188 }
4189
4190 fn get_orders_for_ids(
4196 &self,
4197 client_order_ids: impl IntoIterator<Item = ClientOrderId>,
4198 side: Option<OrderSide>,
4199 ) -> Vec<OrderRef<'_>> {
4200 const UNCACHED_SORT_MAX_LEN: usize = 32;
4201
4202 let mut orders = Vec::new();
4203
4204 for client_order_id in client_order_ids {
4205 let order_cell = self
4206 .orders
4207 .get(&client_order_id)
4208 .unwrap_or_else(|| panic!("Order {client_order_id} not found"));
4209 let order = OrderRef::new(order_cell.borrow());
4210
4211 if side.is_none_or(|side| side == order.order_side()) {
4212 orders.push(order);
4213 }
4214 }
4215
4216 let key = |order: &OrderRef<'_>| order.client_order_id();
4219
4220 if orders.len() <= UNCACHED_SORT_MAX_LEN {
4221 orders.sort_by_key(key);
4222 } else {
4223 orders.sort_by_cached_key(key);
4224 }
4225
4226 orders
4227 }
4228
4229 fn get_positions_for_ids(
4239 &self,
4240 position_ids: &AHashSet<PositionId>,
4241 side: Option<PositionSide>,
4242 ) -> Vec<PositionRef<'_>> {
4243 let mut positions = Vec::new();
4244
4245 for position_id in position_ids {
4246 let position_cell = self
4247 .positions
4248 .get(position_id)
4249 .unwrap_or_else(|| panic!("Position {position_id} not found"));
4250 let position = PositionRef::new(position_cell.borrow());
4251
4252 if side.is_none_or(|side| side == position.side) {
4253 positions.push(position);
4254 }
4255 }
4256
4257 positions.sort_by_key(|p| p.id);
4260 positions
4261 }
4262
4263 #[must_use]
4265 pub fn client_order_ids(
4266 &self,
4267 venue: Option<&Venue>,
4268 instrument_id: Option<&InstrumentId>,
4269 strategy_id: Option<&StrategyId>,
4270 account_id: Option<&AccountId>,
4271 ) -> AHashSet<ClientOrderId> {
4272 self.query_orders_in_bucket(
4273 &self.index.orders,
4274 venue,
4275 instrument_id,
4276 strategy_id,
4277 account_id,
4278 )
4279 }
4280
4281 #[must_use]
4283 pub fn client_order_ids_open(
4284 &self,
4285 venue: Option<&Venue>,
4286 instrument_id: Option<&InstrumentId>,
4287 strategy_id: Option<&StrategyId>,
4288 account_id: Option<&AccountId>,
4289 ) -> AHashSet<ClientOrderId> {
4290 self.query_orders_in_bucket(
4291 &self.index.orders_open,
4292 venue,
4293 instrument_id,
4294 strategy_id,
4295 account_id,
4296 )
4297 }
4298
4299 #[must_use]
4301 pub fn client_order_ids_closed(
4302 &self,
4303 venue: Option<&Venue>,
4304 instrument_id: Option<&InstrumentId>,
4305 strategy_id: Option<&StrategyId>,
4306 account_id: Option<&AccountId>,
4307 ) -> AHashSet<ClientOrderId> {
4308 self.query_orders_in_bucket(
4309 &self.index.orders_closed,
4310 venue,
4311 instrument_id,
4312 strategy_id,
4313 account_id,
4314 )
4315 }
4316
4317 #[must_use]
4322 pub fn client_order_ids_active_local(
4323 &self,
4324 venue: Option<&Venue>,
4325 instrument_id: Option<&InstrumentId>,
4326 strategy_id: Option<&StrategyId>,
4327 account_id: Option<&AccountId>,
4328 ) -> AHashSet<ClientOrderId> {
4329 self.query_orders_in_bucket(
4330 &self.index.orders_active_local,
4331 venue,
4332 instrument_id,
4333 strategy_id,
4334 account_id,
4335 )
4336 }
4337
4338 #[must_use]
4340 pub fn client_order_ids_emulated(
4341 &self,
4342 venue: Option<&Venue>,
4343 instrument_id: Option<&InstrumentId>,
4344 strategy_id: Option<&StrategyId>,
4345 account_id: Option<&AccountId>,
4346 ) -> AHashSet<ClientOrderId> {
4347 self.query_orders_in_bucket(
4348 &self.index.orders_emulated,
4349 venue,
4350 instrument_id,
4351 strategy_id,
4352 account_id,
4353 )
4354 }
4355
4356 #[must_use]
4358 pub fn client_order_ids_inflight(
4359 &self,
4360 venue: Option<&Venue>,
4361 instrument_id: Option<&InstrumentId>,
4362 strategy_id: Option<&StrategyId>,
4363 account_id: Option<&AccountId>,
4364 ) -> AHashSet<ClientOrderId> {
4365 self.query_orders_in_bucket(
4366 &self.index.orders_inflight,
4367 venue,
4368 instrument_id,
4369 strategy_id,
4370 account_id,
4371 )
4372 }
4373
4374 #[must_use]
4376 pub fn position_ids(
4377 &self,
4378 venue: Option<&Venue>,
4379 instrument_id: Option<&InstrumentId>,
4380 strategy_id: Option<&StrategyId>,
4381 account_id: Option<&AccountId>,
4382 ) -> AHashSet<PositionId> {
4383 self.query_positions_in_bucket(
4384 &self.index.positions,
4385 venue,
4386 instrument_id,
4387 strategy_id,
4388 account_id,
4389 )
4390 }
4391
4392 #[must_use]
4394 pub fn position_open_ids(
4395 &self,
4396 venue: Option<&Venue>,
4397 instrument_id: Option<&InstrumentId>,
4398 strategy_id: Option<&StrategyId>,
4399 account_id: Option<&AccountId>,
4400 ) -> AHashSet<PositionId> {
4401 self.query_positions_in_bucket(
4402 &self.index.positions_open,
4403 venue,
4404 instrument_id,
4405 strategy_id,
4406 account_id,
4407 )
4408 }
4409
4410 #[must_use]
4412 pub fn position_closed_ids(
4413 &self,
4414 venue: Option<&Venue>,
4415 instrument_id: Option<&InstrumentId>,
4416 strategy_id: Option<&StrategyId>,
4417 account_id: Option<&AccountId>,
4418 ) -> AHashSet<PositionId> {
4419 self.query_positions_in_bucket(
4420 &self.index.positions_closed,
4421 venue,
4422 instrument_id,
4423 strategy_id,
4424 account_id,
4425 )
4426 }
4427
4428 #[must_use]
4435 pub fn client_order_ids_view(
4436 &self,
4437 venue: Option<&Venue>,
4438 instrument_id: Option<&InstrumentId>,
4439 strategy_id: Option<&StrategyId>,
4440 account_id: Option<&AccountId>,
4441 ) -> Cow<'_, AHashSet<ClientOrderId>> {
4442 self.view_orders_in_bucket(
4443 &self.index.orders,
4444 venue,
4445 instrument_id,
4446 strategy_id,
4447 account_id,
4448 )
4449 }
4450
4451 #[must_use]
4453 pub fn client_order_ids_open_view(
4454 &self,
4455 venue: Option<&Venue>,
4456 instrument_id: Option<&InstrumentId>,
4457 strategy_id: Option<&StrategyId>,
4458 account_id: Option<&AccountId>,
4459 ) -> Cow<'_, AHashSet<ClientOrderId>> {
4460 self.view_orders_in_bucket(
4461 &self.index.orders_open,
4462 venue,
4463 instrument_id,
4464 strategy_id,
4465 account_id,
4466 )
4467 }
4468
4469 #[must_use]
4471 pub fn client_order_ids_closed_view(
4472 &self,
4473 venue: Option<&Venue>,
4474 instrument_id: Option<&InstrumentId>,
4475 strategy_id: Option<&StrategyId>,
4476 account_id: Option<&AccountId>,
4477 ) -> Cow<'_, AHashSet<ClientOrderId>> {
4478 self.view_orders_in_bucket(
4479 &self.index.orders_closed,
4480 venue,
4481 instrument_id,
4482 strategy_id,
4483 account_id,
4484 )
4485 }
4486
4487 #[must_use]
4489 pub fn client_order_ids_active_local_view(
4490 &self,
4491 venue: Option<&Venue>,
4492 instrument_id: Option<&InstrumentId>,
4493 strategy_id: Option<&StrategyId>,
4494 account_id: Option<&AccountId>,
4495 ) -> Cow<'_, AHashSet<ClientOrderId>> {
4496 self.view_orders_in_bucket(
4497 &self.index.orders_active_local,
4498 venue,
4499 instrument_id,
4500 strategy_id,
4501 account_id,
4502 )
4503 }
4504
4505 #[must_use]
4507 pub fn client_order_ids_emulated_view(
4508 &self,
4509 venue: Option<&Venue>,
4510 instrument_id: Option<&InstrumentId>,
4511 strategy_id: Option<&StrategyId>,
4512 account_id: Option<&AccountId>,
4513 ) -> Cow<'_, AHashSet<ClientOrderId>> {
4514 self.view_orders_in_bucket(
4515 &self.index.orders_emulated,
4516 venue,
4517 instrument_id,
4518 strategy_id,
4519 account_id,
4520 )
4521 }
4522
4523 #[must_use]
4525 pub fn client_order_ids_inflight_view(
4526 &self,
4527 venue: Option<&Venue>,
4528 instrument_id: Option<&InstrumentId>,
4529 strategy_id: Option<&StrategyId>,
4530 account_id: Option<&AccountId>,
4531 ) -> Cow<'_, AHashSet<ClientOrderId>> {
4532 self.view_orders_in_bucket(
4533 &self.index.orders_inflight,
4534 venue,
4535 instrument_id,
4536 strategy_id,
4537 account_id,
4538 )
4539 }
4540
4541 #[must_use]
4543 pub fn position_ids_view(
4544 &self,
4545 venue: Option<&Venue>,
4546 instrument_id: Option<&InstrumentId>,
4547 strategy_id: Option<&StrategyId>,
4548 account_id: Option<&AccountId>,
4549 ) -> Cow<'_, AHashSet<PositionId>> {
4550 self.view_positions_in_bucket(
4551 &self.index.positions,
4552 venue,
4553 instrument_id,
4554 strategy_id,
4555 account_id,
4556 )
4557 }
4558
4559 #[must_use]
4561 pub fn position_open_ids_view(
4562 &self,
4563 venue: Option<&Venue>,
4564 instrument_id: Option<&InstrumentId>,
4565 strategy_id: Option<&StrategyId>,
4566 account_id: Option<&AccountId>,
4567 ) -> Cow<'_, AHashSet<PositionId>> {
4568 self.view_positions_in_bucket(
4569 &self.index.positions_open,
4570 venue,
4571 instrument_id,
4572 strategy_id,
4573 account_id,
4574 )
4575 }
4576
4577 #[must_use]
4579 pub fn position_closed_ids_view(
4580 &self,
4581 venue: Option<&Venue>,
4582 instrument_id: Option<&InstrumentId>,
4583 strategy_id: Option<&StrategyId>,
4584 account_id: Option<&AccountId>,
4585 ) -> Cow<'_, AHashSet<PositionId>> {
4586 self.view_positions_in_bucket(
4587 &self.index.positions_closed,
4588 venue,
4589 instrument_id,
4590 strategy_id,
4591 account_id,
4592 )
4593 }
4594
4595 pub fn iter_client_order_ids(
4601 &self,
4602 venue: Option<&Venue>,
4603 instrument_id: Option<&InstrumentId>,
4604 strategy_id: Option<&StrategyId>,
4605 account_id: Option<&AccountId>,
4606 ) -> Box<dyn Iterator<Item = ClientOrderId> + '_> {
4607 self.iter_orders_in_bucket(
4608 &self.index.orders,
4609 venue,
4610 instrument_id,
4611 strategy_id,
4612 account_id,
4613 )
4614 }
4615
4616 pub fn iter_client_order_ids_open(
4618 &self,
4619 venue: Option<&Venue>,
4620 instrument_id: Option<&InstrumentId>,
4621 strategy_id: Option<&StrategyId>,
4622 account_id: Option<&AccountId>,
4623 ) -> Box<dyn Iterator<Item = ClientOrderId> + '_> {
4624 self.iter_orders_in_bucket(
4625 &self.index.orders_open,
4626 venue,
4627 instrument_id,
4628 strategy_id,
4629 account_id,
4630 )
4631 }
4632
4633 pub fn iter_client_order_ids_closed(
4635 &self,
4636 venue: Option<&Venue>,
4637 instrument_id: Option<&InstrumentId>,
4638 strategy_id: Option<&StrategyId>,
4639 account_id: Option<&AccountId>,
4640 ) -> Box<dyn Iterator<Item = ClientOrderId> + '_> {
4641 self.iter_orders_in_bucket(
4642 &self.index.orders_closed,
4643 venue,
4644 instrument_id,
4645 strategy_id,
4646 account_id,
4647 )
4648 }
4649
4650 pub fn iter_client_order_ids_active_local(
4652 &self,
4653 venue: Option<&Venue>,
4654 instrument_id: Option<&InstrumentId>,
4655 strategy_id: Option<&StrategyId>,
4656 account_id: Option<&AccountId>,
4657 ) -> Box<dyn Iterator<Item = ClientOrderId> + '_> {
4658 self.iter_orders_in_bucket(
4659 &self.index.orders_active_local,
4660 venue,
4661 instrument_id,
4662 strategy_id,
4663 account_id,
4664 )
4665 }
4666
4667 pub fn iter_client_order_ids_emulated(
4669 &self,
4670 venue: Option<&Venue>,
4671 instrument_id: Option<&InstrumentId>,
4672 strategy_id: Option<&StrategyId>,
4673 account_id: Option<&AccountId>,
4674 ) -> Box<dyn Iterator<Item = ClientOrderId> + '_> {
4675 self.iter_orders_in_bucket(
4676 &self.index.orders_emulated,
4677 venue,
4678 instrument_id,
4679 strategy_id,
4680 account_id,
4681 )
4682 }
4683
4684 pub fn iter_client_order_ids_inflight(
4686 &self,
4687 venue: Option<&Venue>,
4688 instrument_id: Option<&InstrumentId>,
4689 strategy_id: Option<&StrategyId>,
4690 account_id: Option<&AccountId>,
4691 ) -> Box<dyn Iterator<Item = ClientOrderId> + '_> {
4692 self.iter_orders_in_bucket(
4693 &self.index.orders_inflight,
4694 venue,
4695 instrument_id,
4696 strategy_id,
4697 account_id,
4698 )
4699 }
4700
4701 pub fn iter_position_ids(
4703 &self,
4704 venue: Option<&Venue>,
4705 instrument_id: Option<&InstrumentId>,
4706 strategy_id: Option<&StrategyId>,
4707 account_id: Option<&AccountId>,
4708 ) -> Box<dyn Iterator<Item = PositionId> + '_> {
4709 self.iter_positions_in_bucket(
4710 &self.index.positions,
4711 venue,
4712 instrument_id,
4713 strategy_id,
4714 account_id,
4715 )
4716 }
4717
4718 pub fn iter_position_open_ids(
4720 &self,
4721 venue: Option<&Venue>,
4722 instrument_id: Option<&InstrumentId>,
4723 strategy_id: Option<&StrategyId>,
4724 account_id: Option<&AccountId>,
4725 ) -> Box<dyn Iterator<Item = PositionId> + '_> {
4726 self.iter_positions_in_bucket(
4727 &self.index.positions_open,
4728 venue,
4729 instrument_id,
4730 strategy_id,
4731 account_id,
4732 )
4733 }
4734
4735 pub fn iter_position_closed_ids(
4737 &self,
4738 venue: Option<&Venue>,
4739 instrument_id: Option<&InstrumentId>,
4740 strategy_id: Option<&StrategyId>,
4741 account_id: Option<&AccountId>,
4742 ) -> Box<dyn Iterator<Item = PositionId> + '_> {
4743 self.iter_positions_in_bucket(
4744 &self.index.positions_closed,
4745 venue,
4746 instrument_id,
4747 strategy_id,
4748 account_id,
4749 )
4750 }
4751
4752 #[must_use]
4754 pub fn strategy_ids(&self) -> AHashSet<StrategyId> {
4755 self.index.strategies.clone()
4756 }
4757
4758 #[must_use]
4760 pub fn exec_algorithm_ids(&self) -> AHashSet<ExecAlgorithmId> {
4761 self.index.exec_algorithms.clone()
4762 }
4763
4764 #[must_use]
4773 pub fn order_ref(&self, client_order_id: &ClientOrderId) -> Option<OrderRef<'_>> {
4774 self.orders
4775 .get(client_order_id)
4776 .map(|order_cell| OrderRef::new(order_cell.borrow()))
4777 }
4778
4779 #[must_use]
4783 pub fn order(&self, client_order_id: &ClientOrderId) -> Option<OrderRef<'_>> {
4784 self.order_ref(client_order_id)
4785 }
4786
4787 pub fn try_order_ref(
4793 &self,
4794 client_order_id: &ClientOrderId,
4795 ) -> Result<OrderRef<'_>, OrderLookupError> {
4796 self.orders
4797 .get(client_order_id)
4798 .map(|order_cell| OrderRef::new(order_cell.borrow()))
4799 .ok_or_else(|| OrderLookupError::not_found(*client_order_id))
4800 }
4801
4802 pub fn try_order(
4810 &self,
4811 client_order_id: &ClientOrderId,
4812 ) -> Result<OrderRef<'_>, OrderLookupError> {
4813 self.try_order_ref(client_order_id)
4814 }
4815
4816 #[must_use]
4826 pub fn order_mut(&mut self, client_order_id: &ClientOrderId) -> Option<OrderRefMut<'_>> {
4827 self.orders
4828 .get(client_order_id)
4829 .map(|order_cell| OrderRefMut::new(order_cell.borrow_mut()))
4830 }
4831
4832 #[must_use]
4837 pub fn order_owned(&self, client_order_id: &ClientOrderId) -> Option<OrderAny> {
4838 self.orders
4839 .get(client_order_id)
4840 .map(|order_cell| order_cell.borrow().clone())
4841 }
4842
4843 pub fn try_order_owned(
4849 &self,
4850 client_order_id: &ClientOrderId,
4851 ) -> Result<OrderAny, OrderLookupError> {
4852 self.try_order_ref(client_order_id)
4853 .map(|order| order.cloned())
4854 }
4855
4856 #[must_use]
4858 pub fn orders_for_ids(
4859 &self,
4860 client_order_ids: &[ClientOrderId],
4861 context: &dyn Display,
4862 ) -> Vec<OrderAny> {
4863 let mut orders = Vec::with_capacity(client_order_ids.len());
4864 for id in client_order_ids {
4865 match self.orders.get(id) {
4866 Some(order_cell) => orders.push(order_cell.borrow().clone()),
4867 None => log::error!("Order {id} not found in cache for {context}"),
4868 }
4869 }
4870 orders
4871 }
4872
4873 #[must_use]
4875 pub fn client_order_id(&self, venue_order_id: &VenueOrderId) -> Option<&ClientOrderId> {
4876 self.index.venue_order_ids.get(venue_order_id)
4877 }
4878
4879 #[must_use]
4881 pub fn venue_order_id(&self, client_order_id: &ClientOrderId) -> Option<&VenueOrderId> {
4882 self.index.client_order_ids.get(client_order_id)
4883 }
4884
4885 #[must_use]
4887 pub fn client_id(&self, client_order_id: &ClientOrderId) -> Option<&ClientId> {
4888 self.index.order_client.get(client_order_id)
4889 }
4890
4891 #[must_use]
4897 pub fn orders_refs(
4898 &self,
4899 venue: Option<&Venue>,
4900 instrument_id: Option<&InstrumentId>,
4901 strategy_id: Option<&StrategyId>,
4902 account_id: Option<&AccountId>,
4903 side: Option<OrderSide>,
4904 ) -> Vec<OrderRef<'_>> {
4905 if venue.is_none()
4906 && instrument_id.is_none()
4907 && strategy_id.is_none()
4908 && account_id.is_none()
4909 {
4910 return self.get_orders_for_ids(self.index.orders.iter().copied(), side);
4911 }
4912
4913 let client_order_ids =
4914 self.iter_client_order_ids(venue, instrument_id, strategy_id, account_id);
4915 self.get_orders_for_ids(client_order_ids, side)
4916 }
4917
4918 #[must_use]
4922 pub fn orders(
4923 &self,
4924 venue: Option<&Venue>,
4925 instrument_id: Option<&InstrumentId>,
4926 strategy_id: Option<&StrategyId>,
4927 account_id: Option<&AccountId>,
4928 side: Option<OrderSide>,
4929 ) -> Vec<OrderRef<'_>> {
4930 self.orders_refs(venue, instrument_id, strategy_id, account_id, side)
4931 }
4932
4933 #[must_use]
4935 pub fn orders_open_refs(
4936 &self,
4937 venue: Option<&Venue>,
4938 instrument_id: Option<&InstrumentId>,
4939 strategy_id: Option<&StrategyId>,
4940 account_id: Option<&AccountId>,
4941 side: Option<OrderSide>,
4942 ) -> Vec<OrderRef<'_>> {
4943 let client_order_ids =
4944 self.client_order_ids_open(venue, instrument_id, strategy_id, account_id);
4945 self.get_orders_for_ids(client_order_ids.iter().copied(), side)
4946 }
4947
4948 #[must_use]
4952 pub fn orders_open(
4953 &self,
4954 venue: Option<&Venue>,
4955 instrument_id: Option<&InstrumentId>,
4956 strategy_id: Option<&StrategyId>,
4957 account_id: Option<&AccountId>,
4958 side: Option<OrderSide>,
4959 ) -> Vec<OrderRef<'_>> {
4960 self.orders_open_refs(venue, instrument_id, strategy_id, account_id, side)
4961 }
4962
4963 #[must_use]
4965 pub fn orders_closed_refs(
4966 &self,
4967 venue: Option<&Venue>,
4968 instrument_id: Option<&InstrumentId>,
4969 strategy_id: Option<&StrategyId>,
4970 account_id: Option<&AccountId>,
4971 side: Option<OrderSide>,
4972 ) -> Vec<OrderRef<'_>> {
4973 let client_order_ids =
4974 self.client_order_ids_closed(venue, instrument_id, strategy_id, account_id);
4975 self.get_orders_for_ids(client_order_ids.iter().copied(), side)
4976 }
4977
4978 #[must_use]
4982 pub fn orders_closed(
4983 &self,
4984 venue: Option<&Venue>,
4985 instrument_id: Option<&InstrumentId>,
4986 strategy_id: Option<&StrategyId>,
4987 account_id: Option<&AccountId>,
4988 side: Option<OrderSide>,
4989 ) -> Vec<OrderRef<'_>> {
4990 self.orders_closed_refs(venue, instrument_id, strategy_id, account_id, side)
4991 }
4992
4993 #[must_use]
4998 pub fn orders_active_local_refs(
4999 &self,
5000 venue: Option<&Venue>,
5001 instrument_id: Option<&InstrumentId>,
5002 strategy_id: Option<&StrategyId>,
5003 account_id: Option<&AccountId>,
5004 side: Option<OrderSide>,
5005 ) -> Vec<OrderRef<'_>> {
5006 let client_order_ids =
5007 self.client_order_ids_active_local(venue, instrument_id, strategy_id, account_id);
5008 self.get_orders_for_ids(client_order_ids.iter().copied(), side)
5009 }
5010
5011 #[must_use]
5015 pub fn orders_active_local(
5016 &self,
5017 venue: Option<&Venue>,
5018 instrument_id: Option<&InstrumentId>,
5019 strategy_id: Option<&StrategyId>,
5020 account_id: Option<&AccountId>,
5021 side: Option<OrderSide>,
5022 ) -> Vec<OrderRef<'_>> {
5023 self.orders_active_local_refs(venue, instrument_id, strategy_id, account_id, side)
5024 }
5025
5026 #[must_use]
5028 pub fn orders_emulated_refs(
5029 &self,
5030 venue: Option<&Venue>,
5031 instrument_id: Option<&InstrumentId>,
5032 strategy_id: Option<&StrategyId>,
5033 account_id: Option<&AccountId>,
5034 side: Option<OrderSide>,
5035 ) -> Vec<OrderRef<'_>> {
5036 let client_order_ids =
5037 self.client_order_ids_emulated(venue, instrument_id, strategy_id, account_id);
5038 self.get_orders_for_ids(client_order_ids.iter().copied(), side)
5039 }
5040
5041 #[must_use]
5045 pub fn orders_emulated(
5046 &self,
5047 venue: Option<&Venue>,
5048 instrument_id: Option<&InstrumentId>,
5049 strategy_id: Option<&StrategyId>,
5050 account_id: Option<&AccountId>,
5051 side: Option<OrderSide>,
5052 ) -> Vec<OrderRef<'_>> {
5053 self.orders_emulated_refs(venue, instrument_id, strategy_id, account_id, side)
5054 }
5055
5056 #[must_use]
5058 pub fn orders_inflight_refs(
5059 &self,
5060 venue: Option<&Venue>,
5061 instrument_id: Option<&InstrumentId>,
5062 strategy_id: Option<&StrategyId>,
5063 account_id: Option<&AccountId>,
5064 side: Option<OrderSide>,
5065 ) -> Vec<OrderRef<'_>> {
5066 let client_order_ids =
5067 self.client_order_ids_inflight(venue, instrument_id, strategy_id, account_id);
5068 self.get_orders_for_ids(client_order_ids.iter().copied(), side)
5069 }
5070
5071 #[must_use]
5075 pub fn orders_inflight(
5076 &self,
5077 venue: Option<&Venue>,
5078 instrument_id: Option<&InstrumentId>,
5079 strategy_id: Option<&StrategyId>,
5080 account_id: Option<&AccountId>,
5081 side: Option<OrderSide>,
5082 ) -> Vec<OrderRef<'_>> {
5083 self.orders_inflight_refs(venue, instrument_id, strategy_id, account_id, side)
5084 }
5085
5086 #[must_use]
5088 pub fn orders_for_position(&self, position_id: &PositionId) -> Vec<OrderRef<'_>> {
5089 match self.index.position_orders.get(position_id) {
5090 Some(client_order_ids) => {
5091 self.get_orders_for_ids(client_order_ids.iter().copied(), None)
5092 }
5093 None => Vec::new(),
5094 }
5095 }
5096
5097 pub fn mark_orders_dispatched(
5101 &mut self,
5102 client_order_ids: impl IntoIterator<Item = ClientOrderId>,
5103 ) {
5104 self.orders_dispatched.extend(client_order_ids);
5105 }
5106
5107 pub fn clear_order_dispatch(&mut self, client_order_id: &ClientOrderId) {
5109 self.orders_dispatched.remove(client_order_id);
5110 }
5111
5112 #[must_use]
5114 pub fn is_order_dispatched(&self, client_order_id: &ClientOrderId) -> bool {
5115 self.orders_dispatched.contains(client_order_id)
5116 }
5117
5118 #[must_use]
5120 pub fn order_exists(&self, client_order_id: &ClientOrderId) -> bool {
5121 self.index.orders.contains(client_order_id)
5122 }
5123
5124 #[must_use]
5126 pub fn is_order_open(&self, client_order_id: &ClientOrderId) -> bool {
5127 self.index.orders_open.contains(client_order_id)
5128 }
5129
5130 #[must_use]
5132 pub fn is_order_closed(&self, client_order_id: &ClientOrderId) -> bool {
5133 self.index.orders_closed.contains(client_order_id)
5134 }
5135
5136 #[must_use]
5141 pub fn is_order_active_local(&self, client_order_id: &ClientOrderId) -> bool {
5142 self.index.orders_active_local.contains(client_order_id)
5143 }
5144
5145 #[must_use]
5147 pub fn is_order_emulated(&self, client_order_id: &ClientOrderId) -> bool {
5148 self.index.orders_emulated.contains(client_order_id)
5149 }
5150
5151 #[must_use]
5153 pub fn is_order_inflight(&self, client_order_id: &ClientOrderId) -> bool {
5154 self.index.orders_inflight.contains(client_order_id)
5155 }
5156
5157 #[must_use]
5159 pub fn is_order_pending_cancel_local(&self, client_order_id: &ClientOrderId) -> bool {
5160 self.index.orders_pending_cancel.contains(client_order_id)
5161 }
5162
5163 #[must_use]
5165 pub fn orders_open_count(
5166 &self,
5167 venue: Option<&Venue>,
5168 instrument_id: Option<&InstrumentId>,
5169 strategy_id: Option<&StrategyId>,
5170 account_id: Option<&AccountId>,
5171 side: Option<OrderSide>,
5172 ) -> usize {
5173 self.count_orders_in_bucket(
5174 &self.index.orders_open,
5175 venue,
5176 instrument_id,
5177 strategy_id,
5178 account_id,
5179 side,
5180 )
5181 }
5182
5183 #[must_use]
5185 pub fn orders_closed_count(
5186 &self,
5187 venue: Option<&Venue>,
5188 instrument_id: Option<&InstrumentId>,
5189 strategy_id: Option<&StrategyId>,
5190 account_id: Option<&AccountId>,
5191 side: Option<OrderSide>,
5192 ) -> usize {
5193 self.count_orders_in_bucket(
5194 &self.index.orders_closed,
5195 venue,
5196 instrument_id,
5197 strategy_id,
5198 account_id,
5199 side,
5200 )
5201 }
5202
5203 #[must_use]
5208 pub fn orders_active_local_count(
5209 &self,
5210 venue: Option<&Venue>,
5211 instrument_id: Option<&InstrumentId>,
5212 strategy_id: Option<&StrategyId>,
5213 account_id: Option<&AccountId>,
5214 side: Option<OrderSide>,
5215 ) -> usize {
5216 self.count_orders_in_bucket(
5217 &self.index.orders_active_local,
5218 venue,
5219 instrument_id,
5220 strategy_id,
5221 account_id,
5222 side,
5223 )
5224 }
5225
5226 #[must_use]
5228 pub fn orders_emulated_count(
5229 &self,
5230 venue: Option<&Venue>,
5231 instrument_id: Option<&InstrumentId>,
5232 strategy_id: Option<&StrategyId>,
5233 account_id: Option<&AccountId>,
5234 side: Option<OrderSide>,
5235 ) -> usize {
5236 self.count_orders_in_bucket(
5237 &self.index.orders_emulated,
5238 venue,
5239 instrument_id,
5240 strategy_id,
5241 account_id,
5242 side,
5243 )
5244 }
5245
5246 #[must_use]
5248 pub fn orders_inflight_count(
5249 &self,
5250 venue: Option<&Venue>,
5251 instrument_id: Option<&InstrumentId>,
5252 strategy_id: Option<&StrategyId>,
5253 account_id: Option<&AccountId>,
5254 side: Option<OrderSide>,
5255 ) -> usize {
5256 self.count_orders_in_bucket(
5257 &self.index.orders_inflight,
5258 venue,
5259 instrument_id,
5260 strategy_id,
5261 account_id,
5262 side,
5263 )
5264 }
5265
5266 #[must_use]
5268 pub fn orders_total_count(
5269 &self,
5270 venue: Option<&Venue>,
5271 instrument_id: Option<&InstrumentId>,
5272 strategy_id: Option<&StrategyId>,
5273 account_id: Option<&AccountId>,
5274 side: Option<OrderSide>,
5275 ) -> usize {
5276 self.count_orders_in_bucket(
5277 &self.index.orders,
5278 venue,
5279 instrument_id,
5280 strategy_id,
5281 account_id,
5282 side,
5283 )
5284 }
5285
5286 #[must_use]
5292 pub fn has_orders_open(
5293 &self,
5294 venue: Option<&Venue>,
5295 instrument_id: Option<&InstrumentId>,
5296 strategy_id: Option<&StrategyId>,
5297 account_id: Option<&AccountId>,
5298 side: Option<OrderSide>,
5299 ) -> bool {
5300 self.any_orders_in_bucket(
5301 &self.index.orders_open,
5302 venue,
5303 instrument_id,
5304 strategy_id,
5305 account_id,
5306 side,
5307 )
5308 }
5309
5310 #[must_use]
5312 pub fn has_orders_closed(
5313 &self,
5314 venue: Option<&Venue>,
5315 instrument_id: Option<&InstrumentId>,
5316 strategy_id: Option<&StrategyId>,
5317 account_id: Option<&AccountId>,
5318 side: Option<OrderSide>,
5319 ) -> bool {
5320 self.any_orders_in_bucket(
5321 &self.index.orders_closed,
5322 venue,
5323 instrument_id,
5324 strategy_id,
5325 account_id,
5326 side,
5327 )
5328 }
5329
5330 #[must_use]
5334 pub fn has_orders_active_local(
5335 &self,
5336 venue: Option<&Venue>,
5337 instrument_id: Option<&InstrumentId>,
5338 strategy_id: Option<&StrategyId>,
5339 account_id: Option<&AccountId>,
5340 side: Option<OrderSide>,
5341 ) -> bool {
5342 self.any_orders_in_bucket(
5343 &self.index.orders_active_local,
5344 venue,
5345 instrument_id,
5346 strategy_id,
5347 account_id,
5348 side,
5349 )
5350 }
5351
5352 #[must_use]
5354 pub fn has_orders_emulated(
5355 &self,
5356 venue: Option<&Venue>,
5357 instrument_id: Option<&InstrumentId>,
5358 strategy_id: Option<&StrategyId>,
5359 account_id: Option<&AccountId>,
5360 side: Option<OrderSide>,
5361 ) -> bool {
5362 self.any_orders_in_bucket(
5363 &self.index.orders_emulated,
5364 venue,
5365 instrument_id,
5366 strategy_id,
5367 account_id,
5368 side,
5369 )
5370 }
5371
5372 #[must_use]
5374 pub fn has_orders_inflight(
5375 &self,
5376 venue: Option<&Venue>,
5377 instrument_id: Option<&InstrumentId>,
5378 strategy_id: Option<&StrategyId>,
5379 account_id: Option<&AccountId>,
5380 side: Option<OrderSide>,
5381 ) -> bool {
5382 self.any_orders_in_bucket(
5383 &self.index.orders_inflight,
5384 venue,
5385 instrument_id,
5386 strategy_id,
5387 account_id,
5388 side,
5389 )
5390 }
5391
5392 #[must_use]
5394 pub fn has_orders(
5395 &self,
5396 venue: Option<&Venue>,
5397 instrument_id: Option<&InstrumentId>,
5398 strategy_id: Option<&StrategyId>,
5399 account_id: Option<&AccountId>,
5400 side: Option<OrderSide>,
5401 ) -> bool {
5402 self.any_orders_in_bucket(
5403 &self.index.orders,
5404 venue,
5405 instrument_id,
5406 strategy_id,
5407 account_id,
5408 side,
5409 )
5410 }
5411
5412 #[must_use]
5414 pub fn order_list(&self, order_list_id: &OrderListId) -> Option<&OrderList> {
5415 self.order_lists.get(order_list_id)
5416 }
5417
5418 pub fn try_order_list(
5424 &self,
5425 order_list_id: &OrderListId,
5426 ) -> Result<&OrderList, OrderListLookupError> {
5427 self.order_lists
5428 .get(order_list_id)
5429 .ok_or_else(|| OrderListLookupError::not_found(*order_list_id))
5430 }
5431
5432 #[must_use]
5434 pub fn order_lists(
5435 &self,
5436 venue: Option<&Venue>,
5437 instrument_id: Option<&InstrumentId>,
5438 strategy_id: Option<&StrategyId>,
5439 account_id: Option<&AccountId>,
5440 ) -> Vec<&OrderList> {
5441 let mut order_lists = self.order_lists.values().collect::<Vec<&OrderList>>();
5442
5443 if let Some(venue) = venue {
5444 order_lists.retain(|ol| &ol.instrument_id.venue == venue);
5445 }
5446
5447 if let Some(instrument_id) = instrument_id {
5448 order_lists.retain(|ol| &ol.instrument_id == instrument_id);
5449 }
5450
5451 if let Some(strategy_id) = strategy_id {
5452 order_lists.retain(|ol| &ol.strategy_id == strategy_id);
5453 }
5454
5455 if let Some(account_id) = account_id {
5456 order_lists.retain(|ol| {
5457 ol.client_order_ids.iter().any(|client_order_id| {
5458 self.orders.get(client_order_id).is_some_and(|order_cell| {
5459 order_cell.borrow().account_id().as_ref() == Some(account_id)
5460 })
5461 })
5462 });
5463 }
5464
5465 order_lists
5466 }
5467
5468 #[must_use]
5470 pub fn order_list_exists(&self, order_list_id: &OrderListId) -> bool {
5471 self.order_lists.contains_key(order_list_id)
5472 }
5473
5474 #[must_use]
5479 pub fn orders_for_exec_algorithm(
5480 &self,
5481 exec_algorithm_id: &ExecAlgorithmId,
5482 venue: Option<&Venue>,
5483 instrument_id: Option<&InstrumentId>,
5484 strategy_id: Option<&StrategyId>,
5485 account_id: Option<&AccountId>,
5486 side: Option<OrderSide>,
5487 ) -> Vec<OrderRef<'_>> {
5488 let Some(exec_algorithm_order_ids) =
5489 self.index.exec_algorithm_orders.get(exec_algorithm_id)
5490 else {
5491 return Vec::new();
5492 };
5493
5494 let filtered = self.query_orders_in_bucket(
5495 exec_algorithm_order_ids,
5496 venue,
5497 instrument_id,
5498 strategy_id,
5499 account_id,
5500 );
5501 self.get_orders_for_ids(filtered.iter().copied(), side)
5502 }
5503
5504 #[must_use]
5506 pub fn orders_for_exec_spawn(&self, exec_spawn_id: &ClientOrderId) -> Vec<OrderRef<'_>> {
5507 match self.index.exec_spawn_orders.get(exec_spawn_id) {
5508 Some(ids) => self.get_orders_for_ids(ids.iter().copied(), None),
5509 None => Vec::new(),
5510 }
5511 }
5512
5513 #[must_use]
5515 pub fn exec_spawn_total_quantity(
5516 &self,
5517 exec_spawn_id: &ClientOrderId,
5518 active_only: bool,
5519 ) -> Option<Quantity> {
5520 self.exec_spawn_total(exec_spawn_id, active_only, Order::quantity)
5521 }
5522
5523 #[must_use]
5525 pub fn exec_spawn_total_filled_qty(
5526 &self,
5527 exec_spawn_id: &ClientOrderId,
5528 active_only: bool,
5529 ) -> Option<Quantity> {
5530 self.exec_spawn_total(exec_spawn_id, active_only, Order::filled_qty)
5531 }
5532
5533 #[must_use]
5535 pub fn exec_spawn_total_leaves_qty(
5536 &self,
5537 exec_spawn_id: &ClientOrderId,
5538 active_only: bool,
5539 ) -> Option<Quantity> {
5540 self.exec_spawn_total(exec_spawn_id, active_only, Order::leaves_qty)
5541 }
5542
5543 fn exec_spawn_total(
5544 &self,
5545 exec_spawn_id: &ClientOrderId,
5546 active_only: bool,
5547 quantity: impl Fn(&OrderAny) -> Quantity,
5548 ) -> Option<Quantity> {
5549 self.orders_for_exec_spawn(exec_spawn_id)
5550 .into_iter()
5551 .filter(|order| !active_only || !order.is_closed())
5552 .map(|order| quantity(&order))
5553 .reduce(|total, quantity| total + quantity)
5554 }
5555
5556 #[must_use]
5560 pub fn position_ref(&self, position_id: &PositionId) -> Option<PositionRef<'_>> {
5561 self.positions
5562 .get(position_id)
5563 .map(|position_cell| PositionRef::new(position_cell.borrow()))
5564 }
5565
5566 #[must_use]
5570 pub fn position(&self, position_id: &PositionId) -> Option<PositionRef<'_>> {
5571 self.position_ref(position_id)
5572 }
5573
5574 pub fn try_position_ref(
5580 &self,
5581 position_id: &PositionId,
5582 ) -> Result<PositionRef<'_>, PositionLookupError> {
5583 self.positions
5584 .get(position_id)
5585 .map(|position_cell| PositionRef::new(position_cell.borrow()))
5586 .ok_or_else(|| PositionLookupError::not_found(*position_id))
5587 }
5588
5589 pub fn try_position(
5597 &self,
5598 position_id: &PositionId,
5599 ) -> Result<PositionRef<'_>, PositionLookupError> {
5600 self.try_position_ref(position_id)
5601 }
5602
5603 #[must_use]
5613 pub fn position_mut(&mut self, position_id: &PositionId) -> Option<PositionRefMut<'_>> {
5614 self.positions
5615 .get(position_id)
5616 .map(|position_cell| PositionRefMut::new(position_cell.borrow_mut()))
5617 }
5618
5619 #[must_use]
5624 pub fn position_owned(&self, position_id: &PositionId) -> Option<Position> {
5625 self.positions
5626 .get(position_id)
5627 .map(|position_cell| position_cell.borrow().clone())
5628 }
5629
5630 #[must_use]
5632 pub fn position_for_order_ref(
5633 &self,
5634 client_order_id: &ClientOrderId,
5635 ) -> Option<PositionRef<'_>> {
5636 self.index
5637 .order_position
5638 .get(client_order_id)
5639 .and_then(|position_id| self.positions.get(position_id))
5640 .map(|position_cell| PositionRef::new(position_cell.borrow()))
5641 }
5642
5643 #[must_use]
5647 pub fn position_for_order(&self, client_order_id: &ClientOrderId) -> Option<PositionRef<'_>> {
5648 self.position_for_order_ref(client_order_id)
5649 }
5650
5651 #[must_use]
5653 pub fn position_id(&self, client_order_id: &ClientOrderId) -> Option<&PositionId> {
5654 self.index.order_position.get(client_order_id)
5655 }
5656
5657 pub fn hedging_flip_position_ids(
5667 &self,
5668 client_order_id: ClientOrderId,
5669 ) -> anyhow::Result<Option<[PositionId; 2]>> {
5670 let Some(order) = self.order_ref(&client_order_id) else {
5671 return Ok(None);
5672 };
5673
5674 let mut flip = None;
5675 let mut corrected_source = None;
5676
5677 let mut replay_offsets = AHashMap::new();
5679
5680 for event in order.events() {
5681 let OrderEventAny::Filled(fill) = event else {
5682 continue;
5683 };
5684
5685 let Some(source_id) = fill.position_id.filter(PositionId::is_virtual) else {
5686 continue;
5687 };
5688
5689 let source = self.position_ref(&source_id);
5690 if source.is_some() && self.oms_type(&source_id) != Some(OmsType::Hedging) {
5691 continue;
5692 }
5693
5694 if source.as_ref().is_some_and(|source| {
5695 let offset = replay_offsets.entry(source_id).or_default();
5696 Self::replay_contains_fill(&source.replay_events, fill, offset)
5697 }) {
5698 continue;
5699 }
5700
5701 let closing = source.as_ref().and_then(|source| {
5702 source
5703 .fill_fragments(client_order_id, fill.trade_id)
5704 .into_iter()
5705 .find(|fragment| {
5706 fragment.event_id == fill.event_id && fragment.last_qty < fill.last_qty
5707 })
5708 .map(|closing| (source, closing))
5709 });
5710
5711 let Some((source, closing)) = closing else {
5712 let has_opening = self
5713 .positions_refs(
5714 None,
5715 Some(&fill.instrument_id),
5716 Some(&fill.strategy_id),
5717 Some(&fill.account_id),
5718 None,
5719 )
5720 .iter()
5721 .any(|position| {
5722 position.id != source_id
5723 && position.id.is_virtual()
5724 && self.oms_type(&position.id) == Some(OmsType::Hedging)
5725 && position.replay_events.iter().any(|event| {
5726 matches!(event, PositionReplayEvent::Filled(opening)
5727 if opening.causation_id == Some(fill.event_id)
5728 && opening.client_order_id == client_order_id
5729 && opening.trade_id == fill.trade_id)
5730 })
5731 });
5732
5733 anyhow::ensure!(
5734 !has_opening,
5735 "missing virtual flip source fragment for order {client_order_id}"
5736 );
5737 continue;
5738 };
5739
5740 let (positions, corrected_closing) =
5741 self.hedging_flip_destination(fill, source, closing)?;
5742 anyhow::ensure!(
5743 flip.is_none() || corrected_source == Some(source.id),
5744 "ambiguous virtual flip history for order {client_order_id}"
5745 );
5746 flip = Some(positions);
5747 corrected_source = corrected_closing.then_some(source.id);
5748 }
5749
5750 Ok(flip)
5751 }
5752
5753 fn replay_contains_fill(
5754 events: &[PositionReplayEvent],
5755 fill: &OrderFilled,
5756 offset: &mut usize,
5757 ) -> bool {
5758 let matches_fill = |event: &PositionReplayEvent| matches!(event, PositionReplayEvent::Filled(fragment) if fragment == fill);
5759
5760 if let Some(index) = events[*offset..].iter().position(matches_fill) {
5761 *offset += index + 1;
5762 return true;
5763 }
5764
5765 events[..*offset].iter().any(matches_fill)
5767 }
5768
5769 fn hedging_flip_destination(
5770 &self,
5771 fill: &OrderFilled,
5772 source: &Position,
5773 closing: &OrderFilled,
5774 ) -> anyhow::Result<([PositionId; 2], bool)> {
5775 let positions = self.positions_refs(
5776 None,
5777 Some(&fill.instrument_id),
5778 Some(&fill.strategy_id),
5779 Some(&fill.account_id),
5780 None,
5781 );
5782
5783 let mut openings = positions.iter().flat_map(|position| {
5784 position
5785 .replay_events
5786 .iter()
5787 .filter_map(move |event| match event {
5788 PositionReplayEvent::Filled(opening)
5789 if opening.causation_id == Some(fill.event_id)
5790 && position.id != source.id =>
5791 {
5792 Some((position, opening))
5793 }
5794 _ => None,
5795 })
5796 });
5797
5798 let (destination, opening) = openings.next().ok_or_else(|| {
5799 anyhow::anyhow!(
5800 "missing virtual flip fragment for order {}",
5801 fill.client_order_id
5802 )
5803 })?;
5804
5805 anyhow::ensure!(
5806 openings.next().is_none(),
5807 "ambiguous virtual flip fragments for order {}",
5808 fill.client_order_id
5809 );
5810 anyhow::ensure!(
5811 source.instrument_id == fill.instrument_id
5812 && source.account_id == fill.account_id
5813 && source.strategy_id == fill.strategy_id
5814 && destination.id.is_virtual()
5815 && self.oms_type(&destination.id) == Some(OmsType::Hedging),
5816 "conflicting virtual flip positions for order {}",
5817 fill.client_order_id
5818 );
5819 let (expected_closing, expected_opening) =
5820 fill.split_for_position_flip(closing.last_qty, Some(destination.id), opening.event_id)?;
5821 anyhow::ensure!(
5822 *closing == expected_closing && *opening == expected_opening,
5823 "conflicting virtual flip provenance for order {}",
5824 fill.client_order_id
5825 );
5826
5827 let corrected_closing = source.fill_voids.iter().any(|correction| {
5828 correction.event.client_order_id == fill.client_order_id
5829 && correction.event.trade_id == fill.trade_id
5830 && correction.voided_qty.non_zero()
5831 });
5832
5833 if corrected_closing && source.is_open() {
5834 anyhow::ensure!(
5835 !destination.is_open(),
5836 "ambiguous corrected virtual flip for order {}",
5837 fill.client_order_id
5838 );
5839 return Ok(([destination.id, source.id], corrected_closing));
5840 }
5841
5842 Ok(([source.id, destination.id], corrected_closing))
5843 }
5844
5845 fn reindex_hedging_position(&mut self, client_order_id: ClientOrderId) {
5846 match self.hedging_flip_position_ids(client_order_id) {
5847 Ok(Some([_, position_id])) => {
5848 let position = self
5849 .position_ref(&position_id)
5850 .expect("proven position is cached");
5851 let venue = position.instrument_id.venue;
5852 let strategy_id = position.strategy_id;
5853 drop(position);
5854
5855 if let Err(e) =
5856 self.add_position_id(&position_id, &venue, &client_order_id, &strategy_id)
5857 {
5858 log::error!("Failed to index virtual flip for order {client_order_id}: {e}");
5859 }
5860 }
5861 Ok(None) => {}
5862 Err(e) => {
5863 self.index.order_position.remove(&client_order_id);
5864
5865 log::error!("Cannot index virtual flip for order {client_order_id}: {e}");
5866 }
5867 }
5868 }
5869
5870 #[must_use]
5876 pub fn positions_refs(
5877 &self,
5878 venue: Option<&Venue>,
5879 instrument_id: Option<&InstrumentId>,
5880 strategy_id: Option<&StrategyId>,
5881 account_id: Option<&AccountId>,
5882 side: Option<PositionSide>,
5883 ) -> Vec<PositionRef<'_>> {
5884 let position_ids = self.position_ids(venue, instrument_id, strategy_id, account_id);
5885 self.get_positions_for_ids(&position_ids, side)
5886 }
5887
5888 #[must_use]
5892 pub fn positions(
5893 &self,
5894 venue: Option<&Venue>,
5895 instrument_id: Option<&InstrumentId>,
5896 strategy_id: Option<&StrategyId>,
5897 account_id: Option<&AccountId>,
5898 side: Option<PositionSide>,
5899 ) -> Vec<PositionRef<'_>> {
5900 self.positions_refs(venue, instrument_id, strategy_id, account_id, side)
5901 }
5902
5903 #[must_use]
5905 pub fn positions_open_refs(
5906 &self,
5907 venue: Option<&Venue>,
5908 instrument_id: Option<&InstrumentId>,
5909 strategy_id: Option<&StrategyId>,
5910 account_id: Option<&AccountId>,
5911 side: Option<PositionSide>,
5912 ) -> Vec<PositionRef<'_>> {
5913 let position_ids = self.position_open_ids(venue, instrument_id, strategy_id, account_id);
5914 self.get_positions_for_ids(&position_ids, side)
5915 }
5916
5917 #[must_use]
5921 pub fn positions_open(
5922 &self,
5923 venue: Option<&Venue>,
5924 instrument_id: Option<&InstrumentId>,
5925 strategy_id: Option<&StrategyId>,
5926 account_id: Option<&AccountId>,
5927 side: Option<PositionSide>,
5928 ) -> Vec<PositionRef<'_>> {
5929 self.positions_open_refs(venue, instrument_id, strategy_id, account_id, side)
5930 }
5931
5932 #[must_use]
5934 pub fn positions_closed_refs(
5935 &self,
5936 venue: Option<&Venue>,
5937 instrument_id: Option<&InstrumentId>,
5938 strategy_id: Option<&StrategyId>,
5939 account_id: Option<&AccountId>,
5940 side: Option<PositionSide>,
5941 ) -> Vec<PositionRef<'_>> {
5942 let position_ids = self.position_closed_ids(venue, instrument_id, strategy_id, account_id);
5943 self.get_positions_for_ids(&position_ids, side)
5944 }
5945
5946 #[must_use]
5950 pub fn positions_closed(
5951 &self,
5952 venue: Option<&Venue>,
5953 instrument_id: Option<&InstrumentId>,
5954 strategy_id: Option<&StrategyId>,
5955 account_id: Option<&AccountId>,
5956 side: Option<PositionSide>,
5957 ) -> Vec<PositionRef<'_>> {
5958 self.positions_closed_refs(venue, instrument_id, strategy_id, account_id, side)
5959 }
5960
5961 #[must_use]
5963 pub fn position_exists(&self, position_id: &PositionId) -> bool {
5964 self.index.positions.contains(position_id)
5965 }
5966
5967 #[must_use]
5969 pub fn is_position_open(&self, position_id: &PositionId) -> bool {
5970 self.index.positions_open.contains(position_id)
5971 }
5972
5973 #[must_use]
5975 pub fn is_position_closed(&self, position_id: &PositionId) -> bool {
5976 self.index.positions_closed.contains(position_id)
5977 }
5978
5979 #[must_use]
5981 pub fn positions_open_count(
5982 &self,
5983 venue: Option<&Venue>,
5984 instrument_id: Option<&InstrumentId>,
5985 strategy_id: Option<&StrategyId>,
5986 account_id: Option<&AccountId>,
5987 side: Option<PositionSide>,
5988 ) -> usize {
5989 self.count_positions_in_bucket(
5990 &self.index.positions_open,
5991 venue,
5992 instrument_id,
5993 strategy_id,
5994 account_id,
5995 side,
5996 )
5997 }
5998
5999 #[must_use]
6001 pub fn positions_closed_count(
6002 &self,
6003 venue: Option<&Venue>,
6004 instrument_id: Option<&InstrumentId>,
6005 strategy_id: Option<&StrategyId>,
6006 account_id: Option<&AccountId>,
6007 side: Option<PositionSide>,
6008 ) -> usize {
6009 self.count_positions_in_bucket(
6010 &self.index.positions_closed,
6011 venue,
6012 instrument_id,
6013 strategy_id,
6014 account_id,
6015 side,
6016 )
6017 }
6018
6019 #[must_use]
6021 pub fn positions_total_count(
6022 &self,
6023 venue: Option<&Venue>,
6024 instrument_id: Option<&InstrumentId>,
6025 strategy_id: Option<&StrategyId>,
6026 account_id: Option<&AccountId>,
6027 side: Option<PositionSide>,
6028 ) -> usize {
6029 self.count_positions_in_bucket(
6030 &self.index.positions,
6031 venue,
6032 instrument_id,
6033 strategy_id,
6034 account_id,
6035 side,
6036 )
6037 }
6038
6039 #[must_use]
6045 pub fn has_positions_open(
6046 &self,
6047 venue: Option<&Venue>,
6048 instrument_id: Option<&InstrumentId>,
6049 strategy_id: Option<&StrategyId>,
6050 account_id: Option<&AccountId>,
6051 side: Option<PositionSide>,
6052 ) -> bool {
6053 self.any_positions_in_bucket(
6054 &self.index.positions_open,
6055 venue,
6056 instrument_id,
6057 strategy_id,
6058 account_id,
6059 side,
6060 )
6061 }
6062
6063 #[must_use]
6065 pub fn has_positions_closed(
6066 &self,
6067 venue: Option<&Venue>,
6068 instrument_id: Option<&InstrumentId>,
6069 strategy_id: Option<&StrategyId>,
6070 account_id: Option<&AccountId>,
6071 side: Option<PositionSide>,
6072 ) -> bool {
6073 self.any_positions_in_bucket(
6074 &self.index.positions_closed,
6075 venue,
6076 instrument_id,
6077 strategy_id,
6078 account_id,
6079 side,
6080 )
6081 }
6082
6083 #[must_use]
6085 pub fn has_positions(
6086 &self,
6087 venue: Option<&Venue>,
6088 instrument_id: Option<&InstrumentId>,
6089 strategy_id: Option<&StrategyId>,
6090 account_id: Option<&AccountId>,
6091 side: Option<PositionSide>,
6092 ) -> bool {
6093 self.any_positions_in_bucket(
6094 &self.index.positions,
6095 venue,
6096 instrument_id,
6097 strategy_id,
6098 account_id,
6099 side,
6100 )
6101 }
6102
6103 #[must_use]
6107 pub fn strategy_id_for_order(&self, client_order_id: &ClientOrderId) -> Option<&StrategyId> {
6108 self.index.order_strategy.get(client_order_id)
6109 }
6110
6111 #[must_use]
6113 pub fn strategy_id_for_position(&self, position_id: &PositionId) -> Option<&StrategyId> {
6114 self.index.position_strategy.get(position_id)
6115 }
6116
6117 pub fn get(&self, key: &str) -> anyhow::Result<Option<&Bytes>> {
6125 check_valid_string_utf8(key, stringify!(key))?;
6126
6127 Ok(self.general.get(key))
6128 }
6129
6130 #[must_use]
6140 pub fn price(&self, instrument_id: &InstrumentId, price_type: PriceType) -> Option<Price> {
6141 match price_type {
6142 PriceType::Bid => self
6143 .quotes
6144 .get(instrument_id)
6145 .and_then(|quotes| quotes.front().map(|quote| quote.bid_price)),
6146 PriceType::Ask => self
6147 .quotes
6148 .get(instrument_id)
6149 .and_then(|quotes| quotes.front().map(|quote| quote.ask_price)),
6150 PriceType::Mid => self.quotes.get(instrument_id).and_then(|quotes| {
6151 let quote = quotes.front()?;
6152 let bid = quote.bid_price;
6153 let ask = quote.ask_price;
6154 if bid.is_undefined() || bid.is_error() || ask.is_undefined() || ask.is_error() {
6155 return None;
6156 }
6157
6158 let precision = bid.precision;
6160 check_fixed_precision(precision).ok()?;
6161
6162 #[allow(
6163 clippy::useless_conversion,
6164 reason = "i128::from is a widening conversion when PriceRaw is i64"
6165 )]
6166 let (mut bid_raw, mut ask_raw) = (i128::from(bid.raw()), i128::from(ask.raw()));
6167
6168 if precision < FIXED_PRECISION {
6169 let factor = 10_i128.pow(u32::from(FIXED_PRECISION - precision));
6170 bid_raw = (bid_raw / factor) * factor;
6171 ask_raw = (ask_raw / factor) * factor;
6172 }
6173 let sum = bid_raw.checked_add(ask_raw)?;
6174
6175 if precision < FIXED_PRECISION {
6176 let raw = PriceRaw::try_from(sum / 2).ok()?;
6177 return Price::from_raw_checked(raw, precision + 1).ok();
6178 }
6179
6180 if precision != MAX_FLOAT_PRECISION
6181 && let Some(price) = sum
6182 .checked_mul(5)
6183 .and_then(|raw| PriceRaw::try_from(raw).ok())
6184 .and_then(|raw| Price::from_raw_checked(raw, precision + 1).ok())
6185 {
6186 return Some(price);
6187 }
6188
6189 let mut raw = sum / 2;
6190 if sum % 2 != 0 && raw % 2 != 0 {
6191 raw += sum.signum();
6192 }
6193 let raw = PriceRaw::try_from(raw).ok()?;
6194 Price::from_raw_checked(raw, precision).ok()
6195 }),
6196 PriceType::Last => self
6197 .trades
6198 .get(instrument_id)
6199 .and_then(|trades| trades.front().map(|trade| trade.price)),
6200 PriceType::Mark => self
6201 .mark_prices
6202 .get(instrument_id)
6203 .and_then(|marks| marks.front().map(|mark| mark.value)),
6204 }
6205 }
6206
6207 #[must_use]
6212 pub fn quotes(&self, instrument_id: &InstrumentId) -> Option<Vec<QuoteTick>> {
6213 self.quotes
6214 .get(instrument_id)
6215 .map(|quotes| quotes.iter().copied().collect())
6216 }
6217
6218 #[must_use]
6223 pub fn trades(&self, instrument_id: &InstrumentId) -> Option<Vec<TradeTick>> {
6224 self.trades
6225 .get(instrument_id)
6226 .map(|trades| trades.iter().copied().collect())
6227 }
6228
6229 #[must_use]
6234 pub fn mark_prices(&self, instrument_id: &InstrumentId) -> Option<Vec<MarkPriceUpdate>> {
6235 self.mark_prices
6236 .get(instrument_id)
6237 .map(|mark_prices| mark_prices.iter().copied().collect())
6238 }
6239
6240 #[must_use]
6245 pub fn index_prices(&self, instrument_id: &InstrumentId) -> Option<Vec<IndexPriceUpdate>> {
6246 self.index_prices
6247 .get(instrument_id)
6248 .map(|index_prices| index_prices.iter().copied().collect())
6249 }
6250
6251 #[must_use]
6256 pub fn funding_rates(&self, instrument_id: &InstrumentId) -> Option<Vec<FundingRateUpdate>> {
6257 self.funding_rates
6258 .get(instrument_id)
6259 .map(|funding_rates| funding_rates.iter().copied().collect())
6260 }
6261
6262 #[must_use]
6267 pub fn instrument_statuses(
6268 &self,
6269 instrument_id: &InstrumentId,
6270 ) -> Option<Vec<InstrumentStatus>> {
6271 self.instrument_statuses
6272 .get(instrument_id)
6273 .map(|statuses| statuses.iter().copied().collect())
6274 }
6275
6276 #[must_use]
6280 pub fn bars(&self, bar_type: &BarType) -> Option<Vec<Bar>> {
6281 self.bars
6282 .get(bar_type)
6283 .map(|bars| bars.iter().copied().collect())
6284 }
6285
6286 #[must_use]
6290 pub fn order_book(&self, instrument_id: &InstrumentId) -> Option<&OrderBook> {
6291 self.books.get(instrument_id)
6292 }
6293
6294 pub fn try_order_book(
6300 &self,
6301 instrument_id: &InstrumentId,
6302 ) -> Result<&OrderBook, OrderBookLookupError> {
6303 self.books
6304 .get(instrument_id)
6305 .ok_or_else(|| OrderBookLookupError::not_found(*instrument_id))
6306 }
6307
6308 #[must_use]
6310 pub fn order_book_mut(&mut self, instrument_id: &InstrumentId) -> Option<&mut OrderBook> {
6311 self.books.get_mut(instrument_id)
6312 }
6313
6314 #[must_use]
6316 pub fn own_order_book(&self, instrument_id: &InstrumentId) -> Option<&OwnOrderBook> {
6317 self.own_books.get(instrument_id)
6318 }
6319
6320 pub fn try_own_order_book(
6326 &self,
6327 instrument_id: &InstrumentId,
6328 ) -> Result<&OwnOrderBook, OwnOrderBookLookupError> {
6329 self.own_books
6330 .get(instrument_id)
6331 .ok_or_else(|| OwnOrderBookLookupError::not_found(*instrument_id))
6332 }
6333
6334 #[must_use]
6336 pub fn own_order_book_mut(
6337 &mut self,
6338 instrument_id: &InstrumentId,
6339 ) -> Option<&mut OwnOrderBook> {
6340 self.own_books.get_mut(instrument_id)
6341 }
6342
6343 #[must_use]
6345 pub fn quote(&self, instrument_id: &InstrumentId) -> Option<&QuoteTick> {
6346 self.quotes
6347 .get(instrument_id)
6348 .and_then(|quotes| quotes.front())
6349 }
6350
6351 #[must_use]
6356 pub fn quote_at_index(&self, instrument_id: &InstrumentId, index: usize) -> Option<&QuoteTick> {
6357 self.quotes
6358 .get(instrument_id)
6359 .and_then(|quotes| quotes.get(index))
6360 }
6361
6362 #[must_use]
6364 pub fn trade(&self, instrument_id: &InstrumentId) -> Option<&TradeTick> {
6365 self.trades
6366 .get(instrument_id)
6367 .and_then(|trades| trades.front())
6368 }
6369
6370 #[must_use]
6375 pub fn trade_at_index(&self, instrument_id: &InstrumentId, index: usize) -> Option<&TradeTick> {
6376 self.trades
6377 .get(instrument_id)
6378 .and_then(|trades| trades.get(index))
6379 }
6380
6381 #[must_use]
6383 pub fn mark_price(&self, instrument_id: &InstrumentId) -> Option<&MarkPriceUpdate> {
6384 self.mark_prices
6385 .get(instrument_id)
6386 .and_then(|mark_prices| mark_prices.front())
6387 }
6388
6389 #[must_use]
6391 pub fn index_price(&self, instrument_id: &InstrumentId) -> Option<&IndexPriceUpdate> {
6392 self.index_prices
6393 .get(instrument_id)
6394 .and_then(|index_prices| index_prices.front())
6395 }
6396
6397 #[must_use]
6399 pub fn funding_rate(&self, instrument_id: &InstrumentId) -> Option<&FundingRateUpdate> {
6400 self.funding_rates
6401 .get(instrument_id)
6402 .and_then(|funding_rates| funding_rates.front())
6403 }
6404
6405 #[must_use]
6407 pub fn instrument_status(&self, instrument_id: &InstrumentId) -> Option<&InstrumentStatus> {
6408 self.instrument_statuses
6409 .get(instrument_id)
6410 .and_then(|statuses| statuses.front())
6411 }
6412
6413 #[must_use]
6417 pub fn instrument_close(&self, instrument_id: &InstrumentId) -> Option<&InstrumentClose> {
6418 self.instrument_closes.get(instrument_id)
6419 }
6420
6421 #[must_use]
6423 pub fn instrument_close_ids(&self) -> Vec<&InstrumentId> {
6424 self.instrument_closes.keys().collect()
6425 }
6426
6427 #[must_use]
6429 pub fn bar(&self, bar_type: &BarType) -> Option<&Bar> {
6430 self.bars.get(bar_type).and_then(|bars| bars.front())
6431 }
6432
6433 #[must_use]
6438 pub fn bar_at_index(&self, bar_type: &BarType, index: usize) -> Option<&Bar> {
6439 self.bars.get(bar_type).and_then(|bars| bars.get(index))
6440 }
6441
6442 #[must_use]
6444 pub fn book_update_count(&self, instrument_id: &InstrumentId) -> usize {
6445 self.books
6446 .get(instrument_id)
6447 .map_or(0, |book| book.update_count) as usize
6448 }
6449
6450 #[must_use]
6452 pub fn quote_count(&self, instrument_id: &InstrumentId) -> usize {
6453 self.quotes
6454 .get(instrument_id)
6455 .map_or(0, BoundedVecDeque::len)
6456 }
6457
6458 #[must_use]
6460 pub fn trade_count(&self, instrument_id: &InstrumentId) -> usize {
6461 self.trades
6462 .get(instrument_id)
6463 .map_or(0, BoundedVecDeque::len)
6464 }
6465
6466 #[must_use]
6468 pub fn mark_price_count(&self, instrument_id: &InstrumentId) -> usize {
6469 self.mark_prices
6470 .get(instrument_id)
6471 .map_or(0, BoundedVecDeque::len)
6472 }
6473
6474 #[must_use]
6476 pub fn index_price_count(&self, instrument_id: &InstrumentId) -> usize {
6477 self.index_prices
6478 .get(instrument_id)
6479 .map_or(0, BoundedVecDeque::len)
6480 }
6481
6482 #[must_use]
6484 pub fn funding_rate_count(&self, instrument_id: &InstrumentId) -> usize {
6485 self.funding_rates
6486 .get(instrument_id)
6487 .map_or(0, BoundedVecDeque::len)
6488 }
6489
6490 #[must_use]
6492 pub fn instrument_status_count(&self, instrument_id: &InstrumentId) -> usize {
6493 self.instrument_statuses
6494 .get(instrument_id)
6495 .map_or(0, BoundedVecDeque::len)
6496 }
6497
6498 #[must_use]
6500 pub fn bar_count(&self, bar_type: &BarType) -> usize {
6501 self.bars.get(bar_type).map_or(0, BoundedVecDeque::len)
6502 }
6503
6504 #[must_use]
6506 pub fn has_order_book(&self, instrument_id: &InstrumentId) -> bool {
6507 self.books.contains_key(instrument_id)
6508 }
6509
6510 #[must_use]
6512 pub fn has_quote_ticks(&self, instrument_id: &InstrumentId) -> bool {
6513 self.quote_count(instrument_id) > 0
6514 }
6515
6516 #[must_use]
6518 pub fn has_trade_ticks(&self, instrument_id: &InstrumentId) -> bool {
6519 self.trade_count(instrument_id) > 0
6520 }
6521
6522 #[must_use]
6524 pub fn has_mark_prices(&self, instrument_id: &InstrumentId) -> bool {
6525 self.mark_price_count(instrument_id) > 0
6526 }
6527
6528 #[must_use]
6530 pub fn has_index_prices(&self, instrument_id: &InstrumentId) -> bool {
6531 self.index_price_count(instrument_id) > 0
6532 }
6533
6534 #[must_use]
6536 pub fn has_funding_rates(&self, instrument_id: &InstrumentId) -> bool {
6537 self.funding_rate_count(instrument_id) > 0
6538 }
6539
6540 #[must_use]
6542 pub fn has_instrument_statuses(&self, instrument_id: &InstrumentId) -> bool {
6543 self.instrument_status_count(instrument_id) > 0
6544 }
6545
6546 #[must_use]
6548 pub fn has_instrument_close(&self, instrument_id: &InstrumentId) -> bool {
6549 self.instrument_closes.contains_key(instrument_id)
6550 }
6551
6552 #[must_use]
6554 pub fn has_bars(&self, bar_type: &BarType) -> bool {
6555 self.bar_count(bar_type) > 0
6556 }
6557
6558 #[must_use]
6559 pub fn get_xrate(
6560 &self,
6561 venue: Venue,
6562 from_currency: Currency,
6563 to_currency: Currency,
6564 price_type: PriceType,
6565 ) -> Option<Decimal> {
6566 match self.try_get_xrate(venue, from_currency, to_currency, price_type) {
6567 Ok(rate) => rate,
6568 Err(e) => {
6569 log::error!("Failed to calculate xrate: {e}");
6570 None
6571 }
6572 }
6573 }
6574
6575 pub fn try_get_xrate(
6581 &self,
6582 venue: Venue,
6583 from_currency: Currency,
6584 to_currency: Currency,
6585 price_type: PriceType,
6586 ) -> anyhow::Result<Option<Decimal>> {
6587 if from_currency == to_currency {
6588 return Ok(Some(Decimal::ONE));
6591 }
6592
6593 let (bid_quote, ask_quote) = self.build_quote_table(&venue);
6594
6595 get_exchange_rate(
6596 from_currency.code,
6597 to_currency.code,
6598 price_type,
6599 bid_quote,
6600 ask_quote,
6601 )
6602 }
6603
6604 fn build_quote_table(
6605 &self,
6606 venue: &Venue,
6607 ) -> (AHashMap<Ustr, Decimal>, AHashMap<Ustr, Decimal>) {
6608 let mut bid_quotes = AHashMap::new();
6609 let mut ask_quotes = AHashMap::new();
6610 let mut quote_sources = AHashMap::new();
6611 let mut bar_quotes = None;
6612 let mut pair_buffer = String::new();
6613
6614 for (instrument_id, instrument) in &self.instruments {
6615 if instrument_id.venue != *venue {
6616 continue;
6617 }
6618
6619 let Some(base_currency) = instrument.base_currency() else {
6620 continue;
6621 };
6622
6623 let (bid_price, ask_price) = if let Some(ticks) = self.quotes.get(instrument_id) {
6624 if let Some(tick) = ticks.front() {
6625 (tick.bid_price, tick.ask_price)
6626 } else {
6627 continue; }
6629 } else {
6630 let quotes = bar_quotes.get_or_insert_with(|| self.build_bar_quote_table(venue));
6631 match (
6632 quotes.get(&(*instrument_id, PriceType::Bid)),
6633 quotes.get(&(*instrument_id, PriceType::Ask)),
6634 ) {
6635 (Some((_, bid_bar)), Some((_, ask_bar))) => (bid_bar.close, ask_bar.close),
6636 _ => continue,
6637 }
6638 };
6639
6640 let base = base_currency.code.as_str();
6641 let quote = instrument.quote_currency().code.as_str();
6642 pair_buffer.clear();
6643 pair_buffer.reserve(base.len() + 1 + quote.len());
6644 pair_buffer.push_str(base);
6645 pair_buffer.push('/');
6646 pair_buffer.push_str(quote);
6647 let pair = Ustr::from(pair_buffer.as_str());
6648 let preference = (
6649 bid_price.is_positive() && ask_price.is_positive(),
6650 instrument.instrument_class() == InstrumentClass::Spot,
6651 Reverse(*instrument_id),
6652 );
6653
6654 if quote_sources
6655 .get(&pair)
6656 .is_some_and(|current| current >= &preference)
6657 {
6658 continue;
6659 }
6660
6661 bid_quotes.insert(pair, bid_price.as_decimal());
6662 ask_quotes.insert(pair, ask_price.as_decimal());
6663 quote_sources.insert(pair, preference);
6664 }
6665
6666 (bid_quotes, ask_quotes)
6667 }
6668
6669 fn build_bar_quote_table(
6670 &self,
6671 venue: &Venue,
6672 ) -> AHashMap<(InstrumentId, PriceType), (&BarType, &Bar)> {
6673 let mut quotes: AHashMap<_, (&BarType, &Bar)> = AHashMap::new();
6674
6675 for (bar_type, bars) in &self.bars {
6676 let instrument_id = bar_type.instrument_id();
6677 let price_type = bar_type.spec().price_type;
6678
6679 if instrument_id.venue != *venue
6680 || !matches!(price_type, PriceType::Bid | PriceType::Ask)
6681 {
6682 continue;
6683 }
6684
6685 let Some(bar) = bars.front() else {
6686 continue;
6687 };
6688
6689 quotes
6691 .entry((instrument_id, price_type))
6692 .and_modify(|current| {
6693 if (current.1.ts_init, current.0) < (bar.ts_init, bar_type) {
6694 *current = (bar_type, bar);
6695 }
6696 })
6697 .or_insert((bar_type, bar));
6698 }
6699
6700 quotes
6701 }
6702
6703 #[must_use]
6705 pub fn get_mark_xrate(&self, from_currency: Currency, to_currency: Currency) -> Option<f64> {
6706 self.mark_xrates.get(&(from_currency, to_currency)).copied()
6707 }
6708
6709 pub fn set_mark_xrate(&mut self, from_currency: Currency, to_currency: Currency, xrate: f64) {
6715 assert!(xrate > 0.0, "xrate was zero");
6716 self.mark_xrates.insert((from_currency, to_currency), xrate);
6717 self.mark_xrates
6718 .insert((to_currency, from_currency), 1.0 / xrate);
6719 }
6720
6721 pub fn clear_mark_xrate(&mut self, from_currency: Currency, to_currency: Currency) {
6727 let _ = self.mark_xrates.remove(&(from_currency, to_currency));
6728 }
6729
6730 pub fn clear_mark_xrates(&mut self) {
6732 self.mark_xrates.clear();
6733 }
6734
6735 #[must_use]
6737 pub fn currency(&self, code: &Ustr) -> Option<&Currency> {
6738 self.currencies.get(code)
6739 }
6740
6741 pub fn try_currency(&self, code: &Ustr) -> Result<&Currency, CurrencyLookupError> {
6747 self.currencies
6748 .get(code)
6749 .ok_or_else(|| CurrencyLookupError::not_found(*code))
6750 }
6751
6752 #[must_use]
6756 pub fn instrument(&self, instrument_id: &InstrumentId) -> Option<&InstrumentAny> {
6757 self.instruments.get(instrument_id)
6758 }
6759
6760 pub fn try_instrument(
6766 &self,
6767 instrument_id: &InstrumentId,
6768 ) -> Result<&InstrumentAny, InstrumentLookupError> {
6769 self.instruments
6770 .get(instrument_id)
6771 .ok_or_else(|| InstrumentLookupError::not_found(*instrument_id))
6772 }
6773
6774 #[must_use]
6776 pub fn instrument_ids(&self, venue: Option<&Venue>) -> Vec<&InstrumentId> {
6777 match venue {
6778 Some(v) => self.instruments.keys().filter(|i| &i.venue == v).collect(),
6779 None => self.instruments.keys().collect(),
6780 }
6781 }
6782
6783 #[must_use]
6785 pub fn instruments(&self, venue: &Venue, underlying: Option<&Ustr>) -> Vec<&InstrumentAny> {
6786 self.instruments
6787 .values()
6788 .filter(|i| &i.id().venue == venue)
6789 .filter(|i| underlying.is_none_or(|u| i.underlying() == Some(*u)))
6790 .collect()
6791 }
6792
6793 #[must_use]
6800 pub fn instruments_by_parent(
6801 &self,
6802 venue: &Venue,
6803 root: &Ustr,
6804 class: InstrumentClass,
6805 ) -> Vec<&InstrumentAny> {
6806 self.instruments
6807 .values()
6808 .filter(|i| &i.id().venue == venue)
6809 .filter(|i| i.underlying() == Some(*root))
6810 .filter(|i| i.instrument_class() == class)
6811 .collect()
6812 }
6813
6814 #[must_use]
6816 pub fn bar_types(
6817 &self,
6818 instrument_id: Option<&InstrumentId>,
6819 price_type: Option<&PriceType>,
6820 aggregation_source: AggregationSource,
6821 ) -> Vec<&BarType> {
6822 let mut bar_types = self
6823 .bars
6824 .keys()
6825 .filter(|bar_type| bar_type.aggregation_source() == aggregation_source)
6826 .collect::<Vec<&BarType>>();
6827
6828 if let Some(instrument_id) = instrument_id {
6829 bar_types.retain(|bar_type| bar_type.instrument_id() == *instrument_id);
6830 }
6831
6832 if let Some(price_type) = price_type {
6833 bar_types.retain(|bar_type| &bar_type.spec().price_type == price_type);
6834 }
6835
6836 bar_types
6837 }
6838
6839 #[must_use]
6843 pub fn synthetic(&self, instrument_id: &InstrumentId) -> Option<&SyntheticInstrument> {
6844 self.synthetics.get(instrument_id)
6845 }
6846
6847 pub fn try_synthetic(
6853 &self,
6854 instrument_id: &InstrumentId,
6855 ) -> Result<&SyntheticInstrument, SyntheticInstrumentLookupError> {
6856 self.synthetics
6857 .get(instrument_id)
6858 .ok_or_else(|| SyntheticInstrumentLookupError::not_found(*instrument_id))
6859 }
6860
6861 #[must_use]
6863 pub fn synthetic_ids(&self) -> Vec<&InstrumentId> {
6864 self.synthetics.keys().collect()
6865 }
6866
6867 #[must_use]
6869 pub fn synthetics(&self) -> Vec<&SyntheticInstrument> {
6870 self.synthetics.values().collect()
6871 }
6872
6873 #[must_use]
6877 pub fn account_ref(&self, account_id: &AccountId) -> Option<AccountRef<'_>> {
6878 self.accounts
6879 .get(account_id)
6880 .map(|account_cell| AccountRef::new(account_cell.borrow()))
6881 }
6882
6883 #[must_use]
6887 pub fn account(&self, account_id: &AccountId) -> Option<AccountRef<'_>> {
6888 self.account_ref(account_id)
6889 }
6890
6891 pub fn try_account_ref(
6897 &self,
6898 account_id: &AccountId,
6899 ) -> Result<AccountRef<'_>, AccountLookupError> {
6900 self.accounts
6901 .get(account_id)
6902 .map(|account_cell| AccountRef::new(account_cell.borrow()))
6903 .ok_or_else(|| AccountLookupError::not_found(*account_id))
6904 }
6905
6906 pub fn try_account(
6914 &self,
6915 account_id: &AccountId,
6916 ) -> Result<AccountRef<'_>, AccountLookupError> {
6917 self.try_account_ref(account_id)
6918 }
6919
6920 #[must_use]
6930 pub fn account_mut(&mut self, account_id: &AccountId) -> Option<AccountRefMut<'_>> {
6931 self.accounts
6932 .get(account_id)
6933 .map(|account_cell| AccountRefMut::new(account_cell.borrow_mut()))
6934 }
6935
6936 #[must_use]
6942 pub fn account_owned(&self, account_id: &AccountId) -> Option<AccountAny> {
6943 self.accounts.get(account_id).and_then(|account_cell| {
6944 account_cell
6945 .try_borrow()
6946 .ok()
6947 .map(|account| account.clone())
6948 })
6949 }
6950
6951 #[must_use]
6956 pub fn account_for_venue(&self, venue: &Venue) -> Option<AccountRef<'_>> {
6957 self.account_id(venue)
6958 .and_then(|account_id| self.accounts.get(account_id))
6959 .map(|account_cell| AccountRef::new(account_cell.borrow()))
6960 }
6961
6962 #[must_use]
6968 pub fn account_for_venue_owned(&self, venue: &Venue) -> Option<AccountAny> {
6969 self.account_id(venue)
6970 .and_then(|account_id| self.accounts.get(account_id))
6971 .map(|account_cell| account_cell.borrow().clone())
6972 }
6973
6974 #[must_use]
6978 pub fn account_id(&self, venue: &Venue) -> Option<&AccountId> {
6979 let mut account_ids = self.index.venue_accounts.get(venue)?.iter();
6980 let account_id = account_ids.next()?;
6981 account_ids.next().is_none().then_some(account_id)
6982 }
6983
6984 #[must_use]
6990 pub fn accounts(&self, account_id: &AccountId) -> Vec<AccountRef<'_>> {
6991 self.accounts
6992 .values()
6993 .filter(|account_cell| &account_cell.borrow().id() == account_id)
6994 .map(|account_cell| AccountRef::new(account_cell.borrow()))
6995 .collect()
6996 }
6997
6998 #[must_use]
7000 pub fn accounts_all_owned(&self) -> Vec<AccountAny> {
7001 self.accounts
7002 .values()
7003 .map(|account_cell| account_cell.borrow().clone())
7004 .collect()
7005 }
7006
7007 pub fn update_own_order_book(&mut self, order: &OrderAny) {
7015 if !order.has_price() {
7016 return;
7017 }
7018
7019 let instrument_id = order.instrument_id();
7020
7021 if !self.own_books.contains_key(&instrument_id) {
7022 if order.is_closed() {
7023 return;
7024 }
7025
7026 self.own_books
7027 .insert(instrument_id, OwnOrderBook::new(instrument_id));
7028 }
7029
7030 let Some(own_book) = self.own_books.get_mut(&instrument_id) else {
7031 return;
7032 };
7033
7034 let own_book_order = order.to_own_book_order();
7035
7036 if order.is_closed() {
7037 if let Err(e) = own_book.delete(own_book_order) {
7038 log::debug!(
7039 "Failed to delete order {} from own book: {e}",
7040 order.client_order_id(),
7041 );
7042 } else {
7043 log::debug!("Deleted order {} from own book", order.client_order_id());
7044 }
7045 } else {
7046 if let Err(e) = own_book.update(own_book_order) {
7048 log::debug!(
7049 "Failed to update order {} in own book: {e}; inserting instead",
7050 order.client_order_id(),
7051 );
7052 own_book.add(own_book_order);
7053 }
7054 log::debug!("Updated order {} in own book", order.client_order_id());
7055 }
7056 }
7057
7058 pub fn force_remove_from_own_order_book(&mut self, client_order_id: &ClientOrderId) {
7064 let Some(order_cell) = self.orders.get(client_order_id) else {
7065 return;
7066 };
7067 let order = order_cell.borrow();
7068 let instrument_id = order.instrument_id();
7069 let own_book_order = if order.has_price() {
7070 Some(order.to_own_book_order())
7071 } else {
7072 None
7073 };
7074 drop(order);
7075
7076 self.index.orders_open.remove(client_order_id);
7077 self.index.orders_pending_cancel.remove(client_order_id);
7078 self.index.orders_inflight.remove(client_order_id);
7079 self.index.orders_emulated.remove(client_order_id);
7080 self.index.orders_active_local.remove(client_order_id);
7081
7082 if let Some(own_book) = self.own_books.get_mut(&instrument_id)
7083 && let Some(own_book_order) = own_book_order
7084 {
7085 if let Err(e) = own_book.delete(own_book_order) {
7086 log::debug!("Could not force delete {client_order_id} from own book: {e}");
7087 } else {
7088 log::debug!("Force deleted {client_order_id} from own book");
7089 }
7090 }
7091
7092 self.index.orders_closed.insert(*client_order_id);
7093 }
7094
7095 pub fn audit_own_order_books(&mut self) {
7100 log::debug!("Starting own books audit");
7101 let start = std::time::Instant::now();
7102
7103 let valid_order_ids: AHashSet<ClientOrderId> = self
7104 .index
7105 .orders_open
7106 .iter()
7107 .chain(&self.index.orders_inflight)
7108 .chain(&self.index.orders_active_local)
7109 .copied()
7110 .collect();
7111
7112 for own_book in self.own_books.values_mut() {
7113 own_book.audit_open_orders(&valid_order_ids);
7114 }
7115
7116 log::debug!("Completed own books audit in {:?}", start.elapsed());
7117 }
7118}
7119
7120const POSITION_OMS_KEY_PREFIX: &str = "position_oms:";
7121
7122fn position_oms_key(position_id: PositionId) -> String {
7123 format!("{POSITION_OMS_KEY_PREFIX}{position_id}")
7124}
7125
7126fn resolve_position_oms(position: &Position, oms_type: OmsType) -> OmsType {
7127 if oms_type == OmsType::Unspecified
7128 && position.id.as_str() == format!("{}-{}", position.instrument_id, position.strategy_id)
7129 {
7130 OmsType::Netting
7131 } else {
7132 oms_type
7133 }
7134}
7135
7136fn insert_bar(bars: &mut BoundedVecDeque<Bar>, bar: Bar) {
7141 match bars.front() {
7142 None => bars.push_front(bar),
7143 Some(front) => match bar.ts_event.cmp(&front.ts_event) {
7144 Ordering::Greater => bars.push_front(bar),
7145 Ordering::Equal => {
7146 if bar.bar_type.spec().is_time_aggregated() {
7147 bars.replace_front(bar);
7148 } else {
7149 bars.push_front(bar);
7150 }
7151 }
7152 Ordering::Less => log::debug!(
7153 "Skipping bar {bar} with `ts_event` older than last bar `ts_event` {}",
7154 front.ts_event,
7155 ),
7156 },
7157 }
7158}
7159
7160fn insert_bar_historical(bars: &mut BoundedVecDeque<Bar>, bar: Bar) {
7165 let index = bars.partition_point(|cached| cached.ts_event > bar.ts_event);
7166
7167 if bar.bar_type.spec().is_time_aggregated()
7168 && bars
7169 .get(index)
7170 .is_some_and(|cached| cached.ts_event == bar.ts_event)
7171 {
7172 bars.replace(index, bar);
7173 } else {
7174 bars.insert(index, bar);
7175 }
7176}