Skip to main content

nautilus_common/cache/
api.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! User-facing read API over the platform cache.
17
18use std::{
19    cell::{Ref, RefCell},
20    fmt::Display,
21};
22
23use ahash::AHashSet;
24use bytes::Bytes;
25#[cfg(feature = "defi")]
26use nautilus_model::defi::{Pool, PoolProfiler};
27use nautilus_model::{
28    accounts::AccountAny,
29    data::{
30        Bar, BarType, FundingRateUpdate, GreeksData, IndexPriceUpdate, InstrumentClose,
31        InstrumentStatus, MarkPriceUpdate, QuoteTick, TradeTick, option_chain::OptionGreeks,
32    },
33    enums::{AggregationSource, InstrumentClass, OmsType, OrderSide, PositionSide, PriceType},
34    identifiers::{
35        AccountId, ClientId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
36        StrategyId, Venue, VenueOrderId,
37    },
38    instruments::{InstrumentAny, SyntheticInstrument},
39    orderbook::{OrderBook, own::OwnOrderBook},
40    orders::{OrderAny, OrderList},
41    position::Position,
42    types::{Currency, Money, Price, Quantity},
43};
44use rust_decimal::Decimal;
45use ustr::Ustr;
46
47use super::{
48    Cache,
49    error::{
50        AccountLookupError, CurrencyLookupError, InstrumentLookupError, OrderBookLookupError,
51        OrderListLookupError, OrderLookupError, OwnOrderBookLookupError, PositionLookupError,
52        SyntheticInstrumentLookupError,
53    },
54};
55use crate::component::ComponentAccessError;
56
57/// User-facing cache API.
58///
59/// Point reads return owned snapshots where possible, so actor code does not retain a `Ref` into
60/// the live [`Cache`]. Plural collection reads return owned snapshots of all matching values and
61/// are intentionally named as bulk reads. Prefer the count, ID, or `has_*` methods in hot paths
62/// when a full snapshot is not needed.
63#[derive(Debug)]
64pub struct CacheApi<'a> {
65    cache: &'a RefCell<Cache>,
66}
67
68impl<'a> CacheApi<'a> {
69    pub(crate) fn new(cache: &'a RefCell<Cache>) -> Self {
70        Self { cache }
71    }
72
73    /// Returns the unrealized PnL for the `position` using cached market data.
74    ///
75    /// # Panics
76    ///
77    /// Panics if the cache is already mutably borrowed.
78    #[must_use]
79    pub fn calculate_unrealized_pnl(&self, position: &Position) -> Option<Money> {
80        self.cache().calculate_unrealized_pnl(position)
81    }
82
83    /// Returns the OMS type for the `position_id` (if known).
84    ///
85    /// # Panics
86    ///
87    /// Panics if the cache is already mutably borrowed.
88    #[must_use]
89    pub fn oms_type(&self, position_id: &PositionId) -> Option<OmsType> {
90        self.cache().oms_type(position_id)
91    }
92
93    /// Returns serialized position snapshot frames for the `position_id`.
94    ///
95    /// # Panics
96    ///
97    /// Panics if the cache is already mutably borrowed.
98    #[must_use]
99    pub fn position_snapshot_bytes(&self, position_id: &PositionId) -> Option<Vec<Vec<u8>>> {
100        self.cache().position_snapshot_bytes(position_id)
101    }
102
103    /// Returns the number of stored position snapshots for the `position_id`.
104    ///
105    /// # Panics
106    ///
107    /// Panics if the cache is already mutably borrowed.
108    #[must_use]
109    pub fn position_snapshot_count(&self, position_id: &PositionId) -> usize {
110        self.cache().position_snapshot_count(position_id)
111    }
112
113    /// Returns position snapshots matching the optional filters.
114    ///
115    /// # Panics
116    ///
117    /// Panics if the cache is already mutably borrowed.
118    #[must_use]
119    pub fn position_snapshots(
120        &self,
121        position_id: Option<&PositionId>,
122        account_id: Option<&AccountId>,
123    ) -> Vec<Position> {
124        self.cache().position_snapshots(position_id, account_id)
125    }
126
127    /// Returns position snapshots for `position_id` starting from `skip`.
128    ///
129    /// # Panics
130    ///
131    /// Panics if the cache is already mutably borrowed.
132    #[must_use]
133    pub fn position_snapshots_from(&self, position_id: &PositionId, skip: usize) -> Vec<Position> {
134        self.cache().position_snapshots_from(position_id, skip)
135    }
136
137    /// Returns position snapshot IDs for the `instrument_id`.
138    ///
139    /// # Panics
140    ///
141    /// Panics if the cache is already mutably borrowed.
142    #[must_use]
143    pub fn position_snapshot_ids(&self, instrument_id: &InstrumentId) -> AHashSet<PositionId> {
144        self.cache().position_snapshot_ids(instrument_id)
145    }
146
147    /// Returns the client order IDs of all orders matching the optional filter parameters.
148    ///
149    /// # Panics
150    ///
151    /// Panics if the cache is already mutably borrowed.
152    #[must_use]
153    pub fn client_order_ids(
154        &self,
155        venue: Option<&Venue>,
156        instrument_id: Option<&InstrumentId>,
157        strategy_id: Option<&StrategyId>,
158        account_id: Option<&AccountId>,
159    ) -> AHashSet<ClientOrderId> {
160        self.cache()
161            .client_order_ids(venue, instrument_id, strategy_id, account_id)
162    }
163
164    /// Returns the client order IDs of all open orders matching the optional filter parameters.
165    ///
166    /// # Panics
167    ///
168    /// Panics if the cache is already mutably borrowed.
169    #[must_use]
170    pub fn client_order_ids_open(
171        &self,
172        venue: Option<&Venue>,
173        instrument_id: Option<&InstrumentId>,
174        strategy_id: Option<&StrategyId>,
175        account_id: Option<&AccountId>,
176    ) -> AHashSet<ClientOrderId> {
177        self.cache()
178            .client_order_ids_open(venue, instrument_id, strategy_id, account_id)
179    }
180
181    /// Returns the client order IDs of all closed orders matching the optional filter parameters.
182    ///
183    /// # Panics
184    ///
185    /// Panics if the cache is already mutably borrowed.
186    #[must_use]
187    pub fn client_order_ids_closed(
188        &self,
189        venue: Option<&Venue>,
190        instrument_id: Option<&InstrumentId>,
191        strategy_id: Option<&StrategyId>,
192        account_id: Option<&AccountId>,
193    ) -> AHashSet<ClientOrderId> {
194        self.cache()
195            .client_order_ids_closed(venue, instrument_id, strategy_id, account_id)
196    }
197
198    /// Returns the client order IDs of all locally active orders matching the optional filter parameters.
199    ///
200    /// # Panics
201    ///
202    /// Panics if the cache is already mutably borrowed.
203    #[must_use]
204    pub fn client_order_ids_active_local(
205        &self,
206        venue: Option<&Venue>,
207        instrument_id: Option<&InstrumentId>,
208        strategy_id: Option<&StrategyId>,
209        account_id: Option<&AccountId>,
210    ) -> AHashSet<ClientOrderId> {
211        self.cache()
212            .client_order_ids_active_local(venue, instrument_id, strategy_id, account_id)
213    }
214
215    /// Returns the client order IDs of all emulated orders matching the optional filter parameters.
216    ///
217    /// # Panics
218    ///
219    /// Panics if the cache is already mutably borrowed.
220    #[must_use]
221    pub fn client_order_ids_emulated(
222        &self,
223        venue: Option<&Venue>,
224        instrument_id: Option<&InstrumentId>,
225        strategy_id: Option<&StrategyId>,
226        account_id: Option<&AccountId>,
227    ) -> AHashSet<ClientOrderId> {
228        self.cache()
229            .client_order_ids_emulated(venue, instrument_id, strategy_id, account_id)
230    }
231
232    /// Returns the client order IDs of all in-flight orders matching the optional filter parameters.
233    ///
234    /// # Panics
235    ///
236    /// Panics if the cache is already mutably borrowed.
237    #[must_use]
238    pub fn client_order_ids_inflight(
239        &self,
240        venue: Option<&Venue>,
241        instrument_id: Option<&InstrumentId>,
242        strategy_id: Option<&StrategyId>,
243        account_id: Option<&AccountId>,
244    ) -> AHashSet<ClientOrderId> {
245        self.cache()
246            .client_order_ids_inflight(venue, instrument_id, strategy_id, account_id)
247    }
248
249    /// Returns the position IDs of all positions matching the optional filter parameters.
250    ///
251    /// # Panics
252    ///
253    /// Panics if the cache is already mutably borrowed.
254    #[must_use]
255    pub fn position_ids(
256        &self,
257        venue: Option<&Venue>,
258        instrument_id: Option<&InstrumentId>,
259        strategy_id: Option<&StrategyId>,
260        account_id: Option<&AccountId>,
261    ) -> AHashSet<PositionId> {
262        self.cache()
263            .position_ids(venue, instrument_id, strategy_id, account_id)
264    }
265
266    /// Returns the position IDs of all open positions matching the optional filter parameters.
267    ///
268    /// # Panics
269    ///
270    /// Panics if the cache is already mutably borrowed.
271    #[must_use]
272    pub fn position_open_ids(
273        &self,
274        venue: Option<&Venue>,
275        instrument_id: Option<&InstrumentId>,
276        strategy_id: Option<&StrategyId>,
277        account_id: Option<&AccountId>,
278    ) -> AHashSet<PositionId> {
279        self.cache()
280            .position_open_ids(venue, instrument_id, strategy_id, account_id)
281    }
282
283    /// Returns the position IDs of all closed positions matching the optional filter parameters.
284    ///
285    /// # Panics
286    ///
287    /// Panics if the cache is already mutably borrowed.
288    #[must_use]
289    pub fn position_closed_ids(
290        &self,
291        venue: Option<&Venue>,
292        instrument_id: Option<&InstrumentId>,
293        strategy_id: Option<&StrategyId>,
294        account_id: Option<&AccountId>,
295    ) -> AHashSet<PositionId> {
296        self.cache()
297            .position_closed_ids(venue, instrument_id, strategy_id, account_id)
298    }
299
300    /// Returns the strategy IDs in the cache.
301    ///
302    /// # Panics
303    ///
304    /// Panics if the cache is already mutably borrowed.
305    #[must_use]
306    pub fn strategy_ids(&self) -> AHashSet<StrategyId> {
307        self.cache().strategy_ids()
308    }
309
310    /// Returns the execution algorithm IDs in the cache.
311    ///
312    /// # Panics
313    ///
314    /// Panics if the cache is already mutably borrowed.
315    #[must_use]
316    pub fn exec_algorithm_ids(&self) -> AHashSet<ExecAlgorithmId> {
317        self.cache().exec_algorithm_ids()
318    }
319
320    /// Returns an owned copy of the order for the `client_order_id` (if found).
321    ///
322    /// # Panics
323    ///
324    /// Panics if the cache is already mutably borrowed.
325    #[must_use]
326    pub fn order(&self, client_order_id: &ClientOrderId) -> Option<OrderAny> {
327        self.cache().order_owned(client_order_id)
328    }
329
330    /// Returns an owned copy of the order for the `client_order_id`.
331    ///
332    /// # Errors
333    ///
334    /// Returns:
335    /// - [`OrderLookupError::NotFound`] when the order is not present in the cache.
336    /// - [`OrderLookupError::Access`] if the cache is already mutably borrowed.
337    pub fn try_order(&self, client_order_id: &ClientOrderId) -> Result<OrderAny, OrderLookupError> {
338        self.try_cache("try_order")?
339            .try_order_owned(client_order_id)
340    }
341
342    /// Returns owned copies of the orders for `client_order_ids`.
343    ///
344    /// # Panics
345    ///
346    /// Panics if the cache is already mutably borrowed.
347    #[must_use]
348    pub fn orders_for_ids(
349        &self,
350        client_order_ids: &[ClientOrderId],
351        context: &dyn Display,
352    ) -> Vec<OrderAny> {
353        self.cache().orders_for_ids(client_order_ids, context)
354    }
355
356    /// Returns the client order ID for the `venue_order_id` (if found).
357    ///
358    /// # Panics
359    ///
360    /// Panics if the cache is already mutably borrowed.
361    #[must_use]
362    pub fn client_order_id(&self, venue_order_id: &VenueOrderId) -> Option<ClientOrderId> {
363        self.cache().client_order_id(venue_order_id).copied()
364    }
365
366    /// Returns the venue order ID for the `client_order_id` (if found).
367    ///
368    /// # Panics
369    ///
370    /// Panics if the cache is already mutably borrowed.
371    #[must_use]
372    pub fn venue_order_id(&self, client_order_id: &ClientOrderId) -> Option<VenueOrderId> {
373        self.cache().venue_order_id(client_order_id).copied()
374    }
375
376    /// Returns the client ID indexed for the `client_order_id` (if found).
377    ///
378    /// # Panics
379    ///
380    /// Panics if the cache is already mutably borrowed.
381    #[must_use]
382    pub fn client_id(&self, client_order_id: &ClientOrderId) -> Option<ClientId> {
383        self.cache().client_id(client_order_id).copied()
384    }
385
386    /// Returns owned copies of all orders matching the optional filter parameters.
387    ///
388    /// # Panics
389    ///
390    /// Panics if the cache is already mutably borrowed.
391    #[must_use]
392    pub fn orders(
393        &self,
394        venue: Option<&Venue>,
395        instrument_id: Option<&InstrumentId>,
396        strategy_id: Option<&StrategyId>,
397        account_id: Option<&AccountId>,
398        side: Option<OrderSide>,
399    ) -> Vec<OrderAny> {
400        self.cache()
401            .orders_refs(venue, instrument_id, strategy_id, account_id, side)
402            .into_iter()
403            .map(|order| order.cloned())
404            .collect()
405    }
406
407    /// Returns owned copies of all open orders matching the optional filter parameters.
408    ///
409    /// # Panics
410    ///
411    /// Panics if the cache is already mutably borrowed.
412    #[must_use]
413    pub fn orders_open(
414        &self,
415        venue: Option<&Venue>,
416        instrument_id: Option<&InstrumentId>,
417        strategy_id: Option<&StrategyId>,
418        account_id: Option<&AccountId>,
419        side: Option<OrderSide>,
420    ) -> Vec<OrderAny> {
421        self.cache()
422            .orders_open_refs(venue, instrument_id, strategy_id, account_id, side)
423            .into_iter()
424            .map(|order| order.cloned())
425            .collect()
426    }
427
428    /// Returns owned copies of all closed orders matching the optional filter parameters.
429    ///
430    /// # Panics
431    ///
432    /// Panics if the cache is already mutably borrowed.
433    #[must_use]
434    pub fn orders_closed(
435        &self,
436        venue: Option<&Venue>,
437        instrument_id: Option<&InstrumentId>,
438        strategy_id: Option<&StrategyId>,
439        account_id: Option<&AccountId>,
440        side: Option<OrderSide>,
441    ) -> Vec<OrderAny> {
442        self.cache()
443            .orders_closed_refs(venue, instrument_id, strategy_id, account_id, side)
444            .into_iter()
445            .map(|order| order.cloned())
446            .collect()
447    }
448
449    /// Returns owned copies of all locally active orders matching the optional filter parameters.
450    ///
451    /// # Panics
452    ///
453    /// Panics if the cache is already mutably borrowed.
454    #[must_use]
455    pub fn orders_active_local(
456        &self,
457        venue: Option<&Venue>,
458        instrument_id: Option<&InstrumentId>,
459        strategy_id: Option<&StrategyId>,
460        account_id: Option<&AccountId>,
461        side: Option<OrderSide>,
462    ) -> Vec<OrderAny> {
463        self.cache()
464            .orders_active_local_refs(venue, instrument_id, strategy_id, account_id, side)
465            .into_iter()
466            .map(|order| order.cloned())
467            .collect()
468    }
469
470    /// Returns owned copies of all emulated orders matching the optional filter parameters.
471    ///
472    /// # Panics
473    ///
474    /// Panics if the cache is already mutably borrowed.
475    #[must_use]
476    pub fn orders_emulated(
477        &self,
478        venue: Option<&Venue>,
479        instrument_id: Option<&InstrumentId>,
480        strategy_id: Option<&StrategyId>,
481        account_id: Option<&AccountId>,
482        side: Option<OrderSide>,
483    ) -> Vec<OrderAny> {
484        self.cache()
485            .orders_emulated_refs(venue, instrument_id, strategy_id, account_id, side)
486            .into_iter()
487            .map(|order| order.cloned())
488            .collect()
489    }
490
491    /// Returns owned copies of all in-flight orders matching the optional filter parameters.
492    ///
493    /// # Panics
494    ///
495    /// Panics if the cache is already mutably borrowed.
496    #[must_use]
497    pub fn orders_inflight(
498        &self,
499        venue: Option<&Venue>,
500        instrument_id: Option<&InstrumentId>,
501        strategy_id: Option<&StrategyId>,
502        account_id: Option<&AccountId>,
503        side: Option<OrderSide>,
504    ) -> Vec<OrderAny> {
505        self.cache()
506            .orders_inflight_refs(venue, instrument_id, strategy_id, account_id, side)
507            .into_iter()
508            .map(|order| order.cloned())
509            .collect()
510    }
511
512    /// Returns owned copies of all orders for the `position_id`.
513    ///
514    /// # Panics
515    ///
516    /// Panics if the cache is already mutably borrowed.
517    #[must_use]
518    pub fn orders_for_position(&self, position_id: &PositionId) -> Vec<OrderAny> {
519        self.cache()
520            .orders_for_position(position_id)
521            .into_iter()
522            .map(|order| order.cloned())
523            .collect()
524    }
525
526    /// Returns whether an order with the `client_order_id` exists.
527    ///
528    /// # Panics
529    ///
530    /// Panics if the cache is already mutably borrowed.
531    #[must_use]
532    pub fn order_exists(&self, client_order_id: &ClientOrderId) -> bool {
533        self.cache().order_exists(client_order_id)
534    }
535
536    /// Returns whether an order with the `client_order_id` is open.
537    ///
538    /// # Panics
539    ///
540    /// Panics if the cache is already mutably borrowed.
541    #[must_use]
542    pub fn is_order_open(&self, client_order_id: &ClientOrderId) -> bool {
543        self.cache().is_order_open(client_order_id)
544    }
545
546    /// Returns whether an order with the `client_order_id` is closed.
547    ///
548    /// # Panics
549    ///
550    /// Panics if the cache is already mutably borrowed.
551    #[must_use]
552    pub fn is_order_closed(&self, client_order_id: &ClientOrderId) -> bool {
553        self.cache().is_order_closed(client_order_id)
554    }
555
556    /// Returns whether an order with the `client_order_id` is locally active.
557    ///
558    /// # Panics
559    ///
560    /// Panics if the cache is already mutably borrowed.
561    #[must_use]
562    pub fn is_order_active_local(&self, client_order_id: &ClientOrderId) -> bool {
563        self.cache().is_order_active_local(client_order_id)
564    }
565
566    /// Returns whether an order with the `client_order_id` is emulated.
567    ///
568    /// # Panics
569    ///
570    /// Panics if the cache is already mutably borrowed.
571    #[must_use]
572    pub fn is_order_emulated(&self, client_order_id: &ClientOrderId) -> bool {
573        self.cache().is_order_emulated(client_order_id)
574    }
575
576    /// Returns whether an order with the `client_order_id` is in-flight.
577    ///
578    /// # Panics
579    ///
580    /// Panics if the cache is already mutably borrowed.
581    #[must_use]
582    pub fn is_order_inflight(&self, client_order_id: &ClientOrderId) -> bool {
583        self.cache().is_order_inflight(client_order_id)
584    }
585
586    /// Returns whether an order with the `client_order_id` is `PENDING_CANCEL` locally.
587    ///
588    /// # Panics
589    ///
590    /// Panics if the cache is already mutably borrowed.
591    #[must_use]
592    pub fn is_order_pending_cancel_local(&self, client_order_id: &ClientOrderId) -> bool {
593        self.cache().is_order_pending_cancel_local(client_order_id)
594    }
595
596    /// Returns the count of all open orders matching the optional filter parameters.
597    ///
598    /// # Panics
599    ///
600    /// Panics if the cache is already mutably borrowed.
601    #[must_use]
602    pub fn orders_open_count(
603        &self,
604        venue: Option<&Venue>,
605        instrument_id: Option<&InstrumentId>,
606        strategy_id: Option<&StrategyId>,
607        account_id: Option<&AccountId>,
608        side: Option<OrderSide>,
609    ) -> usize {
610        self.cache()
611            .orders_open_count(venue, instrument_id, strategy_id, account_id, side)
612    }
613
614    /// Returns the count of all closed orders matching the optional filter parameters.
615    ///
616    /// # Panics
617    ///
618    /// Panics if the cache is already mutably borrowed.
619    #[must_use]
620    pub fn orders_closed_count(
621        &self,
622        venue: Option<&Venue>,
623        instrument_id: Option<&InstrumentId>,
624        strategy_id: Option<&StrategyId>,
625        account_id: Option<&AccountId>,
626        side: Option<OrderSide>,
627    ) -> usize {
628        self.cache()
629            .orders_closed_count(venue, instrument_id, strategy_id, account_id, side)
630    }
631
632    /// Returns the count of all locally active orders matching the optional filter parameters.
633    ///
634    /// # Panics
635    ///
636    /// Panics if the cache is already mutably borrowed.
637    #[must_use]
638    pub fn orders_active_local_count(
639        &self,
640        venue: Option<&Venue>,
641        instrument_id: Option<&InstrumentId>,
642        strategy_id: Option<&StrategyId>,
643        account_id: Option<&AccountId>,
644        side: Option<OrderSide>,
645    ) -> usize {
646        self.cache()
647            .orders_active_local_count(venue, instrument_id, strategy_id, account_id, side)
648    }
649
650    /// Returns the count of all emulated orders matching the optional filter parameters.
651    ///
652    /// # Panics
653    ///
654    /// Panics if the cache is already mutably borrowed.
655    #[must_use]
656    pub fn orders_emulated_count(
657        &self,
658        venue: Option<&Venue>,
659        instrument_id: Option<&InstrumentId>,
660        strategy_id: Option<&StrategyId>,
661        account_id: Option<&AccountId>,
662        side: Option<OrderSide>,
663    ) -> usize {
664        self.cache()
665            .orders_emulated_count(venue, instrument_id, strategy_id, account_id, side)
666    }
667
668    /// Returns the count of all in-flight orders matching the optional filter parameters.
669    ///
670    /// # Panics
671    ///
672    /// Panics if the cache is already mutably borrowed.
673    #[must_use]
674    pub fn orders_inflight_count(
675        &self,
676        venue: Option<&Venue>,
677        instrument_id: Option<&InstrumentId>,
678        strategy_id: Option<&StrategyId>,
679        account_id: Option<&AccountId>,
680        side: Option<OrderSide>,
681    ) -> usize {
682        self.cache()
683            .orders_inflight_count(venue, instrument_id, strategy_id, account_id, side)
684    }
685
686    /// Returns the count of all orders matching the optional filter parameters.
687    ///
688    /// # Panics
689    ///
690    /// Panics if the cache is already mutably borrowed.
691    #[must_use]
692    pub fn orders_total_count(
693        &self,
694        venue: Option<&Venue>,
695        instrument_id: Option<&InstrumentId>,
696        strategy_id: Option<&StrategyId>,
697        account_id: Option<&AccountId>,
698        side: Option<OrderSide>,
699    ) -> usize {
700        self.cache()
701            .orders_total_count(venue, instrument_id, strategy_id, account_id, side)
702    }
703
704    /// Returns whether any open order matches the optional filter parameters.
705    ///
706    /// # Panics
707    ///
708    /// Panics if the cache is already mutably borrowed.
709    #[must_use]
710    pub fn has_orders_open(
711        &self,
712        venue: Option<&Venue>,
713        instrument_id: Option<&InstrumentId>,
714        strategy_id: Option<&StrategyId>,
715        account_id: Option<&AccountId>,
716        side: Option<OrderSide>,
717    ) -> bool {
718        self.cache()
719            .has_orders_open(venue, instrument_id, strategy_id, account_id, side)
720    }
721
722    /// Returns whether any closed order matches the optional filter parameters.
723    ///
724    /// # Panics
725    ///
726    /// Panics if the cache is already mutably borrowed.
727    #[must_use]
728    pub fn has_orders_closed(
729        &self,
730        venue: Option<&Venue>,
731        instrument_id: Option<&InstrumentId>,
732        strategy_id: Option<&StrategyId>,
733        account_id: Option<&AccountId>,
734        side: Option<OrderSide>,
735    ) -> bool {
736        self.cache()
737            .has_orders_closed(venue, instrument_id, strategy_id, account_id, side)
738    }
739
740    /// Returns whether any locally active order matches the optional filter parameters.
741    ///
742    /// # Panics
743    ///
744    /// Panics if the cache is already mutably borrowed.
745    #[must_use]
746    pub fn has_orders_active_local(
747        &self,
748        venue: Option<&Venue>,
749        instrument_id: Option<&InstrumentId>,
750        strategy_id: Option<&StrategyId>,
751        account_id: Option<&AccountId>,
752        side: Option<OrderSide>,
753    ) -> bool {
754        self.cache()
755            .has_orders_active_local(venue, instrument_id, strategy_id, account_id, side)
756    }
757
758    /// Returns whether any emulated order matches the optional filter parameters.
759    ///
760    /// # Panics
761    ///
762    /// Panics if the cache is already mutably borrowed.
763    #[must_use]
764    pub fn has_orders_emulated(
765        &self,
766        venue: Option<&Venue>,
767        instrument_id: Option<&InstrumentId>,
768        strategy_id: Option<&StrategyId>,
769        account_id: Option<&AccountId>,
770        side: Option<OrderSide>,
771    ) -> bool {
772        self.cache()
773            .has_orders_emulated(venue, instrument_id, strategy_id, account_id, side)
774    }
775
776    /// Returns whether any in-flight order matches the optional filter parameters.
777    ///
778    /// # Panics
779    ///
780    /// Panics if the cache is already mutably borrowed.
781    #[must_use]
782    pub fn has_orders_inflight(
783        &self,
784        venue: Option<&Venue>,
785        instrument_id: Option<&InstrumentId>,
786        strategy_id: Option<&StrategyId>,
787        account_id: Option<&AccountId>,
788        side: Option<OrderSide>,
789    ) -> bool {
790        self.cache()
791            .has_orders_inflight(venue, instrument_id, strategy_id, account_id, side)
792    }
793
794    /// Returns whether any order matches the optional filter parameters.
795    ///
796    /// # Panics
797    ///
798    /// Panics if the cache is already mutably borrowed.
799    #[must_use]
800    pub fn has_orders(
801        &self,
802        venue: Option<&Venue>,
803        instrument_id: Option<&InstrumentId>,
804        strategy_id: Option<&StrategyId>,
805        account_id: Option<&AccountId>,
806        side: Option<OrderSide>,
807    ) -> bool {
808        self.cache()
809            .has_orders(venue, instrument_id, strategy_id, account_id, side)
810    }
811
812    /// Returns an owned copy of the order list for the `order_list_id` (if found).
813    ///
814    /// # Panics
815    ///
816    /// Panics if the cache is already mutably borrowed.
817    #[must_use]
818    pub fn order_list(&self, order_list_id: &OrderListId) -> Option<OrderList> {
819        self.cache().order_list(order_list_id).cloned()
820    }
821
822    /// Returns an owned copy of the order list for the `order_list_id`.
823    ///
824    /// # Errors
825    ///
826    /// Returns:
827    /// - [`OrderListLookupError::NotFound`] when the order list is not present in the cache.
828    /// - [`OrderListLookupError::Access`] if the cache is already mutably borrowed.
829    pub fn try_order_list(
830        &self,
831        order_list_id: &OrderListId,
832    ) -> Result<OrderList, OrderListLookupError> {
833        self.try_cache("try_order_list")?
834            .try_order_list(order_list_id)
835            .cloned()
836    }
837
838    /// Returns owned copies of all order lists matching the optional filter parameters.
839    ///
840    /// # Panics
841    ///
842    /// Panics if the cache is already mutably borrowed.
843    #[must_use]
844    pub fn order_lists(
845        &self,
846        venue: Option<&Venue>,
847        instrument_id: Option<&InstrumentId>,
848        strategy_id: Option<&StrategyId>,
849        account_id: Option<&AccountId>,
850    ) -> Vec<OrderList> {
851        self.cache()
852            .order_lists(venue, instrument_id, strategy_id, account_id)
853            .into_iter()
854            .cloned()
855            .collect()
856    }
857
858    /// Returns whether an order list with the `order_list_id` exists.
859    ///
860    /// # Panics
861    ///
862    /// Panics if the cache is already mutably borrowed.
863    #[must_use]
864    pub fn order_list_exists(&self, order_list_id: &OrderListId) -> bool {
865        self.cache().order_list_exists(order_list_id)
866    }
867
868    /// Returns owned copies of all orders associated with the `exec_algorithm_id`.
869    ///
870    /// # Panics
871    ///
872    /// Panics if the cache is already mutably borrowed.
873    #[must_use]
874    pub fn orders_for_exec_algorithm(
875        &self,
876        exec_algorithm_id: &ExecAlgorithmId,
877        venue: Option<&Venue>,
878        instrument_id: Option<&InstrumentId>,
879        strategy_id: Option<&StrategyId>,
880        account_id: Option<&AccountId>,
881        side: Option<OrderSide>,
882    ) -> Vec<OrderAny> {
883        self.cache()
884            .orders_for_exec_algorithm(
885                exec_algorithm_id,
886                venue,
887                instrument_id,
888                strategy_id,
889                account_id,
890                side,
891            )
892            .into_iter()
893            .map(|order| order.cloned())
894            .collect()
895    }
896
897    /// Returns owned copies of all orders with the `exec_spawn_id`.
898    ///
899    /// # Panics
900    ///
901    /// Panics if the cache is already mutably borrowed.
902    #[must_use]
903    pub fn orders_for_exec_spawn(&self, exec_spawn_id: &ClientOrderId) -> Vec<OrderAny> {
904        self.cache()
905            .orders_for_exec_spawn(exec_spawn_id)
906            .into_iter()
907            .map(|order| order.cloned())
908            .collect()
909    }
910
911    /// Returns the total order quantity for the `exec_spawn_id`.
912    ///
913    /// # Panics
914    ///
915    /// Panics if the cache is already mutably borrowed.
916    #[must_use]
917    pub fn exec_spawn_total_quantity(
918        &self,
919        exec_spawn_id: &ClientOrderId,
920        active_only: bool,
921    ) -> Option<Quantity> {
922        self.cache()
923            .exec_spawn_total_quantity(exec_spawn_id, active_only)
924    }
925
926    /// Returns the total filled quantity for all orders with the `exec_spawn_id`.
927    ///
928    /// # Panics
929    ///
930    /// Panics if the cache is already mutably borrowed.
931    #[must_use]
932    pub fn exec_spawn_total_filled_qty(
933        &self,
934        exec_spawn_id: &ClientOrderId,
935        active_only: bool,
936    ) -> Option<Quantity> {
937        self.cache()
938            .exec_spawn_total_filled_qty(exec_spawn_id, active_only)
939    }
940
941    /// Returns the total leaves quantity for all orders with the `exec_spawn_id`.
942    ///
943    /// # Panics
944    ///
945    /// Panics if the cache is already mutably borrowed.
946    #[must_use]
947    pub fn exec_spawn_total_leaves_qty(
948        &self,
949        exec_spawn_id: &ClientOrderId,
950        active_only: bool,
951    ) -> Option<Quantity> {
952        self.cache()
953            .exec_spawn_total_leaves_qty(exec_spawn_id, active_only)
954    }
955
956    /// Returns an owned copy of the position for the `position_id` (if found).
957    ///
958    /// # Panics
959    ///
960    /// Panics if the cache is already mutably borrowed.
961    #[must_use]
962    pub fn position(&self, position_id: &PositionId) -> Option<Position> {
963        self.cache()
964            .position_ref(position_id)
965            .map(|position| position.cloned())
966    }
967
968    /// Returns an owned copy of the position for the `position_id`.
969    ///
970    /// # Errors
971    ///
972    /// Returns:
973    /// - [`PositionLookupError::NotFound`] when the position is not present in the cache.
974    /// - [`PositionLookupError::Access`] if the cache is already mutably borrowed.
975    pub fn try_position(&self, position_id: &PositionId) -> Result<Position, PositionLookupError> {
976        self.try_cache("try_position")?
977            .try_position_ref(position_id)
978            .map(|position| position.cloned())
979    }
980
981    /// Returns an owned copy of the position for the `client_order_id` (if found).
982    ///
983    /// # Panics
984    ///
985    /// Panics if the cache is already mutably borrowed.
986    #[must_use]
987    pub fn position_for_order(&self, client_order_id: &ClientOrderId) -> Option<Position> {
988        self.cache()
989            .position_for_order_ref(client_order_id)
990            .map(|position| position.cloned())
991    }
992
993    /// Returns the position ID for the `client_order_id` (if found).
994    ///
995    /// # Panics
996    ///
997    /// Panics if the cache is already mutably borrowed.
998    #[must_use]
999    pub fn position_id(&self, client_order_id: &ClientOrderId) -> Option<PositionId> {
1000        self.cache().position_id(client_order_id).copied()
1001    }
1002
1003    /// Returns owned copies of all positions matching the optional filter parameters.
1004    ///
1005    /// # Panics
1006    ///
1007    /// Panics if the cache is already mutably borrowed.
1008    #[must_use]
1009    pub fn positions(
1010        &self,
1011        venue: Option<&Venue>,
1012        instrument_id: Option<&InstrumentId>,
1013        strategy_id: Option<&StrategyId>,
1014        account_id: Option<&AccountId>,
1015        side: Option<PositionSide>,
1016    ) -> Vec<Position> {
1017        self.cache()
1018            .positions_refs(venue, instrument_id, strategy_id, account_id, side)
1019            .into_iter()
1020            .map(|position| position.cloned())
1021            .collect()
1022    }
1023
1024    /// Returns owned copies of all open positions matching the optional filter parameters.
1025    ///
1026    /// # Panics
1027    ///
1028    /// Panics if the cache is already mutably borrowed.
1029    #[must_use]
1030    pub fn positions_open(
1031        &self,
1032        venue: Option<&Venue>,
1033        instrument_id: Option<&InstrumentId>,
1034        strategy_id: Option<&StrategyId>,
1035        account_id: Option<&AccountId>,
1036        side: Option<PositionSide>,
1037    ) -> Vec<Position> {
1038        self.cache()
1039            .positions_open_refs(venue, instrument_id, strategy_id, account_id, side)
1040            .into_iter()
1041            .map(|position| position.cloned())
1042            .collect()
1043    }
1044
1045    /// Returns owned copies of all closed positions matching the optional filter parameters.
1046    ///
1047    /// # Panics
1048    ///
1049    /// Panics if the cache is already mutably borrowed.
1050    #[must_use]
1051    pub fn positions_closed(
1052        &self,
1053        venue: Option<&Venue>,
1054        instrument_id: Option<&InstrumentId>,
1055        strategy_id: Option<&StrategyId>,
1056        account_id: Option<&AccountId>,
1057        side: Option<PositionSide>,
1058    ) -> Vec<Position> {
1059        self.cache()
1060            .positions_closed_refs(venue, instrument_id, strategy_id, account_id, side)
1061            .into_iter()
1062            .map(|position| position.cloned())
1063            .collect()
1064    }
1065
1066    /// Returns whether a position with the `position_id` exists.
1067    ///
1068    /// # Panics
1069    ///
1070    /// Panics if the cache is already mutably borrowed.
1071    #[must_use]
1072    pub fn position_exists(&self, position_id: &PositionId) -> bool {
1073        self.cache().position_exists(position_id)
1074    }
1075
1076    /// Returns whether a position with the `position_id` is open.
1077    ///
1078    /// # Panics
1079    ///
1080    /// Panics if the cache is already mutably borrowed.
1081    #[must_use]
1082    pub fn is_position_open(&self, position_id: &PositionId) -> bool {
1083        self.cache().is_position_open(position_id)
1084    }
1085
1086    /// Returns whether a position with the `position_id` is closed.
1087    ///
1088    /// # Panics
1089    ///
1090    /// Panics if the cache is already mutably borrowed.
1091    #[must_use]
1092    pub fn is_position_closed(&self, position_id: &PositionId) -> bool {
1093        self.cache().is_position_closed(position_id)
1094    }
1095
1096    /// Returns the count of all open positions matching the optional filter parameters.
1097    ///
1098    /// # Panics
1099    ///
1100    /// Panics if the cache is already mutably borrowed.
1101    #[must_use]
1102    pub fn positions_open_count(
1103        &self,
1104        venue: Option<&Venue>,
1105        instrument_id: Option<&InstrumentId>,
1106        strategy_id: Option<&StrategyId>,
1107        account_id: Option<&AccountId>,
1108        side: Option<PositionSide>,
1109    ) -> usize {
1110        self.cache()
1111            .positions_open_count(venue, instrument_id, strategy_id, account_id, side)
1112    }
1113
1114    /// Returns the count of all closed positions matching the optional filter parameters.
1115    ///
1116    /// # Panics
1117    ///
1118    /// Panics if the cache is already mutably borrowed.
1119    #[must_use]
1120    pub fn positions_closed_count(
1121        &self,
1122        venue: Option<&Venue>,
1123        instrument_id: Option<&InstrumentId>,
1124        strategy_id: Option<&StrategyId>,
1125        account_id: Option<&AccountId>,
1126        side: Option<PositionSide>,
1127    ) -> usize {
1128        self.cache()
1129            .positions_closed_count(venue, instrument_id, strategy_id, account_id, side)
1130    }
1131
1132    /// Returns the count of all positions matching the optional filter parameters.
1133    ///
1134    /// # Panics
1135    ///
1136    /// Panics if the cache is already mutably borrowed.
1137    #[must_use]
1138    pub fn positions_total_count(
1139        &self,
1140        venue: Option<&Venue>,
1141        instrument_id: Option<&InstrumentId>,
1142        strategy_id: Option<&StrategyId>,
1143        account_id: Option<&AccountId>,
1144        side: Option<PositionSide>,
1145    ) -> usize {
1146        self.cache()
1147            .positions_total_count(venue, instrument_id, strategy_id, account_id, side)
1148    }
1149
1150    /// Returns whether any open position matches the optional filter parameters.
1151    ///
1152    /// # Panics
1153    ///
1154    /// Panics if the cache is already mutably borrowed.
1155    #[must_use]
1156    pub fn has_positions_open(
1157        &self,
1158        venue: Option<&Venue>,
1159        instrument_id: Option<&InstrumentId>,
1160        strategy_id: Option<&StrategyId>,
1161        account_id: Option<&AccountId>,
1162        side: Option<PositionSide>,
1163    ) -> bool {
1164        self.cache()
1165            .has_positions_open(venue, instrument_id, strategy_id, account_id, side)
1166    }
1167
1168    /// Returns whether any closed position matches the optional filter parameters.
1169    ///
1170    /// # Panics
1171    ///
1172    /// Panics if the cache is already mutably borrowed.
1173    #[must_use]
1174    pub fn has_positions_closed(
1175        &self,
1176        venue: Option<&Venue>,
1177        instrument_id: Option<&InstrumentId>,
1178        strategy_id: Option<&StrategyId>,
1179        account_id: Option<&AccountId>,
1180        side: Option<PositionSide>,
1181    ) -> bool {
1182        self.cache()
1183            .has_positions_closed(venue, instrument_id, strategy_id, account_id, side)
1184    }
1185
1186    /// Returns whether any position matches the optional filter parameters.
1187    ///
1188    /// # Panics
1189    ///
1190    /// Panics if the cache is already mutably borrowed.
1191    #[must_use]
1192    pub fn has_positions(
1193        &self,
1194        venue: Option<&Venue>,
1195        instrument_id: Option<&InstrumentId>,
1196        strategy_id: Option<&StrategyId>,
1197        account_id: Option<&AccountId>,
1198        side: Option<PositionSide>,
1199    ) -> bool {
1200        self.cache()
1201            .has_positions(venue, instrument_id, strategy_id, account_id, side)
1202    }
1203
1204    /// Returns the strategy ID for the `client_order_id` (if found).
1205    ///
1206    /// # Panics
1207    ///
1208    /// Panics if the cache is already mutably borrowed.
1209    #[must_use]
1210    pub fn strategy_id_for_order(&self, client_order_id: &ClientOrderId) -> Option<StrategyId> {
1211        self.cache().strategy_id_for_order(client_order_id).copied()
1212    }
1213
1214    /// Returns the strategy ID for the `position_id` (if found).
1215    ///
1216    /// # Panics
1217    ///
1218    /// Panics if the cache is already mutably borrowed.
1219    #[must_use]
1220    pub fn strategy_id_for_position(&self, position_id: &PositionId) -> Option<StrategyId> {
1221        self.cache().strategy_id_for_position(position_id).copied()
1222    }
1223
1224    /// Returns the general cache value for the `key` (if found).
1225    ///
1226    /// # Errors
1227    ///
1228    /// Returns an error if:
1229    /// - The `key` is invalid.
1230    /// - The cache is already mutably borrowed ([`ComponentAccessError`]).
1231    pub fn get(&self, key: &str) -> anyhow::Result<Option<Bytes>> {
1232        let cache = self.try_cache("get")?;
1233        let value = cache.get(key)?;
1234        Ok(value.cloned())
1235    }
1236
1237    /// Returns the price for the `instrument_id` and `price_type` (if found).
1238    ///
1239    /// # Panics
1240    ///
1241    /// Panics if the cache is already mutably borrowed, or if `price_type` is [`PriceType::Mid`]
1242    /// and the quote price precision is already at the maximum fixed precision.
1243    #[must_use]
1244    pub fn price(&self, instrument_id: &InstrumentId, price_type: PriceType) -> Option<Price> {
1245        self.cache().price(instrument_id, price_type)
1246    }
1247
1248    /// Returns all quotes for the `instrument_id` (if found).
1249    ///
1250    /// # Panics
1251    ///
1252    /// Panics if the cache is already mutably borrowed.
1253    #[must_use]
1254    pub fn quotes(&self, instrument_id: &InstrumentId) -> Option<Vec<QuoteTick>> {
1255        self.cache().quotes(instrument_id)
1256    }
1257
1258    /// Returns all trades for the `instrument_id` (if found).
1259    ///
1260    /// # Panics
1261    ///
1262    /// Panics if the cache is already mutably borrowed.
1263    #[must_use]
1264    pub fn trades(&self, instrument_id: &InstrumentId) -> Option<Vec<TradeTick>> {
1265        self.cache().trades(instrument_id)
1266    }
1267
1268    /// Returns all mark price updates for the `instrument_id` (if found).
1269    ///
1270    /// # Panics
1271    ///
1272    /// Panics if the cache is already mutably borrowed.
1273    #[must_use]
1274    pub fn mark_prices(&self, instrument_id: &InstrumentId) -> Option<Vec<MarkPriceUpdate>> {
1275        self.cache().mark_prices(instrument_id)
1276    }
1277
1278    /// Returns all index price updates for the `instrument_id` (if found).
1279    ///
1280    /// # Panics
1281    ///
1282    /// Panics if the cache is already mutably borrowed.
1283    #[must_use]
1284    pub fn index_prices(&self, instrument_id: &InstrumentId) -> Option<Vec<IndexPriceUpdate>> {
1285        self.cache().index_prices(instrument_id)
1286    }
1287
1288    /// Returns all funding rate updates for the `instrument_id` (if found).
1289    ///
1290    /// # Panics
1291    ///
1292    /// Panics if the cache is already mutably borrowed.
1293    #[must_use]
1294    pub fn funding_rates(&self, instrument_id: &InstrumentId) -> Option<Vec<FundingRateUpdate>> {
1295        self.cache().funding_rates(instrument_id)
1296    }
1297
1298    /// Returns all instrument status updates for the `instrument_id` (if found).
1299    ///
1300    /// # Panics
1301    ///
1302    /// Panics if the cache is already mutably borrowed.
1303    #[must_use]
1304    pub fn instrument_statuses(
1305        &self,
1306        instrument_id: &InstrumentId,
1307    ) -> Option<Vec<InstrumentStatus>> {
1308        self.cache().instrument_statuses(instrument_id)
1309    }
1310
1311    /// Returns all bars for the `bar_type` (if found).
1312    ///
1313    /// # Panics
1314    ///
1315    /// Panics if the cache is already mutably borrowed.
1316    #[must_use]
1317    pub fn bars(&self, bar_type: &BarType) -> Option<Vec<Bar>> {
1318        self.cache().bars(bar_type)
1319    }
1320
1321    /// Returns an owned copy of the order book for the `instrument_id` (if found).
1322    ///
1323    /// # Panics
1324    ///
1325    /// Panics if the cache is already mutably borrowed.
1326    #[must_use]
1327    pub fn order_book(&self, instrument_id: &InstrumentId) -> Option<OrderBook> {
1328        self.cache().order_book(instrument_id).cloned()
1329    }
1330
1331    /// Returns an owned copy of the order book for the `instrument_id`.
1332    ///
1333    /// # Errors
1334    ///
1335    /// Returns:
1336    /// - [`OrderBookLookupError::NotFound`] when the order book is not present in the cache.
1337    /// - [`OrderBookLookupError::Access`] if the cache is already mutably borrowed.
1338    pub fn try_order_book(
1339        &self,
1340        instrument_id: &InstrumentId,
1341    ) -> Result<OrderBook, OrderBookLookupError> {
1342        self.try_cache("try_order_book")?
1343            .try_order_book(instrument_id)
1344            .cloned()
1345    }
1346
1347    /// Returns an owned copy of the own order book for the `instrument_id` (if found).
1348    ///
1349    /// # Panics
1350    ///
1351    /// Panics if the cache is already mutably borrowed.
1352    #[must_use]
1353    pub fn own_order_book(&self, instrument_id: &InstrumentId) -> Option<OwnOrderBook> {
1354        self.cache().own_order_book(instrument_id).cloned()
1355    }
1356
1357    /// Returns an owned copy of the own order book for the `instrument_id`.
1358    ///
1359    /// # Errors
1360    ///
1361    /// Returns:
1362    /// - [`OwnOrderBookLookupError::NotFound`] when the own order book is not present in the cache.
1363    /// - [`OwnOrderBookLookupError::Access`] if the cache is already mutably borrowed.
1364    pub fn try_own_order_book(
1365        &self,
1366        instrument_id: &InstrumentId,
1367    ) -> Result<OwnOrderBook, OwnOrderBookLookupError> {
1368        self.try_cache("try_own_order_book")?
1369            .try_own_order_book(instrument_id)
1370            .cloned()
1371    }
1372
1373    /// Returns the latest quote for the `instrument_id` (if found).
1374    ///
1375    /// # Panics
1376    ///
1377    /// Panics if the cache is already mutably borrowed.
1378    #[must_use]
1379    pub fn quote(&self, instrument_id: &InstrumentId) -> Option<QuoteTick> {
1380        self.cache().quote(instrument_id).copied()
1381    }
1382
1383    /// Returns the quote at `index` for the `instrument_id` (if found).
1384    ///
1385    /// Index 0 is the most recent.
1386    ///
1387    /// # Panics
1388    ///
1389    /// Panics if the cache is already mutably borrowed.
1390    #[must_use]
1391    pub fn quote_at_index(&self, instrument_id: &InstrumentId, index: usize) -> Option<QuoteTick> {
1392        self.cache().quote_at_index(instrument_id, index).copied()
1393    }
1394
1395    /// Returns the latest trade for the `instrument_id` (if found).
1396    ///
1397    /// # Panics
1398    ///
1399    /// Panics if the cache is already mutably borrowed.
1400    #[must_use]
1401    pub fn trade(&self, instrument_id: &InstrumentId) -> Option<TradeTick> {
1402        self.cache().trade(instrument_id).copied()
1403    }
1404
1405    /// Returns the trade at `index` for the `instrument_id` (if found).
1406    ///
1407    /// Index 0 is the most recent.
1408    ///
1409    /// # Panics
1410    ///
1411    /// Panics if the cache is already mutably borrowed.
1412    #[must_use]
1413    pub fn trade_at_index(&self, instrument_id: &InstrumentId, index: usize) -> Option<TradeTick> {
1414        self.cache().trade_at_index(instrument_id, index).copied()
1415    }
1416
1417    /// Returns the latest mark price update for the `instrument_id` (if found).
1418    ///
1419    /// # Panics
1420    ///
1421    /// Panics if the cache is already mutably borrowed.
1422    #[must_use]
1423    pub fn mark_price(&self, instrument_id: &InstrumentId) -> Option<MarkPriceUpdate> {
1424        self.cache().mark_price(instrument_id).copied()
1425    }
1426
1427    /// Returns the latest index price update for the `instrument_id` (if found).
1428    ///
1429    /// # Panics
1430    ///
1431    /// Panics if the cache is already mutably borrowed.
1432    #[must_use]
1433    pub fn index_price(&self, instrument_id: &InstrumentId) -> Option<IndexPriceUpdate> {
1434        self.cache().index_price(instrument_id).copied()
1435    }
1436
1437    /// Returns the latest funding rate update for the `instrument_id` (if found).
1438    ///
1439    /// # Panics
1440    ///
1441    /// Panics if the cache is already mutably borrowed.
1442    #[must_use]
1443    pub fn funding_rate(&self, instrument_id: &InstrumentId) -> Option<FundingRateUpdate> {
1444        self.cache().funding_rate(instrument_id).copied()
1445    }
1446
1447    /// Returns the latest instrument status update for the `instrument_id` (if found).
1448    ///
1449    /// # Panics
1450    ///
1451    /// Panics if the cache is already mutably borrowed.
1452    #[must_use]
1453    pub fn instrument_status(&self, instrument_id: &InstrumentId) -> Option<InstrumentStatus> {
1454        self.cache().instrument_status(instrument_id).copied()
1455    }
1456
1457    /// Returns the cached close for the `instrument_id` (if found).
1458    ///
1459    /// # Panics
1460    ///
1461    /// Panics if the cache is already mutably borrowed.
1462    #[must_use]
1463    pub fn instrument_close(&self, instrument_id: &InstrumentId) -> Option<InstrumentClose> {
1464        self.cache().instrument_close(instrument_id).copied()
1465    }
1466
1467    /// Returns the latest bar for the `bar_type` (if found).
1468    ///
1469    /// # Panics
1470    ///
1471    /// Panics if the cache is already mutably borrowed.
1472    #[must_use]
1473    pub fn bar(&self, bar_type: &BarType) -> Option<Bar> {
1474        self.cache().bar(bar_type).copied()
1475    }
1476
1477    /// Returns the bar at `index` for the `bar_type` (if found).
1478    ///
1479    /// Index 0 is the most recent.
1480    ///
1481    /// # Panics
1482    ///
1483    /// Panics if the cache is already mutably borrowed.
1484    #[must_use]
1485    pub fn bar_at_index(&self, bar_type: &BarType, index: usize) -> Option<Bar> {
1486        self.cache().bar_at_index(bar_type, index).copied()
1487    }
1488
1489    /// Returns the order book update count for the `instrument_id`.
1490    ///
1491    /// # Panics
1492    ///
1493    /// Panics if the cache is already mutably borrowed.
1494    #[must_use]
1495    pub fn book_update_count(&self, instrument_id: &InstrumentId) -> usize {
1496        self.cache().book_update_count(instrument_id)
1497    }
1498
1499    /// Returns the quote tick count for the `instrument_id`.
1500    ///
1501    /// # Panics
1502    ///
1503    /// Panics if the cache is already mutably borrowed.
1504    #[must_use]
1505    pub fn quote_count(&self, instrument_id: &InstrumentId) -> usize {
1506        self.cache().quote_count(instrument_id)
1507    }
1508
1509    /// Returns the trade tick count for the `instrument_id`.
1510    ///
1511    /// # Panics
1512    ///
1513    /// Panics if the cache is already mutably borrowed.
1514    #[must_use]
1515    pub fn trade_count(&self, instrument_id: &InstrumentId) -> usize {
1516        self.cache().trade_count(instrument_id)
1517    }
1518
1519    /// Returns the mark price update count for the `instrument_id`.
1520    ///
1521    /// # Panics
1522    ///
1523    /// Panics if the cache is already mutably borrowed.
1524    #[must_use]
1525    pub fn mark_price_count(&self, instrument_id: &InstrumentId) -> usize {
1526        self.cache().mark_price_count(instrument_id)
1527    }
1528
1529    /// Returns the index price update count for the `instrument_id`.
1530    ///
1531    /// # Panics
1532    ///
1533    /// Panics if the cache is already mutably borrowed.
1534    #[must_use]
1535    pub fn index_price_count(&self, instrument_id: &InstrumentId) -> usize {
1536        self.cache().index_price_count(instrument_id)
1537    }
1538
1539    /// Returns the funding rate update count for the `instrument_id`.
1540    ///
1541    /// # Panics
1542    ///
1543    /// Panics if the cache is already mutably borrowed.
1544    #[must_use]
1545    pub fn funding_rate_count(&self, instrument_id: &InstrumentId) -> usize {
1546        self.cache().funding_rate_count(instrument_id)
1547    }
1548
1549    /// Returns the instrument status update count for the `instrument_id`.
1550    ///
1551    /// # Panics
1552    ///
1553    /// Panics if the cache is already mutably borrowed.
1554    #[must_use]
1555    pub fn instrument_status_count(&self, instrument_id: &InstrumentId) -> usize {
1556        self.cache().instrument_status_count(instrument_id)
1557    }
1558
1559    /// Returns the bar count for the `bar_type`.
1560    ///
1561    /// # Panics
1562    ///
1563    /// Panics if the cache is already mutably borrowed.
1564    #[must_use]
1565    pub fn bar_count(&self, bar_type: &BarType) -> usize {
1566        self.cache().bar_count(bar_type)
1567    }
1568
1569    /// Returns whether the cache contains an order book for the `instrument_id`.
1570    ///
1571    /// # Panics
1572    ///
1573    /// Panics if the cache is already mutably borrowed.
1574    #[must_use]
1575    pub fn has_order_book(&self, instrument_id: &InstrumentId) -> bool {
1576        self.cache().has_order_book(instrument_id)
1577    }
1578
1579    /// Returns whether the cache contains quotes for the `instrument_id`.
1580    ///
1581    /// # Panics
1582    ///
1583    /// Panics if the cache is already mutably borrowed.
1584    #[must_use]
1585    pub fn has_quote_ticks(&self, instrument_id: &InstrumentId) -> bool {
1586        self.cache().has_quote_ticks(instrument_id)
1587    }
1588
1589    /// Returns whether the cache contains trades for the `instrument_id`.
1590    ///
1591    /// # Panics
1592    ///
1593    /// Panics if the cache is already mutably borrowed.
1594    #[must_use]
1595    pub fn has_trade_ticks(&self, instrument_id: &InstrumentId) -> bool {
1596        self.cache().has_trade_ticks(instrument_id)
1597    }
1598
1599    /// Returns whether the cache contains mark price updates for the `instrument_id`.
1600    ///
1601    /// # Panics
1602    ///
1603    /// Panics if the cache is already mutably borrowed.
1604    #[must_use]
1605    pub fn has_mark_prices(&self, instrument_id: &InstrumentId) -> bool {
1606        self.cache().has_mark_prices(instrument_id)
1607    }
1608
1609    /// Returns whether the cache contains index price updates for the `instrument_id`.
1610    ///
1611    /// # Panics
1612    ///
1613    /// Panics if the cache is already mutably borrowed.
1614    #[must_use]
1615    pub fn has_index_prices(&self, instrument_id: &InstrumentId) -> bool {
1616        self.cache().has_index_prices(instrument_id)
1617    }
1618
1619    /// Returns whether the cache contains funding rate updates for the `instrument_id`.
1620    ///
1621    /// # Panics
1622    ///
1623    /// Panics if the cache is already mutably borrowed.
1624    #[must_use]
1625    pub fn has_funding_rates(&self, instrument_id: &InstrumentId) -> bool {
1626        self.cache().has_funding_rates(instrument_id)
1627    }
1628
1629    /// Returns whether the cache contains instrument status updates for the `instrument_id`.
1630    ///
1631    /// # Panics
1632    ///
1633    /// Panics if the cache is already mutably borrowed.
1634    #[must_use]
1635    pub fn has_instrument_statuses(&self, instrument_id: &InstrumentId) -> bool {
1636        self.cache().has_instrument_statuses(instrument_id)
1637    }
1638
1639    /// Returns whether the cache contains a close for the `instrument_id`.
1640    ///
1641    /// # Panics
1642    ///
1643    /// Panics if the cache is already mutably borrowed.
1644    #[must_use]
1645    pub fn has_instrument_close(&self, instrument_id: &InstrumentId) -> bool {
1646        self.cache().has_instrument_close(instrument_id)
1647    }
1648
1649    /// Returns whether the cache contains bars for the `bar_type`.
1650    ///
1651    /// # Panics
1652    ///
1653    /// Panics if the cache is already mutably borrowed.
1654    #[must_use]
1655    pub fn has_bars(&self, bar_type: &BarType) -> bool {
1656        self.cache().has_bars(bar_type)
1657    }
1658
1659    /// Returns the exchange rate for the given currencies and price type (if available).
1660    ///
1661    /// # Panics
1662    ///
1663    /// Panics if the cache is already mutably borrowed.
1664    #[must_use]
1665    pub fn get_xrate(
1666        &self,
1667        venue: Venue,
1668        from_currency: Currency,
1669        to_currency: Currency,
1670        price_type: PriceType,
1671    ) -> Option<Decimal> {
1672        self.cache()
1673            .get_xrate(venue, from_currency, to_currency, price_type)
1674    }
1675
1676    /// Returns the mark exchange rate for the currency pair (if set).
1677    ///
1678    /// # Panics
1679    ///
1680    /// Panics if the cache is already mutably borrowed.
1681    #[must_use]
1682    pub fn get_mark_xrate(&self, from_currency: Currency, to_currency: Currency) -> Option<f64> {
1683        self.cache().get_mark_xrate(from_currency, to_currency)
1684    }
1685
1686    /// Returns the yield curve for the `key` (if found).
1687    ///
1688    /// # Panics
1689    ///
1690    /// Panics if the cache is already mutably borrowed.
1691    #[must_use]
1692    pub fn yield_curve(&self, key: &str) -> Option<Box<dyn Fn(f64) -> f64>> {
1693        self.cache().yield_curve(key)
1694    }
1695
1696    /// Returns an owned copy of the greeks data for the `instrument_id` (if found).
1697    ///
1698    /// # Panics
1699    ///
1700    /// Panics if the cache is already mutably borrowed.
1701    #[must_use]
1702    pub fn greeks(&self, instrument_id: &InstrumentId) -> Option<GreeksData> {
1703        self.cache().greeks(instrument_id)
1704    }
1705
1706    /// Returns exchange-provided option greeks for the `instrument_id` (if found).
1707    ///
1708    /// # Panics
1709    ///
1710    /// Panics if the cache is already mutably borrowed.
1711    #[must_use]
1712    pub fn option_greeks(&self, instrument_id: &InstrumentId) -> Option<OptionGreeks> {
1713        self.cache().option_greeks(instrument_id).copied()
1714    }
1715
1716    /// Returns the currency for the `code` (if found).
1717    ///
1718    /// # Panics
1719    ///
1720    /// Panics if the cache is already mutably borrowed.
1721    #[must_use]
1722    pub fn currency(&self, code: &Ustr) -> Option<Currency> {
1723        self.cache().currency(code).copied()
1724    }
1725
1726    /// Returns the currency for the `code`.
1727    ///
1728    /// # Errors
1729    ///
1730    /// Returns:
1731    /// - [`CurrencyLookupError::NotFound`] when the currency is not present in the cache.
1732    /// - [`CurrencyLookupError::Access`] if the cache is already mutably borrowed.
1733    pub fn try_currency(&self, code: &Ustr) -> Result<Currency, CurrencyLookupError> {
1734        self.try_cache("try_currency")?.try_currency(code).copied()
1735    }
1736
1737    /// Returns an owned copy of the instrument for the `instrument_id` (if found).
1738    ///
1739    /// # Panics
1740    ///
1741    /// Panics if the cache is already mutably borrowed.
1742    #[must_use]
1743    pub fn instrument(&self, instrument_id: &InstrumentId) -> Option<InstrumentAny> {
1744        self.cache().instrument(instrument_id).cloned()
1745    }
1746
1747    /// Returns an owned copy of the instrument for the `instrument_id`.
1748    ///
1749    /// # Errors
1750    ///
1751    /// Returns:
1752    /// - [`InstrumentLookupError::NotFound`] when the instrument is not present in the cache.
1753    /// - [`InstrumentLookupError::Access`] if the cache is already mutably borrowed.
1754    pub fn try_instrument(
1755        &self,
1756        instrument_id: &InstrumentId,
1757    ) -> Result<InstrumentAny, InstrumentLookupError> {
1758        self.try_cache("try_instrument")?
1759            .try_instrument(instrument_id)
1760            .cloned()
1761    }
1762
1763    /// Returns the instrument IDs in the cache, optionally filtered by `venue`.
1764    ///
1765    /// # Panics
1766    ///
1767    /// Panics if the cache is already mutably borrowed.
1768    #[must_use]
1769    pub fn instrument_ids(&self, venue: Option<&Venue>) -> Vec<InstrumentId> {
1770        self.cache()
1771            .instrument_ids(venue)
1772            .into_iter()
1773            .copied()
1774            .collect()
1775    }
1776
1777    /// Returns owned copies of all instruments for the `venue`.
1778    ///
1779    /// # Panics
1780    ///
1781    /// Panics if the cache is already mutably borrowed.
1782    #[must_use]
1783    pub fn instruments(&self, venue: &Venue, underlying: Option<&Ustr>) -> Vec<InstrumentAny> {
1784        self.cache()
1785            .instruments(venue, underlying)
1786            .into_iter()
1787            .cloned()
1788            .collect()
1789    }
1790
1791    /// Returns owned copies of all instruments for the `venue`, parent `root`, and instrument
1792    /// `class`.
1793    ///
1794    /// # Panics
1795    ///
1796    /// Panics if the cache is already mutably borrowed.
1797    #[must_use]
1798    pub fn instruments_by_parent(
1799        &self,
1800        venue: &Venue,
1801        root: &Ustr,
1802        class: InstrumentClass,
1803    ) -> Vec<InstrumentAny> {
1804        self.cache()
1805            .instruments_by_parent(venue, root, class)
1806            .into_iter()
1807            .cloned()
1808            .collect()
1809    }
1810
1811    /// Returns the bar types in the cache, optionally filtered by instrument and price type.
1812    ///
1813    /// # Panics
1814    ///
1815    /// Panics if the cache is already mutably borrowed.
1816    #[must_use]
1817    pub fn bar_types(
1818        &self,
1819        instrument_id: Option<&InstrumentId>,
1820        price_type: Option<&PriceType>,
1821        aggregation_source: AggregationSource,
1822    ) -> Vec<BarType> {
1823        self.cache()
1824            .bar_types(instrument_id, price_type, aggregation_source)
1825            .into_iter()
1826            .copied()
1827            .collect()
1828    }
1829
1830    /// Returns an owned copy of the synthetic instrument for the `instrument_id` (if found).
1831    ///
1832    /// # Panics
1833    ///
1834    /// Panics if the cache is already mutably borrowed.
1835    #[must_use]
1836    pub fn synthetic(&self, instrument_id: &InstrumentId) -> Option<SyntheticInstrument> {
1837        self.cache().synthetic(instrument_id).cloned()
1838    }
1839
1840    /// Returns an owned copy of the synthetic instrument for the `instrument_id`.
1841    ///
1842    /// # Errors
1843    ///
1844    /// Returns:
1845    /// - [`SyntheticInstrumentLookupError::NotFound`] when the synthetic instrument is not present
1846    ///   in the cache.
1847    /// - [`SyntheticInstrumentLookupError::Access`] if the cache is already mutably borrowed.
1848    pub fn try_synthetic(
1849        &self,
1850        instrument_id: &InstrumentId,
1851    ) -> Result<SyntheticInstrument, SyntheticInstrumentLookupError> {
1852        self.try_cache("try_synthetic")?
1853            .try_synthetic(instrument_id)
1854            .cloned()
1855    }
1856
1857    /// Returns the synthetic instrument IDs in the cache.
1858    ///
1859    /// # Panics
1860    ///
1861    /// Panics if the cache is already mutably borrowed.
1862    #[must_use]
1863    pub fn synthetic_ids(&self) -> Vec<InstrumentId> {
1864        self.cache().synthetic_ids().into_iter().copied().collect()
1865    }
1866
1867    /// Returns owned copies of all synthetic instruments in the cache.
1868    ///
1869    /// # Panics
1870    ///
1871    /// Panics if the cache is already mutably borrowed.
1872    #[must_use]
1873    pub fn synthetics(&self) -> Vec<SyntheticInstrument> {
1874        self.cache().synthetics().into_iter().cloned().collect()
1875    }
1876
1877    /// Returns an owned copy of the pool for the `instrument_id` (if found).
1878    ///
1879    /// # Panics
1880    ///
1881    /// Panics if the cache is already mutably borrowed.
1882    #[cfg(feature = "defi")]
1883    #[must_use]
1884    pub fn pool(&self, instrument_id: &InstrumentId) -> Option<Pool> {
1885        self.cache().pool(instrument_id).cloned()
1886    }
1887
1888    /// Returns the pool instrument IDs in the cache, optionally filtered by `venue`.
1889    ///
1890    /// # Panics
1891    ///
1892    /// Panics if the cache is already mutably borrowed.
1893    #[cfg(feature = "defi")]
1894    #[must_use]
1895    pub fn pool_ids(&self, venue: Option<&Venue>) -> Vec<InstrumentId> {
1896        self.cache().pool_ids(venue)
1897    }
1898
1899    /// Returns owned copies of all pools in the cache, optionally filtered by `venue`.
1900    ///
1901    /// # Panics
1902    ///
1903    /// Panics if the cache is already mutably borrowed.
1904    #[cfg(feature = "defi")]
1905    #[must_use]
1906    pub fn pools(&self, venue: Option<&Venue>) -> Vec<Pool> {
1907        self.cache().pools(venue).into_iter().cloned().collect()
1908    }
1909
1910    /// Returns an owned copy of the pool profiler for the `instrument_id` (if found).
1911    ///
1912    /// # Panics
1913    ///
1914    /// Panics if the cache is already mutably borrowed.
1915    #[cfg(feature = "defi")]
1916    #[must_use]
1917    pub fn pool_profiler(&self, instrument_id: &InstrumentId) -> Option<PoolProfiler> {
1918        self.cache().pool_profiler(instrument_id).cloned()
1919    }
1920
1921    /// Returns the pool profiler instrument IDs in the cache, optionally filtered by `venue`.
1922    ///
1923    /// # Panics
1924    ///
1925    /// Panics if the cache is already mutably borrowed.
1926    #[cfg(feature = "defi")]
1927    #[must_use]
1928    pub fn pool_profiler_ids(&self, venue: Option<&Venue>) -> Vec<InstrumentId> {
1929        self.cache().pool_profiler_ids(venue)
1930    }
1931
1932    /// Returns owned copies of all pool profilers in the cache, optionally filtered by `venue`.
1933    ///
1934    /// # Panics
1935    ///
1936    /// Panics if the cache is already mutably borrowed.
1937    #[cfg(feature = "defi")]
1938    #[must_use]
1939    pub fn pool_profilers(&self, venue: Option<&Venue>) -> Vec<PoolProfiler> {
1940        self.cache()
1941            .pool_profilers(venue)
1942            .into_iter()
1943            .cloned()
1944            .collect()
1945    }
1946
1947    /// Returns an owned copy of the account for the `account_id` (if found).
1948    ///
1949    /// # Panics
1950    ///
1951    /// Panics if the cache is already mutably borrowed.
1952    #[must_use]
1953    pub fn account(&self, account_id: &AccountId) -> Option<AccountAny> {
1954        self.cache().account_owned(account_id)
1955    }
1956
1957    /// Returns an owned copy of the account for the `account_id`.
1958    ///
1959    /// # Errors
1960    ///
1961    /// Returns:
1962    /// - [`AccountLookupError::NotFound`] when the account is not present in the cache.
1963    /// - [`AccountLookupError::Access`] if the cache is already mutably borrowed.
1964    pub fn try_account(&self, account_id: &AccountId) -> Result<AccountAny, AccountLookupError> {
1965        self.try_cache("try_account")?
1966            .try_account(account_id)
1967            .map(|account| account.cloned())
1968    }
1969
1970    /// Returns an owned copy of the account for the `venue` (if found).
1971    ///
1972    /// # Panics
1973    ///
1974    /// Panics if the cache is already mutably borrowed.
1975    #[must_use]
1976    pub fn account_for_venue(&self, venue: &Venue) -> Option<AccountAny> {
1977        self.cache().account_for_venue_owned(venue)
1978    }
1979
1980    /// Returns the account ID for the `venue` (if found).
1981    ///
1982    /// # Panics
1983    ///
1984    /// Panics if the cache is already mutably borrowed.
1985    #[must_use]
1986    pub fn account_id(&self, venue: &Venue) -> Option<AccountId> {
1987        self.cache().account_id(venue).copied()
1988    }
1989
1990    /// Returns owned copies of all accounts matching the `account_id`.
1991    ///
1992    /// # Panics
1993    ///
1994    /// Panics if the cache is already mutably borrowed.
1995    #[must_use]
1996    pub fn accounts(&self, account_id: &AccountId) -> Vec<AccountAny> {
1997        self.cache()
1998            .accounts(account_id)
1999            .into_iter()
2000            .map(|account| account.cloned())
2001            .collect()
2002    }
2003
2004    /// Returns owned copies of every account in the cache.
2005    ///
2006    /// # Panics
2007    ///
2008    /// Panics if the cache is already mutably borrowed.
2009    #[must_use]
2010    pub fn accounts_all(&self) -> Vec<AccountAny> {
2011        self.cache().accounts_all_owned()
2012    }
2013
2014    fn cache(&self) -> Ref<'_, Cache> {
2015        self.try_cache("cache read")
2016            .unwrap_or_else(|e| panic!("{e}"))
2017    }
2018
2019    fn try_cache(&self, operation: &'static str) -> Result<Ref<'_, Cache>, ComponentAccessError> {
2020        self.cache
2021            .try_borrow()
2022            .map_err(|_| ComponentAccessError::ReadConflict {
2023                resource: "cache",
2024                operation,
2025            })
2026    }
2027}