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 an error if:
335    /// - The order is not present in the cache.
336    /// - 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 an error if:
827    /// - The order list is not present in the cache.
828    /// - 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 an error if:
973    /// - The position is not present in the cache.
974    /// - 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.
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    /// For `Mid`, returns `None` if no quote is cached or either price is a sentinel.
1240    /// For quote precision `p`, the midpoint has precision `p + 1` when exactly representable,
1241    /// otherwise `p`, rounded half-even if necessary. The fallback applies when the precision
1242    /// limit or raw range rules out `p + 1`, and when `p` is the maximum float precision (16),
1243    /// so a midpoint of a float-convertible quote stays float-convertible.
1244    ///
1245    /// # Panics
1246    ///
1247    /// Panics if the cache is already mutably borrowed.
1248    #[must_use]
1249    pub fn price(&self, instrument_id: &InstrumentId, price_type: PriceType) -> Option<Price> {
1250        self.cache().price(instrument_id, price_type)
1251    }
1252
1253    /// Returns all quotes for the `instrument_id` (if found).
1254    ///
1255    /// # Panics
1256    ///
1257    /// Panics if the cache is already mutably borrowed.
1258    #[must_use]
1259    pub fn quotes(&self, instrument_id: &InstrumentId) -> Option<Vec<QuoteTick>> {
1260        self.cache().quotes(instrument_id)
1261    }
1262
1263    /// Returns all trades for the `instrument_id` (if found).
1264    ///
1265    /// # Panics
1266    ///
1267    /// Panics if the cache is already mutably borrowed.
1268    #[must_use]
1269    pub fn trades(&self, instrument_id: &InstrumentId) -> Option<Vec<TradeTick>> {
1270        self.cache().trades(instrument_id)
1271    }
1272
1273    /// Returns all mark price updates for the `instrument_id` (if found).
1274    ///
1275    /// # Panics
1276    ///
1277    /// Panics if the cache is already mutably borrowed.
1278    #[must_use]
1279    pub fn mark_prices(&self, instrument_id: &InstrumentId) -> Option<Vec<MarkPriceUpdate>> {
1280        self.cache().mark_prices(instrument_id)
1281    }
1282
1283    /// Returns all index price updates for the `instrument_id` (if found).
1284    ///
1285    /// # Panics
1286    ///
1287    /// Panics if the cache is already mutably borrowed.
1288    #[must_use]
1289    pub fn index_prices(&self, instrument_id: &InstrumentId) -> Option<Vec<IndexPriceUpdate>> {
1290        self.cache().index_prices(instrument_id)
1291    }
1292
1293    /// Returns all funding rate updates for the `instrument_id` (if found).
1294    ///
1295    /// # Panics
1296    ///
1297    /// Panics if the cache is already mutably borrowed.
1298    #[must_use]
1299    pub fn funding_rates(&self, instrument_id: &InstrumentId) -> Option<Vec<FundingRateUpdate>> {
1300        self.cache().funding_rates(instrument_id)
1301    }
1302
1303    /// Returns all instrument status updates for the `instrument_id` (if found).
1304    ///
1305    /// # Panics
1306    ///
1307    /// Panics if the cache is already mutably borrowed.
1308    #[must_use]
1309    pub fn instrument_statuses(
1310        &self,
1311        instrument_id: &InstrumentId,
1312    ) -> Option<Vec<InstrumentStatus>> {
1313        self.cache().instrument_statuses(instrument_id)
1314    }
1315
1316    /// Returns all bars for the `bar_type` (if found).
1317    ///
1318    /// # Panics
1319    ///
1320    /// Panics if the cache is already mutably borrowed.
1321    #[must_use]
1322    pub fn bars(&self, bar_type: &BarType) -> Option<Vec<Bar>> {
1323        self.cache().bars(bar_type)
1324    }
1325
1326    /// Returns an owned copy of the order book for the `instrument_id` (if found).
1327    ///
1328    /// # Panics
1329    ///
1330    /// Panics if the cache is already mutably borrowed.
1331    #[must_use]
1332    pub fn order_book(&self, instrument_id: &InstrumentId) -> Option<OrderBook> {
1333        self.cache().order_book(instrument_id).cloned()
1334    }
1335
1336    /// Returns an owned copy of the order book for the `instrument_id`.
1337    ///
1338    /// # Errors
1339    ///
1340    /// Returns an error if:
1341    /// - The order book is not present in the cache.
1342    /// - The cache is already mutably borrowed.
1343    pub fn try_order_book(
1344        &self,
1345        instrument_id: &InstrumentId,
1346    ) -> Result<OrderBook, OrderBookLookupError> {
1347        self.try_cache("try_order_book")?
1348            .try_order_book(instrument_id)
1349            .cloned()
1350    }
1351
1352    /// Returns an owned copy of the own order book for the `instrument_id` (if found).
1353    ///
1354    /// # Panics
1355    ///
1356    /// Panics if the cache is already mutably borrowed.
1357    #[must_use]
1358    pub fn own_order_book(&self, instrument_id: &InstrumentId) -> Option<OwnOrderBook> {
1359        self.cache().own_order_book(instrument_id).cloned()
1360    }
1361
1362    /// Returns an owned copy of the own order book for the `instrument_id`.
1363    ///
1364    /// # Errors
1365    ///
1366    /// Returns an error if:
1367    /// - The own order book is not present in the cache.
1368    /// - The cache is already mutably borrowed.
1369    pub fn try_own_order_book(
1370        &self,
1371        instrument_id: &InstrumentId,
1372    ) -> Result<OwnOrderBook, OwnOrderBookLookupError> {
1373        self.try_cache("try_own_order_book")?
1374            .try_own_order_book(instrument_id)
1375            .cloned()
1376    }
1377
1378    /// Returns the latest quote for the `instrument_id` (if found).
1379    ///
1380    /// # Panics
1381    ///
1382    /// Panics if the cache is already mutably borrowed.
1383    #[must_use]
1384    pub fn quote(&self, instrument_id: &InstrumentId) -> Option<QuoteTick> {
1385        self.cache().quote(instrument_id).copied()
1386    }
1387
1388    /// Returns the quote at `index` for the `instrument_id` (if found).
1389    ///
1390    /// Index 0 is the most recent.
1391    ///
1392    /// # Panics
1393    ///
1394    /// Panics if the cache is already mutably borrowed.
1395    #[must_use]
1396    pub fn quote_at_index(&self, instrument_id: &InstrumentId, index: usize) -> Option<QuoteTick> {
1397        self.cache().quote_at_index(instrument_id, index).copied()
1398    }
1399
1400    /// Returns the latest trade for the `instrument_id` (if found).
1401    ///
1402    /// # Panics
1403    ///
1404    /// Panics if the cache is already mutably borrowed.
1405    #[must_use]
1406    pub fn trade(&self, instrument_id: &InstrumentId) -> Option<TradeTick> {
1407        self.cache().trade(instrument_id).copied()
1408    }
1409
1410    /// Returns the trade at `index` for the `instrument_id` (if found).
1411    ///
1412    /// Index 0 is the most recent.
1413    ///
1414    /// # Panics
1415    ///
1416    /// Panics if the cache is already mutably borrowed.
1417    #[must_use]
1418    pub fn trade_at_index(&self, instrument_id: &InstrumentId, index: usize) -> Option<TradeTick> {
1419        self.cache().trade_at_index(instrument_id, index).copied()
1420    }
1421
1422    /// Returns the latest mark price update for the `instrument_id` (if found).
1423    ///
1424    /// # Panics
1425    ///
1426    /// Panics if the cache is already mutably borrowed.
1427    #[must_use]
1428    pub fn mark_price(&self, instrument_id: &InstrumentId) -> Option<MarkPriceUpdate> {
1429        self.cache().mark_price(instrument_id).copied()
1430    }
1431
1432    /// Returns the latest index price update for the `instrument_id` (if found).
1433    ///
1434    /// # Panics
1435    ///
1436    /// Panics if the cache is already mutably borrowed.
1437    #[must_use]
1438    pub fn index_price(&self, instrument_id: &InstrumentId) -> Option<IndexPriceUpdate> {
1439        self.cache().index_price(instrument_id).copied()
1440    }
1441
1442    /// Returns the latest funding rate update for the `instrument_id` (if found).
1443    ///
1444    /// # Panics
1445    ///
1446    /// Panics if the cache is already mutably borrowed.
1447    #[must_use]
1448    pub fn funding_rate(&self, instrument_id: &InstrumentId) -> Option<FundingRateUpdate> {
1449        self.cache().funding_rate(instrument_id).copied()
1450    }
1451
1452    /// Returns the latest instrument status update for the `instrument_id` (if found).
1453    ///
1454    /// # Panics
1455    ///
1456    /// Panics if the cache is already mutably borrowed.
1457    #[must_use]
1458    pub fn instrument_status(&self, instrument_id: &InstrumentId) -> Option<InstrumentStatus> {
1459        self.cache().instrument_status(instrument_id).copied()
1460    }
1461
1462    /// Returns the cached close for the `instrument_id` (if found).
1463    ///
1464    /// # Panics
1465    ///
1466    /// Panics if the cache is already mutably borrowed.
1467    #[must_use]
1468    pub fn instrument_close(&self, instrument_id: &InstrumentId) -> Option<InstrumentClose> {
1469        self.cache().instrument_close(instrument_id).copied()
1470    }
1471
1472    /// Returns the latest bar for the `bar_type` (if found).
1473    ///
1474    /// # Panics
1475    ///
1476    /// Panics if the cache is already mutably borrowed.
1477    #[must_use]
1478    pub fn bar(&self, bar_type: &BarType) -> Option<Bar> {
1479        self.cache().bar(bar_type).copied()
1480    }
1481
1482    /// Returns the bar at `index` for the `bar_type` (if found).
1483    ///
1484    /// Index 0 is the most recent.
1485    ///
1486    /// # Panics
1487    ///
1488    /// Panics if the cache is already mutably borrowed.
1489    #[must_use]
1490    pub fn bar_at_index(&self, bar_type: &BarType, index: usize) -> Option<Bar> {
1491        self.cache().bar_at_index(bar_type, index).copied()
1492    }
1493
1494    /// Returns the order book update count for the `instrument_id`.
1495    ///
1496    /// # Panics
1497    ///
1498    /// Panics if the cache is already mutably borrowed.
1499    #[must_use]
1500    pub fn book_update_count(&self, instrument_id: &InstrumentId) -> usize {
1501        self.cache().book_update_count(instrument_id)
1502    }
1503
1504    /// Returns the quote tick count for the `instrument_id`.
1505    ///
1506    /// # Panics
1507    ///
1508    /// Panics if the cache is already mutably borrowed.
1509    #[must_use]
1510    pub fn quote_count(&self, instrument_id: &InstrumentId) -> usize {
1511        self.cache().quote_count(instrument_id)
1512    }
1513
1514    /// Returns the trade tick count for the `instrument_id`.
1515    ///
1516    /// # Panics
1517    ///
1518    /// Panics if the cache is already mutably borrowed.
1519    #[must_use]
1520    pub fn trade_count(&self, instrument_id: &InstrumentId) -> usize {
1521        self.cache().trade_count(instrument_id)
1522    }
1523
1524    /// Returns the mark price update count for the `instrument_id`.
1525    ///
1526    /// # Panics
1527    ///
1528    /// Panics if the cache is already mutably borrowed.
1529    #[must_use]
1530    pub fn mark_price_count(&self, instrument_id: &InstrumentId) -> usize {
1531        self.cache().mark_price_count(instrument_id)
1532    }
1533
1534    /// Returns the index price update count for the `instrument_id`.
1535    ///
1536    /// # Panics
1537    ///
1538    /// Panics if the cache is already mutably borrowed.
1539    #[must_use]
1540    pub fn index_price_count(&self, instrument_id: &InstrumentId) -> usize {
1541        self.cache().index_price_count(instrument_id)
1542    }
1543
1544    /// Returns the funding rate update count for the `instrument_id`.
1545    ///
1546    /// # Panics
1547    ///
1548    /// Panics if the cache is already mutably borrowed.
1549    #[must_use]
1550    pub fn funding_rate_count(&self, instrument_id: &InstrumentId) -> usize {
1551        self.cache().funding_rate_count(instrument_id)
1552    }
1553
1554    /// Returns the instrument status update count for the `instrument_id`.
1555    ///
1556    /// # Panics
1557    ///
1558    /// Panics if the cache is already mutably borrowed.
1559    #[must_use]
1560    pub fn instrument_status_count(&self, instrument_id: &InstrumentId) -> usize {
1561        self.cache().instrument_status_count(instrument_id)
1562    }
1563
1564    /// Returns the bar count for the `bar_type`.
1565    ///
1566    /// # Panics
1567    ///
1568    /// Panics if the cache is already mutably borrowed.
1569    #[must_use]
1570    pub fn bar_count(&self, bar_type: &BarType) -> usize {
1571        self.cache().bar_count(bar_type)
1572    }
1573
1574    /// Returns whether the cache contains an order book for the `instrument_id`.
1575    ///
1576    /// # Panics
1577    ///
1578    /// Panics if the cache is already mutably borrowed.
1579    #[must_use]
1580    pub fn has_order_book(&self, instrument_id: &InstrumentId) -> bool {
1581        self.cache().has_order_book(instrument_id)
1582    }
1583
1584    /// Returns whether the cache contains quotes for the `instrument_id`.
1585    ///
1586    /// # Panics
1587    ///
1588    /// Panics if the cache is already mutably borrowed.
1589    #[must_use]
1590    pub fn has_quote_ticks(&self, instrument_id: &InstrumentId) -> bool {
1591        self.cache().has_quote_ticks(instrument_id)
1592    }
1593
1594    /// Returns whether the cache contains trades for the `instrument_id`.
1595    ///
1596    /// # Panics
1597    ///
1598    /// Panics if the cache is already mutably borrowed.
1599    #[must_use]
1600    pub fn has_trade_ticks(&self, instrument_id: &InstrumentId) -> bool {
1601        self.cache().has_trade_ticks(instrument_id)
1602    }
1603
1604    /// Returns whether the cache contains mark price updates for the `instrument_id`.
1605    ///
1606    /// # Panics
1607    ///
1608    /// Panics if the cache is already mutably borrowed.
1609    #[must_use]
1610    pub fn has_mark_prices(&self, instrument_id: &InstrumentId) -> bool {
1611        self.cache().has_mark_prices(instrument_id)
1612    }
1613
1614    /// Returns whether the cache contains index price updates for the `instrument_id`.
1615    ///
1616    /// # Panics
1617    ///
1618    /// Panics if the cache is already mutably borrowed.
1619    #[must_use]
1620    pub fn has_index_prices(&self, instrument_id: &InstrumentId) -> bool {
1621        self.cache().has_index_prices(instrument_id)
1622    }
1623
1624    /// Returns whether the cache contains funding rate updates for the `instrument_id`.
1625    ///
1626    /// # Panics
1627    ///
1628    /// Panics if the cache is already mutably borrowed.
1629    #[must_use]
1630    pub fn has_funding_rates(&self, instrument_id: &InstrumentId) -> bool {
1631        self.cache().has_funding_rates(instrument_id)
1632    }
1633
1634    /// Returns whether the cache contains instrument status updates for the `instrument_id`.
1635    ///
1636    /// # Panics
1637    ///
1638    /// Panics if the cache is already mutably borrowed.
1639    #[must_use]
1640    pub fn has_instrument_statuses(&self, instrument_id: &InstrumentId) -> bool {
1641        self.cache().has_instrument_statuses(instrument_id)
1642    }
1643
1644    /// Returns whether the cache contains a close for the `instrument_id`.
1645    ///
1646    /// # Panics
1647    ///
1648    /// Panics if the cache is already mutably borrowed.
1649    #[must_use]
1650    pub fn has_instrument_close(&self, instrument_id: &InstrumentId) -> bool {
1651        self.cache().has_instrument_close(instrument_id)
1652    }
1653
1654    /// Returns whether the cache contains bars for the `bar_type`.
1655    ///
1656    /// # Panics
1657    ///
1658    /// Panics if the cache is already mutably borrowed.
1659    #[must_use]
1660    pub fn has_bars(&self, bar_type: &BarType) -> bool {
1661        self.cache().has_bars(bar_type)
1662    }
1663
1664    /// Returns the exchange rate for the given currencies and price type (if available).
1665    ///
1666    /// # Panics
1667    ///
1668    /// Panics if the cache is already mutably borrowed.
1669    #[must_use]
1670    pub fn get_xrate(
1671        &self,
1672        venue: Venue,
1673        from_currency: Currency,
1674        to_currency: Currency,
1675        price_type: PriceType,
1676    ) -> Option<Decimal> {
1677        self.cache()
1678            .get_xrate(venue, from_currency, to_currency, price_type)
1679    }
1680
1681    /// Returns the mark exchange rate for the currency pair (if set).
1682    ///
1683    /// # Panics
1684    ///
1685    /// Panics if the cache is already mutably borrowed.
1686    #[must_use]
1687    pub fn get_mark_xrate(&self, from_currency: Currency, to_currency: Currency) -> Option<f64> {
1688        self.cache().get_mark_xrate(from_currency, to_currency)
1689    }
1690
1691    /// Returns the yield curve for the `key` (if found).
1692    ///
1693    /// # Panics
1694    ///
1695    /// Panics if the cache is already mutably borrowed.
1696    #[must_use]
1697    pub fn yield_curve(&self, key: &str) -> Option<Box<dyn Fn(f64) -> f64>> {
1698        self.cache().yield_curve(key)
1699    }
1700
1701    /// Returns an owned copy of the greeks data for the `instrument_id` (if found).
1702    ///
1703    /// # Panics
1704    ///
1705    /// Panics if the cache is already mutably borrowed.
1706    #[must_use]
1707    pub fn greeks(&self, instrument_id: &InstrumentId) -> Option<GreeksData> {
1708        self.cache().greeks(instrument_id)
1709    }
1710
1711    /// Returns exchange-provided option greeks for the `instrument_id` (if found).
1712    ///
1713    /// # Panics
1714    ///
1715    /// Panics if the cache is already mutably borrowed.
1716    #[must_use]
1717    pub fn option_greeks(&self, instrument_id: &InstrumentId) -> Option<OptionGreeks> {
1718        self.cache().option_greeks(instrument_id).copied()
1719    }
1720
1721    /// Returns the currency for the `code` (if found).
1722    ///
1723    /// # Panics
1724    ///
1725    /// Panics if the cache is already mutably borrowed.
1726    #[must_use]
1727    pub fn currency(&self, code: &Ustr) -> Option<Currency> {
1728        self.cache().currency(code).copied()
1729    }
1730
1731    /// Returns the currency for the `code`.
1732    ///
1733    /// # Errors
1734    ///
1735    /// Returns an error if:
1736    /// - The currency is not present in the cache.
1737    /// - The cache is already mutably borrowed.
1738    pub fn try_currency(&self, code: &Ustr) -> Result<Currency, CurrencyLookupError> {
1739        self.try_cache("try_currency")?.try_currency(code).copied()
1740    }
1741
1742    /// Returns an owned copy of the instrument for the `instrument_id` (if found).
1743    ///
1744    /// # Panics
1745    ///
1746    /// Panics if the cache is already mutably borrowed.
1747    #[must_use]
1748    pub fn instrument(&self, instrument_id: &InstrumentId) -> Option<InstrumentAny> {
1749        self.cache().instrument(instrument_id).cloned()
1750    }
1751
1752    /// Returns an owned copy of the instrument for the `instrument_id`.
1753    ///
1754    /// # Errors
1755    ///
1756    /// Returns an error if:
1757    /// - The instrument is not present in the cache.
1758    /// - The cache is already mutably borrowed.
1759    pub fn try_instrument(
1760        &self,
1761        instrument_id: &InstrumentId,
1762    ) -> Result<InstrumentAny, InstrumentLookupError> {
1763        self.try_cache("try_instrument")?
1764            .try_instrument(instrument_id)
1765            .cloned()
1766    }
1767
1768    /// Returns the instrument IDs in the cache, optionally filtered by `venue`.
1769    ///
1770    /// # Panics
1771    ///
1772    /// Panics if the cache is already mutably borrowed.
1773    #[must_use]
1774    pub fn instrument_ids(&self, venue: Option<&Venue>) -> Vec<InstrumentId> {
1775        self.cache()
1776            .instrument_ids(venue)
1777            .into_iter()
1778            .copied()
1779            .collect()
1780    }
1781
1782    /// Returns owned copies of all instruments for the `venue`.
1783    ///
1784    /// # Panics
1785    ///
1786    /// Panics if the cache is already mutably borrowed.
1787    #[must_use]
1788    pub fn instruments(&self, venue: &Venue, underlying: Option<&Ustr>) -> Vec<InstrumentAny> {
1789        self.cache()
1790            .instruments(venue, underlying)
1791            .into_iter()
1792            .cloned()
1793            .collect()
1794    }
1795
1796    /// Returns owned copies of all instruments for the `venue`, parent `root`, and instrument
1797    /// `class`.
1798    ///
1799    /// # Panics
1800    ///
1801    /// Panics if the cache is already mutably borrowed.
1802    #[must_use]
1803    pub fn instruments_by_parent(
1804        &self,
1805        venue: &Venue,
1806        root: &Ustr,
1807        class: InstrumentClass,
1808    ) -> Vec<InstrumentAny> {
1809        self.cache()
1810            .instruments_by_parent(venue, root, class)
1811            .into_iter()
1812            .cloned()
1813            .collect()
1814    }
1815
1816    /// Returns the bar types in the cache, optionally filtered by instrument and price type.
1817    ///
1818    /// # Panics
1819    ///
1820    /// Panics if the cache is already mutably borrowed.
1821    #[must_use]
1822    pub fn bar_types(
1823        &self,
1824        instrument_id: Option<&InstrumentId>,
1825        price_type: Option<&PriceType>,
1826        aggregation_source: AggregationSource,
1827    ) -> Vec<BarType> {
1828        self.cache()
1829            .bar_types(instrument_id, price_type, aggregation_source)
1830            .into_iter()
1831            .copied()
1832            .collect()
1833    }
1834
1835    /// Returns an owned copy of the synthetic instrument for the `instrument_id` (if found).
1836    ///
1837    /// # Panics
1838    ///
1839    /// Panics if the cache is already mutably borrowed.
1840    #[must_use]
1841    pub fn synthetic(&self, instrument_id: &InstrumentId) -> Option<SyntheticInstrument> {
1842        self.cache().synthetic(instrument_id).cloned()
1843    }
1844
1845    /// Returns an owned copy of the synthetic instrument for the `instrument_id`.
1846    ///
1847    /// # Errors
1848    ///
1849    /// Returns an error if:
1850    /// - The synthetic instrument is not present
1851    ///   in the cache.
1852    /// - The cache is already mutably borrowed.
1853    pub fn try_synthetic(
1854        &self,
1855        instrument_id: &InstrumentId,
1856    ) -> Result<SyntheticInstrument, SyntheticInstrumentLookupError> {
1857        self.try_cache("try_synthetic")?
1858            .try_synthetic(instrument_id)
1859            .cloned()
1860    }
1861
1862    /// Returns the synthetic instrument IDs in the cache.
1863    ///
1864    /// # Panics
1865    ///
1866    /// Panics if the cache is already mutably borrowed.
1867    #[must_use]
1868    pub fn synthetic_ids(&self) -> Vec<InstrumentId> {
1869        self.cache().synthetic_ids().into_iter().copied().collect()
1870    }
1871
1872    /// Returns owned copies of all synthetic instruments in the cache.
1873    ///
1874    /// # Panics
1875    ///
1876    /// Panics if the cache is already mutably borrowed.
1877    #[must_use]
1878    pub fn synthetics(&self) -> Vec<SyntheticInstrument> {
1879        self.cache().synthetics().into_iter().cloned().collect()
1880    }
1881
1882    /// Returns an owned copy of the pool for the `instrument_id` (if found).
1883    ///
1884    /// # Panics
1885    ///
1886    /// Panics if the cache is already mutably borrowed.
1887    #[cfg(feature = "defi")]
1888    #[must_use]
1889    pub fn pool(&self, instrument_id: &InstrumentId) -> Option<Pool> {
1890        self.cache().pool(instrument_id).cloned()
1891    }
1892
1893    /// Returns the pool instrument IDs in the cache, optionally filtered by `venue`.
1894    ///
1895    /// # Panics
1896    ///
1897    /// Panics if the cache is already mutably borrowed.
1898    #[cfg(feature = "defi")]
1899    #[must_use]
1900    pub fn pool_ids(&self, venue: Option<&Venue>) -> Vec<InstrumentId> {
1901        self.cache().pool_ids(venue)
1902    }
1903
1904    /// Returns owned copies of all pools in the cache, optionally filtered by `venue`.
1905    ///
1906    /// # Panics
1907    ///
1908    /// Panics if the cache is already mutably borrowed.
1909    #[cfg(feature = "defi")]
1910    #[must_use]
1911    pub fn pools(&self, venue: Option<&Venue>) -> Vec<Pool> {
1912        self.cache().pools(venue).into_iter().cloned().collect()
1913    }
1914
1915    /// Returns an owned copy of the pool profiler for the `instrument_id` (if found).
1916    ///
1917    /// # Panics
1918    ///
1919    /// Panics if the cache is already mutably borrowed.
1920    #[cfg(feature = "defi")]
1921    #[must_use]
1922    pub fn pool_profiler(&self, instrument_id: &InstrumentId) -> Option<PoolProfiler> {
1923        self.cache().pool_profiler(instrument_id).cloned()
1924    }
1925
1926    /// Returns the pool profiler instrument IDs in the cache, optionally filtered by `venue`.
1927    ///
1928    /// # Panics
1929    ///
1930    /// Panics if the cache is already mutably borrowed.
1931    #[cfg(feature = "defi")]
1932    #[must_use]
1933    pub fn pool_profiler_ids(&self, venue: Option<&Venue>) -> Vec<InstrumentId> {
1934        self.cache().pool_profiler_ids(venue)
1935    }
1936
1937    /// Returns owned copies of all pool profilers in the cache, optionally filtered by `venue`.
1938    ///
1939    /// # Panics
1940    ///
1941    /// Panics if the cache is already mutably borrowed.
1942    #[cfg(feature = "defi")]
1943    #[must_use]
1944    pub fn pool_profilers(&self, venue: Option<&Venue>) -> Vec<PoolProfiler> {
1945        self.cache()
1946            .pool_profilers(venue)
1947            .into_iter()
1948            .cloned()
1949            .collect()
1950    }
1951
1952    /// Returns an owned copy of the account for the `account_id` (if found).
1953    ///
1954    /// # Panics
1955    ///
1956    /// Panics if the cache is already mutably borrowed.
1957    #[must_use]
1958    pub fn account(&self, account_id: &AccountId) -> Option<AccountAny> {
1959        self.cache().account_owned(account_id)
1960    }
1961
1962    /// Returns an owned copy of the account for the `account_id`.
1963    ///
1964    /// # Errors
1965    ///
1966    /// Returns an error if:
1967    /// - The account is not present in the cache.
1968    /// - The cache is already mutably borrowed.
1969    pub fn try_account(&self, account_id: &AccountId) -> Result<AccountAny, AccountLookupError> {
1970        self.try_cache("try_account")?
1971            .try_account(account_id)
1972            .map(|account| account.cloned())
1973    }
1974
1975    /// Returns an owned copy of the account for the `venue` (if found).
1976    ///
1977    /// Returns `None` when more than one account is issued under the `venue`.
1978    ///
1979    /// # Panics
1980    ///
1981    /// Panics if the cache is already mutably borrowed.
1982    #[must_use]
1983    pub fn account_for_venue(&self, venue: &Venue) -> Option<AccountAny> {
1984        self.cache().account_for_venue_owned(venue)
1985    }
1986
1987    /// Returns the account ID for the `venue` (if found).
1988    ///
1989    /// Returns `None` when more than one account is issued under the `venue`.
1990    ///
1991    /// # Panics
1992    ///
1993    /// Panics if the cache is already mutably borrowed.
1994    #[must_use]
1995    pub fn account_id(&self, venue: &Venue) -> Option<AccountId> {
1996        self.cache().account_id(venue).copied()
1997    }
1998
1999    /// Returns owned copies of all accounts matching the `account_id`.
2000    ///
2001    /// # Panics
2002    ///
2003    /// Panics if the cache is already mutably borrowed.
2004    #[must_use]
2005    pub fn accounts(&self, account_id: &AccountId) -> Vec<AccountAny> {
2006        self.cache()
2007            .accounts(account_id)
2008            .into_iter()
2009            .map(|account| account.cloned())
2010            .collect()
2011    }
2012
2013    /// Returns owned copies of every account in the cache.
2014    ///
2015    /// # Panics
2016    ///
2017    /// Panics if the cache is already mutably borrowed.
2018    #[must_use]
2019    pub fn accounts_all(&self) -> Vec<AccountAny> {
2020        self.cache().accounts_all_owned()
2021    }
2022
2023    fn cache(&self) -> Ref<'_, Cache> {
2024        self.try_cache("cache read")
2025            .unwrap_or_else(|e| panic!("{e}"))
2026    }
2027
2028    fn try_cache(&self, operation: &'static str) -> Result<Ref<'_, Cache>, ComponentAccessError> {
2029        self.cache
2030            .try_borrow()
2031            .map_err(|_| ComponentAccessError::ReadConflict {
2032                resource: "cache",
2033                operation,
2034            })
2035    }
2036}