1use std::{cell::RefCell, rc::Rc, sync::LazyLock};
19
20use bytes::Bytes;
21use nautilus_core::python::to_pyvalue_err;
22#[cfg(feature = "defi")]
23use nautilus_model::defi::{Pool, PoolProfiler};
24use nautilus_model::{
25 data::{
26 Bar, BarType, FundingRateUpdate, InstrumentClose, InstrumentStatus, QuoteTick, TradeTick,
27 YieldCurve,
28 prices::{IndexPriceUpdate, MarkPriceUpdate},
29 },
30 enums::{AggregationSource, OmsType, OrderSide, PositionSide, PriceType},
31 identifiers::{
32 AccountId, ClientId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
33 StrategyId, Venue, VenueOrderId,
34 },
35 instruments::SyntheticInstrument,
36 orderbook::{OrderBook, own::OwnOrderBook},
37 orders::OrderList,
38 position::Position,
39 python::{
40 account::account_any_to_pyobject,
41 instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
42 orders::{order_any_to_pyobject, pyobject_to_order_any},
43 },
44 types::{Currency, Money, Price, Quantity},
45};
46use pyo3::prelude::*;
47use rust_decimal::prelude::ToPrimitive;
48
49use crate::{
50 cache::{Cache, CacheConfig, database::CacheDatabaseFactory},
51 enums::SerializationEncoding,
52 python::{config_error_to_pyvalue_err, factory::FactoryRegistry},
53};
54
55pub type CacheDatabaseFactoryRegistry = FactoryRegistry<dyn CacheDatabaseFactory>;
57
58static GLOBAL_CACHE_DATABASE_FACTORY_REGISTRY: LazyLock<CacheDatabaseFactoryRegistry> =
59 LazyLock::new(|| CacheDatabaseFactoryRegistry::new("cache database factory"));
60
61#[must_use]
63pub fn get_global_cache_database_factory_registry() -> &'static CacheDatabaseFactoryRegistry {
64 &GLOBAL_CACHE_DATABASE_FACTORY_REGISTRY
65}
66
67#[allow(non_camel_case_types)]
72#[pyo3::pyclass(
73 module = "nautilus_trader.common",
74 name = "Cache",
75 unsendable,
76 from_py_object
77)]
78#[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.common")]
79#[derive(Debug, Clone)]
80pub struct PyCache(Rc<RefCell<Cache>>);
81
82impl PyCache {
83 #[must_use]
85 pub fn from_rc(rc: Rc<RefCell<Cache>>) -> Self {
86 Self(rc)
87 }
88
89 #[must_use]
91 pub fn cache_rc(&self) -> Rc<RefCell<Cache>> {
92 self.0.clone()
93 }
94}
95
96#[pymethods]
97#[pyo3_stub_gen::derive::gen_stub_pymethods]
98impl PyCache {
99 #[new]
100 #[pyo3(signature = (config=None))]
101 fn py_new(config: Option<CacheConfig>) -> PyResult<Self> {
102 let cache = Cache::try_new(config, None).map_err(config_error_to_pyvalue_err)?;
103 Ok(Self(Rc::new(RefCell::new(cache))))
104 }
105
106 #[pyo3(name = "reset")]
107 fn py_reset(&mut self) {
108 self.0.borrow_mut().reset();
109 }
110
111 #[pyo3(name = "dispose")]
112 fn py_dispose(&mut self) {
113 self.0.borrow_mut().dispose();
114 }
115
116 #[pyo3(name = "purge_closed_orders", signature = (ts_now, buffer_secs=0))]
117 fn py_purge_closed_orders(&mut self, ts_now: u64, buffer_secs: u64) {
118 self.0
119 .borrow_mut()
120 .purge_closed_orders(ts_now.into(), buffer_secs);
121 }
122
123 #[pyo3(name = "purge_closed_positions", signature = (ts_now, buffer_secs=0))]
124 fn py_purge_closed_positions(&mut self, ts_now: u64, buffer_secs: u64) {
125 self.0
126 .borrow_mut()
127 .purge_closed_positions(ts_now.into(), buffer_secs);
128 }
129
130 #[pyo3(name = "purge_order")]
131 fn py_purge_order(&mut self, client_order_id: ClientOrderId) {
132 self.0.borrow_mut().purge_order(client_order_id);
133 }
134
135 #[pyo3(name = "purge_position")]
136 fn py_purge_position(&mut self, position_id: PositionId) {
137 self.0.borrow_mut().purge_position(position_id);
138 }
139
140 #[pyo3(name = "purge_instrument")]
141 fn py_purge_instrument(&mut self, instrument_id: InstrumentId) {
142 self.0.borrow_mut().purge_instrument(instrument_id);
143 }
144
145 #[pyo3(name = "purge_account_events", signature = (ts_now, lookback_secs=0))]
146 fn py_purge_account_events(&mut self, ts_now: u64, lookback_secs: u64) {
147 self.0
148 .borrow_mut()
149 .purge_account_events(ts_now.into(), lookback_secs);
150 }
151
152 #[pyo3(name = "get")]
153 fn py_get(&self, key: &str) -> PyResult<Option<Vec<u8>>> {
154 match self.0.borrow().get(key).map_err(to_pyvalue_err)? {
155 Some(bytes) => Ok(Some(bytes.to_vec())),
156 None => Ok(None),
157 }
158 }
159
160 #[pyo3(name = "add")]
161 fn py_add_general(&mut self, key: &str, value: Vec<u8>) -> PyResult<()> {
162 self.0
163 .borrow_mut()
164 .add(key, Bytes::from(value))
165 .map_err(to_pyvalue_err)
166 }
167
168 #[pyo3(name = "add_instrument_close")]
170 fn py_add_instrument_close(&mut self, close: InstrumentClose) -> PyResult<()> {
171 self.0
172 .borrow_mut()
173 .add_instrument_close(close)
174 .map_err(to_pyvalue_err)
175 }
176
177 #[pyo3(name = "add_yield_curve")]
178 fn py_add_yield_curve(&mut self, yield_curve: YieldCurve) -> PyResult<()> {
179 self.0
180 .borrow_mut()
181 .add_yield_curve(yield_curve)
182 .map_err(to_pyvalue_err)
183 }
184
185 #[pyo3(name = "quote", signature = (instrument_id, index=0))]
190 fn py_quote(&self, instrument_id: InstrumentId, index: usize) -> Option<QuoteTick> {
191 self.0
192 .borrow()
193 .quote_at_index(&instrument_id, index)
194 .copied()
195 }
196
197 #[pyo3(name = "trade", signature = (instrument_id, index=0))]
202 fn py_trade(&self, instrument_id: InstrumentId, index: usize) -> Option<TradeTick> {
203 self.0
204 .borrow()
205 .trade_at_index(&instrument_id, index)
206 .copied()
207 }
208
209 #[pyo3(name = "bar", signature = (bar_type, index=0))]
214 fn py_bar(&self, bar_type: BarType, index: usize) -> Option<Bar> {
215 self.0.borrow().bar_at_index(&bar_type, index).copied()
216 }
217
218 #[pyo3(name = "quotes")]
223 fn py_quotes(&self, instrument_id: InstrumentId) -> Option<Vec<QuoteTick>> {
224 self.0.borrow().quotes(&instrument_id)
225 }
226
227 #[pyo3(name = "trades")]
232 fn py_trades(&self, instrument_id: InstrumentId) -> Option<Vec<TradeTick>> {
233 self.0.borrow().trades(&instrument_id)
234 }
235
236 #[pyo3(name = "bars")]
240 fn py_bars(&self, bar_type: BarType) -> Option<Vec<Bar>> {
241 self.0.borrow().bars(&bar_type)
242 }
243
244 #[pyo3(name = "bar_types", signature = (aggregation_source, instrument_id=None, price_type=None))]
245 fn py_bar_types(
246 &self,
247 aggregation_source: AggregationSource,
248 instrument_id: Option<InstrumentId>,
249 price_type: Option<PriceType>,
250 ) -> Vec<BarType> {
251 self.0
252 .borrow()
253 .bar_types(
254 instrument_id.as_ref(),
255 price_type.as_ref(),
256 aggregation_source,
257 )
258 .into_iter()
259 .copied()
260 .collect()
261 }
262
263 #[pyo3(name = "mark_price")]
265 fn py_mark_price(&self, instrument_id: InstrumentId) -> Option<MarkPriceUpdate> {
266 self.0.borrow().mark_price(&instrument_id).copied()
267 }
268
269 #[pyo3(name = "mark_prices")]
274 fn py_mark_prices(&self, instrument_id: InstrumentId) -> Option<Vec<MarkPriceUpdate>> {
275 self.0.borrow().mark_prices(&instrument_id)
276 }
277
278 #[pyo3(name = "index_price")]
280 fn py_index_price(&self, instrument_id: InstrumentId) -> Option<IndexPriceUpdate> {
281 self.0.borrow().index_price(&instrument_id).copied()
282 }
283
284 #[pyo3(name = "index_prices")]
289 fn py_index_prices(&self, instrument_id: InstrumentId) -> Option<Vec<IndexPriceUpdate>> {
290 self.0.borrow().index_prices(&instrument_id)
291 }
292
293 #[pyo3(name = "funding_rate")]
295 fn py_funding_rate(&self, instrument_id: InstrumentId) -> Option<FundingRateUpdate> {
296 self.0.borrow().funding_rate(&instrument_id).copied()
297 }
298
299 #[pyo3(name = "funding_rates")]
304 fn py_funding_rates(&self, instrument_id: InstrumentId) -> Option<Vec<FundingRateUpdate>> {
305 self.0.borrow().funding_rates(&instrument_id)
306 }
307
308 #[pyo3(name = "instrument_status")]
310 fn py_instrument_status(&self, instrument_id: InstrumentId) -> Option<InstrumentStatus> {
311 self.0.borrow().instrument_status(&instrument_id).copied()
312 }
313
314 #[pyo3(name = "instrument_statuses")]
319 fn py_instrument_statuses(&self, instrument_id: InstrumentId) -> Option<Vec<InstrumentStatus>> {
320 self.0.borrow().instrument_statuses(&instrument_id)
321 }
322
323 #[pyo3(name = "instrument_close")]
327 fn py_instrument_close(&self, instrument_id: InstrumentId) -> Option<InstrumentClose> {
328 self.0.borrow().instrument_close(&instrument_id).copied()
329 }
330
331 #[pyo3(name = "price")]
332 fn py_price(&self, instrument_id: InstrumentId, price_type: PriceType) -> Option<Price> {
333 self.0.borrow().price(&instrument_id, price_type)
334 }
335
336 #[pyo3(name = "order_book")]
340 fn py_order_book(&self, instrument_id: InstrumentId) -> Option<OrderBook> {
341 self.0.borrow().order_book(&instrument_id).cloned()
342 }
343
344 #[pyo3(name = "top_of_book")]
356 fn py_top_of_book(
357 &self,
358 instrument_id: InstrumentId,
359 ) -> Option<(Price, Quantity, Price, Quantity)> {
360 let cache = self.0.borrow();
361 let book = cache.order_book(&instrument_id)?;
362 Some((
363 book.best_bid_price()?,
364 book.best_bid_size()?,
365 book.best_ask_price()?,
366 book.best_ask_size()?,
367 ))
368 }
369
370 #[pyo3(name = "has_order_book")]
371 fn py_has_order_book(&self, instrument_id: InstrumentId) -> bool {
372 self.0.borrow().has_order_book(&instrument_id)
373 }
374
375 #[pyo3(name = "book_update_count")]
376 fn py_book_update_count(&self, instrument_id: InstrumentId) -> usize {
377 self.0.borrow().book_update_count(&instrument_id)
378 }
379
380 #[pyo3(name = "has_quote_ticks")]
381 fn py_has_quote_ticks(&self, instrument_id: InstrumentId) -> bool {
382 self.0.borrow().has_quote_ticks(&instrument_id)
383 }
384
385 #[pyo3(name = "has_trade_ticks")]
386 fn py_has_trade_ticks(&self, instrument_id: InstrumentId) -> bool {
387 self.0.borrow().has_trade_ticks(&instrument_id)
388 }
389
390 #[pyo3(name = "has_mark_prices")]
391 fn py_has_mark_prices(&self, instrument_id: InstrumentId) -> bool {
392 self.0.borrow().has_mark_prices(&instrument_id)
393 }
394
395 #[pyo3(name = "has_index_prices")]
396 fn py_has_index_prices(&self, instrument_id: InstrumentId) -> bool {
397 self.0.borrow().has_index_prices(&instrument_id)
398 }
399
400 #[pyo3(name = "has_funding_rates")]
401 fn py_has_funding_rates(&self, instrument_id: InstrumentId) -> bool {
402 self.0.borrow().has_funding_rates(&instrument_id)
403 }
404
405 #[pyo3(name = "has_instrument_statuses")]
406 fn py_has_instrument_statuses(&self, instrument_id: InstrumentId) -> bool {
407 self.0.borrow().has_instrument_statuses(&instrument_id)
408 }
409
410 #[pyo3(name = "has_instrument_close")]
411 fn py_has_instrument_close(&self, instrument_id: InstrumentId) -> bool {
412 self.0.borrow().has_instrument_close(&instrument_id)
413 }
414
415 #[pyo3(name = "has_bars")]
416 fn py_has_bars(&self, bar_type: BarType) -> bool {
417 self.0.borrow().has_bars(&bar_type)
418 }
419
420 #[pyo3(name = "quote_count")]
421 fn py_quote_count(&self, instrument_id: InstrumentId) -> usize {
422 self.0.borrow().quote_count(&instrument_id)
423 }
424
425 #[pyo3(name = "trade_count")]
426 fn py_trade_count(&self, instrument_id: InstrumentId) -> usize {
427 self.0.borrow().trade_count(&instrument_id)
428 }
429
430 #[pyo3(name = "mark_price_count")]
431 fn py_mark_price_count(&self, instrument_id: InstrumentId) -> usize {
432 self.0.borrow().mark_price_count(&instrument_id)
433 }
434
435 #[pyo3(name = "index_price_count")]
436 fn py_index_price_count(&self, instrument_id: InstrumentId) -> usize {
437 self.0.borrow().index_price_count(&instrument_id)
438 }
439
440 #[pyo3(name = "funding_rate_count")]
441 fn py_funding_rate_count(&self, instrument_id: InstrumentId) -> usize {
442 self.0.borrow().funding_rate_count(&instrument_id)
443 }
444
445 #[pyo3(name = "instrument_status_count")]
446 fn py_instrument_status_count(&self, instrument_id: InstrumentId) -> usize {
447 self.0.borrow().instrument_status_count(&instrument_id)
448 }
449
450 #[pyo3(name = "bar_count")]
451 fn py_bar_count(&self, bar_type: BarType) -> usize {
452 self.0.borrow().bar_count(&bar_type)
453 }
454
455 #[pyo3(name = "get_xrate")]
456 fn py_get_xrate(
457 &self,
458 venue: Venue,
459 from_currency: Currency,
460 to_currency: Currency,
461 price_type: PriceType,
462 ) -> Option<f64> {
463 self.0
464 .borrow()
465 .get_xrate(venue, from_currency, to_currency, price_type)
466 .and_then(|rate| rate.to_f64())
467 }
468
469 #[pyo3(name = "get_mark_xrate")]
470 fn py_get_mark_xrate(&self, from_currency: Currency, to_currency: Currency) -> Option<f64> {
471 self.0.borrow().get_mark_xrate(from_currency, to_currency)
472 }
473
474 #[pyo3(name = "own_order_book")]
475 fn py_own_order_book(&self, instrument_id: InstrumentId) -> Option<OwnOrderBook> {
476 self.0.borrow().own_order_book(&instrument_id).cloned()
477 }
478
479 #[pyo3(name = "instrument")]
480 fn py_instrument(
481 &self,
482 py: Python,
483 instrument_id: InstrumentId,
484 ) -> PyResult<Option<Py<PyAny>>> {
485 let cache = self.0.borrow();
486 match cache.instrument(&instrument_id) {
487 Some(instrument) => Ok(Some(instrument_any_to_pyobject(py, instrument.clone())?)),
488 None => Ok(None),
489 }
490 }
491
492 #[pyo3(name = "instrument_ids", signature = (venue=None))]
493 fn py_instrument_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
494 self.0
495 .borrow()
496 .instrument_ids(venue.as_ref())
497 .into_iter()
498 .copied()
499 .collect()
500 }
501
502 #[pyo3(name = "instruments", signature = (venue=None))]
503 fn py_instruments(&self, py: Python, venue: Option<Venue>) -> PyResult<Vec<Py<PyAny>>> {
504 let cache = self.0.borrow();
505 let mut py_instruments = Vec::new();
506
507 match venue {
508 Some(venue) => {
509 for instrument in cache.instruments(&venue, None) {
510 py_instruments.push(instrument_any_to_pyobject(py, (*instrument).clone())?);
511 }
512 }
513 None => {
514 for instrument_id in cache.instrument_ids(None) {
515 if let Some(instrument) = cache.instrument(instrument_id) {
516 py_instruments.push(instrument_any_to_pyobject(py, instrument.clone())?);
517 }
518 }
519 }
520 }
521 Ok(py_instruments)
522 }
523
524 #[pyo3(name = "synthetic")]
525 fn py_synthetic(&self, instrument_id: InstrumentId) -> Option<SyntheticInstrument> {
526 self.0.borrow().synthetic(&instrument_id).cloned()
527 }
528
529 #[pyo3(name = "synthetic_ids")]
530 fn py_synthetic_ids(&self) -> Vec<InstrumentId> {
531 self.0
532 .borrow()
533 .synthetic_ids()
534 .into_iter()
535 .copied()
536 .collect()
537 }
538
539 #[pyo3(name = "account")]
540 fn py_account(&self, py: Python, account_id: AccountId) -> PyResult<Option<Py<PyAny>>> {
541 let cache = self.0.borrow();
542 match cache.account(&account_id) {
543 Some(account) => Ok(Some(account_any_to_pyobject(py, account.clone())?)),
544 None => Ok(None),
545 }
546 }
547
548 #[pyo3(name = "account_for_venue")]
549 fn py_account_for_venue(&self, py: Python, venue: Venue) -> PyResult<Option<Py<PyAny>>> {
550 let cache = self.0.borrow();
551 match cache.account_for_venue(&venue) {
552 Some(account) => Ok(Some(account_any_to_pyobject(py, account.clone())?)),
553 None => Ok(None),
554 }
555 }
556
557 #[pyo3(name = "account_id")]
558 fn py_account_id(&self, venue: Venue) -> Option<AccountId> {
559 self.0.borrow().account_id(&venue).copied()
560 }
561
562 #[pyo3(name = "client_order_ids", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
563 fn py_client_order_ids(
564 &self,
565 venue: Option<Venue>,
566 instrument_id: Option<InstrumentId>,
567 strategy_id: Option<StrategyId>,
568 account_id: Option<AccountId>,
569 ) -> Vec<ClientOrderId> {
570 self.0
571 .borrow()
572 .client_order_ids(
573 venue.as_ref(),
574 instrument_id.as_ref(),
575 strategy_id.as_ref(),
576 account_id.as_ref(),
577 )
578 .into_iter()
579 .collect()
580 }
581
582 #[pyo3(name = "client_order_ids_open", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
583 fn py_client_order_ids_open(
584 &self,
585 venue: Option<Venue>,
586 instrument_id: Option<InstrumentId>,
587 strategy_id: Option<StrategyId>,
588 account_id: Option<AccountId>,
589 ) -> Vec<ClientOrderId> {
590 self.0
591 .borrow()
592 .client_order_ids_open(
593 venue.as_ref(),
594 instrument_id.as_ref(),
595 strategy_id.as_ref(),
596 account_id.as_ref(),
597 )
598 .into_iter()
599 .collect()
600 }
601
602 #[pyo3(name = "client_order_ids_closed", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
603 fn py_client_order_ids_closed(
604 &self,
605 venue: Option<Venue>,
606 instrument_id: Option<InstrumentId>,
607 strategy_id: Option<StrategyId>,
608 account_id: Option<AccountId>,
609 ) -> Vec<ClientOrderId> {
610 self.0
611 .borrow()
612 .client_order_ids_closed(
613 venue.as_ref(),
614 instrument_id.as_ref(),
615 strategy_id.as_ref(),
616 account_id.as_ref(),
617 )
618 .into_iter()
619 .collect()
620 }
621
622 #[pyo3(name = "client_order_ids_emulated", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
623 fn py_client_order_ids_emulated(
624 &self,
625 venue: Option<Venue>,
626 instrument_id: Option<InstrumentId>,
627 strategy_id: Option<StrategyId>,
628 account_id: Option<AccountId>,
629 ) -> Vec<ClientOrderId> {
630 self.0
631 .borrow()
632 .client_order_ids_emulated(
633 venue.as_ref(),
634 instrument_id.as_ref(),
635 strategy_id.as_ref(),
636 account_id.as_ref(),
637 )
638 .into_iter()
639 .collect()
640 }
641
642 #[pyo3(name = "client_order_ids_inflight", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
643 fn py_client_order_ids_inflight(
644 &self,
645 venue: Option<Venue>,
646 instrument_id: Option<InstrumentId>,
647 strategy_id: Option<StrategyId>,
648 account_id: Option<AccountId>,
649 ) -> Vec<ClientOrderId> {
650 self.0
651 .borrow()
652 .client_order_ids_inflight(
653 venue.as_ref(),
654 instrument_id.as_ref(),
655 strategy_id.as_ref(),
656 account_id.as_ref(),
657 )
658 .into_iter()
659 .collect()
660 }
661
662 #[pyo3(name = "position_ids", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
663 fn py_position_ids(
664 &self,
665 venue: Option<Venue>,
666 instrument_id: Option<InstrumentId>,
667 strategy_id: Option<StrategyId>,
668 account_id: Option<AccountId>,
669 ) -> Vec<PositionId> {
670 self.0
671 .borrow()
672 .position_ids(
673 venue.as_ref(),
674 instrument_id.as_ref(),
675 strategy_id.as_ref(),
676 account_id.as_ref(),
677 )
678 .into_iter()
679 .collect()
680 }
681
682 #[pyo3(name = "position_open_ids", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
683 fn py_position_open_ids(
684 &self,
685 venue: Option<Venue>,
686 instrument_id: Option<InstrumentId>,
687 strategy_id: Option<StrategyId>,
688 account_id: Option<AccountId>,
689 ) -> Vec<PositionId> {
690 self.0
691 .borrow()
692 .position_open_ids(
693 venue.as_ref(),
694 instrument_id.as_ref(),
695 strategy_id.as_ref(),
696 account_id.as_ref(),
697 )
698 .into_iter()
699 .collect()
700 }
701
702 #[pyo3(name = "position_closed_ids", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
703 fn py_position_closed_ids(
704 &self,
705 venue: Option<Venue>,
706 instrument_id: Option<InstrumentId>,
707 strategy_id: Option<StrategyId>,
708 account_id: Option<AccountId>,
709 ) -> Vec<PositionId> {
710 self.0
711 .borrow()
712 .position_closed_ids(
713 venue.as_ref(),
714 instrument_id.as_ref(),
715 strategy_id.as_ref(),
716 account_id.as_ref(),
717 )
718 .into_iter()
719 .collect()
720 }
721
722 #[pyo3(name = "strategy_ids")]
723 fn py_strategy_ids(&self) -> Vec<StrategyId> {
724 self.0.borrow().strategy_ids().into_iter().collect()
725 }
726
727 #[pyo3(name = "exec_algorithm_ids")]
728 fn py_exec_algorithm_ids(&self) -> Vec<ExecAlgorithmId> {
729 self.0.borrow().exec_algorithm_ids().into_iter().collect()
730 }
731
732 #[pyo3(name = "order")]
733 fn py_order(&self, py: Python, client_order_id: ClientOrderId) -> PyResult<Option<Py<PyAny>>> {
734 let cache = self.0.borrow();
735 match cache.order(&client_order_id) {
736 Some(order) => Ok(Some(order_any_to_pyobject(py, order.clone())?)),
737 None => Ok(None),
738 }
739 }
740
741 #[pyo3(name = "client_order_id")]
742 fn py_client_order_id(&self, venue_order_id: VenueOrderId) -> Option<ClientOrderId> {
743 self.0.borrow().client_order_id(&venue_order_id).copied()
744 }
745
746 #[pyo3(name = "venue_order_id")]
747 fn py_venue_order_id(&self, client_order_id: ClientOrderId) -> Option<VenueOrderId> {
748 self.0.borrow().venue_order_id(&client_order_id).copied()
749 }
750
751 #[pyo3(name = "client_id")]
752 fn py_client_id(&self, client_order_id: ClientOrderId) -> Option<ClientId> {
753 self.0.borrow().client_id(&client_order_id).copied()
754 }
755
756 #[pyo3(name = "orders", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
757 fn py_orders(
758 &self,
759 py: Python,
760 venue: Option<Venue>,
761 instrument_id: Option<InstrumentId>,
762 strategy_id: Option<StrategyId>,
763 account_id: Option<AccountId>,
764 side: Option<OrderSide>,
765 ) -> PyResult<Vec<Py<PyAny>>> {
766 let cache = self.0.borrow();
767 cache
768 .orders(
769 venue.as_ref(),
770 instrument_id.as_ref(),
771 strategy_id.as_ref(),
772 account_id.as_ref(),
773 side,
774 )
775 .into_iter()
776 .map(|o| order_any_to_pyobject(py, o.clone()))
777 .collect()
778 }
779
780 #[pyo3(name = "orders_open", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
781 fn py_orders_open(
782 &self,
783 py: Python,
784 venue: Option<Venue>,
785 instrument_id: Option<InstrumentId>,
786 strategy_id: Option<StrategyId>,
787 account_id: Option<AccountId>,
788 side: Option<OrderSide>,
789 ) -> PyResult<Vec<Py<PyAny>>> {
790 let cache = self.0.borrow();
791 cache
792 .orders_open(
793 venue.as_ref(),
794 instrument_id.as_ref(),
795 strategy_id.as_ref(),
796 account_id.as_ref(),
797 side,
798 )
799 .into_iter()
800 .map(|o| order_any_to_pyobject(py, o.clone()))
801 .collect()
802 }
803
804 #[pyo3(name = "orders_closed", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
805 fn py_orders_closed(
806 &self,
807 py: Python,
808 venue: Option<Venue>,
809 instrument_id: Option<InstrumentId>,
810 strategy_id: Option<StrategyId>,
811 account_id: Option<AccountId>,
812 side: Option<OrderSide>,
813 ) -> PyResult<Vec<Py<PyAny>>> {
814 let cache = self.0.borrow();
815 cache
816 .orders_closed(
817 venue.as_ref(),
818 instrument_id.as_ref(),
819 strategy_id.as_ref(),
820 account_id.as_ref(),
821 side,
822 )
823 .into_iter()
824 .map(|o| order_any_to_pyobject(py, o.clone()))
825 .collect()
826 }
827
828 #[pyo3(name = "orders_emulated", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
829 fn py_orders_emulated(
830 &self,
831 py: Python,
832 venue: Option<Venue>,
833 instrument_id: Option<InstrumentId>,
834 strategy_id: Option<StrategyId>,
835 account_id: Option<AccountId>,
836 side: Option<OrderSide>,
837 ) -> PyResult<Vec<Py<PyAny>>> {
838 let cache = self.0.borrow();
839 cache
840 .orders_emulated(
841 venue.as_ref(),
842 instrument_id.as_ref(),
843 strategy_id.as_ref(),
844 account_id.as_ref(),
845 side,
846 )
847 .into_iter()
848 .map(|o| order_any_to_pyobject(py, o.clone()))
849 .collect()
850 }
851
852 #[pyo3(name = "orders_inflight", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
853 fn py_orders_inflight(
854 &self,
855 py: Python,
856 venue: Option<Venue>,
857 instrument_id: Option<InstrumentId>,
858 strategy_id: Option<StrategyId>,
859 account_id: Option<AccountId>,
860 side: Option<OrderSide>,
861 ) -> PyResult<Vec<Py<PyAny>>> {
862 let cache = self.0.borrow();
863 cache
864 .orders_inflight(
865 venue.as_ref(),
866 instrument_id.as_ref(),
867 strategy_id.as_ref(),
868 account_id.as_ref(),
869 side,
870 )
871 .into_iter()
872 .map(|o| order_any_to_pyobject(py, o.clone()))
873 .collect()
874 }
875
876 #[pyo3(name = "orders_for_position")]
877 fn py_orders_for_position(
878 &self,
879 py: Python,
880 position_id: PositionId,
881 ) -> PyResult<Vec<Py<PyAny>>> {
882 let cache = self.0.borrow();
883 cache
884 .orders_for_position(&position_id)
885 .into_iter()
886 .map(|o| order_any_to_pyobject(py, o.clone()))
887 .collect()
888 }
889
890 #[pyo3(name = "order_exists")]
891 fn py_order_exists(&self, client_order_id: ClientOrderId) -> bool {
892 self.0.borrow().order_exists(&client_order_id)
893 }
894
895 #[pyo3(name = "is_order_open")]
896 fn py_is_order_open(&self, client_order_id: ClientOrderId) -> bool {
897 self.0.borrow().is_order_open(&client_order_id)
898 }
899
900 #[pyo3(name = "is_order_closed")]
901 fn py_is_order_closed(&self, client_order_id: ClientOrderId) -> bool {
902 self.0.borrow().is_order_closed(&client_order_id)
903 }
904
905 #[pyo3(name = "is_order_emulated")]
906 fn py_is_order_emulated(&self, client_order_id: ClientOrderId) -> bool {
907 self.0.borrow().is_order_emulated(&client_order_id)
908 }
909
910 #[pyo3(name = "is_order_inflight")]
911 fn py_is_order_inflight(&self, client_order_id: ClientOrderId) -> bool {
912 self.0.borrow().is_order_inflight(&client_order_id)
913 }
914
915 #[pyo3(name = "is_order_pending_cancel_local")]
916 fn py_is_order_pending_cancel_local(&self, client_order_id: ClientOrderId) -> bool {
917 self.0
918 .borrow()
919 .is_order_pending_cancel_local(&client_order_id)
920 }
921
922 #[pyo3(name = "orders_open_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
923 fn py_orders_open_count(
924 &self,
925 venue: Option<Venue>,
926 instrument_id: Option<InstrumentId>,
927 strategy_id: Option<StrategyId>,
928 account_id: Option<AccountId>,
929 side: Option<OrderSide>,
930 ) -> usize {
931 self.0.borrow().orders_open_count(
932 venue.as_ref(),
933 instrument_id.as_ref(),
934 strategy_id.as_ref(),
935 account_id.as_ref(),
936 side,
937 )
938 }
939
940 #[pyo3(name = "orders_closed_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
941 fn py_orders_closed_count(
942 &self,
943 venue: Option<Venue>,
944 instrument_id: Option<InstrumentId>,
945 strategy_id: Option<StrategyId>,
946 account_id: Option<AccountId>,
947 side: Option<OrderSide>,
948 ) -> usize {
949 self.0.borrow().orders_closed_count(
950 venue.as_ref(),
951 instrument_id.as_ref(),
952 strategy_id.as_ref(),
953 account_id.as_ref(),
954 side,
955 )
956 }
957
958 #[pyo3(name = "orders_emulated_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
959 fn py_orders_emulated_count(
960 &self,
961 venue: Option<Venue>,
962 instrument_id: Option<InstrumentId>,
963 strategy_id: Option<StrategyId>,
964 account_id: Option<AccountId>,
965 side: Option<OrderSide>,
966 ) -> usize {
967 self.0.borrow().orders_emulated_count(
968 venue.as_ref(),
969 instrument_id.as_ref(),
970 strategy_id.as_ref(),
971 account_id.as_ref(),
972 side,
973 )
974 }
975
976 #[pyo3(name = "orders_inflight_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
977 fn py_orders_inflight_count(
978 &self,
979 venue: Option<Venue>,
980 instrument_id: Option<InstrumentId>,
981 strategy_id: Option<StrategyId>,
982 account_id: Option<AccountId>,
983 side: Option<OrderSide>,
984 ) -> usize {
985 self.0.borrow().orders_inflight_count(
986 venue.as_ref(),
987 instrument_id.as_ref(),
988 strategy_id.as_ref(),
989 account_id.as_ref(),
990 side,
991 )
992 }
993
994 #[pyo3(name = "orders_total_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
995 fn py_orders_total_count(
996 &self,
997 venue: Option<Venue>,
998 instrument_id: Option<InstrumentId>,
999 strategy_id: Option<StrategyId>,
1000 account_id: Option<AccountId>,
1001 side: Option<OrderSide>,
1002 ) -> usize {
1003 self.0.borrow().orders_total_count(
1004 venue.as_ref(),
1005 instrument_id.as_ref(),
1006 strategy_id.as_ref(),
1007 account_id.as_ref(),
1008 side,
1009 )
1010 }
1011
1012 #[pyo3(name = "order_list")]
1013 fn py_order_list(&self, order_list_id: OrderListId) -> Option<OrderList> {
1014 self.0.borrow().order_list(&order_list_id).cloned()
1015 }
1016
1017 #[pyo3(name = "order_lists", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
1018 fn py_order_lists(
1019 &self,
1020 venue: Option<Venue>,
1021 instrument_id: Option<InstrumentId>,
1022 strategy_id: Option<StrategyId>,
1023 account_id: Option<AccountId>,
1024 ) -> Vec<OrderList> {
1025 let cache = self.0.borrow();
1026 cache
1027 .order_lists(
1028 venue.as_ref(),
1029 instrument_id.as_ref(),
1030 strategy_id.as_ref(),
1031 account_id.as_ref(),
1032 )
1033 .into_iter()
1034 .cloned()
1035 .collect()
1036 }
1037
1038 #[pyo3(name = "order_list_exists")]
1039 fn py_order_list_exists(&self, order_list_id: OrderListId) -> bool {
1040 self.0.borrow().order_list_exists(&order_list_id)
1041 }
1042
1043 #[pyo3(name = "orders_for_exec_algorithm", signature = (exec_algorithm_id, venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1044 #[expect(clippy::too_many_arguments)]
1045 fn py_orders_for_exec_algorithm(
1046 &self,
1047 py: Python,
1048 exec_algorithm_id: ExecAlgorithmId,
1049 venue: Option<Venue>,
1050 instrument_id: Option<InstrumentId>,
1051 strategy_id: Option<StrategyId>,
1052 account_id: Option<AccountId>,
1053 side: Option<OrderSide>,
1054 ) -> PyResult<Vec<Py<PyAny>>> {
1055 let cache = self.0.borrow();
1056 cache
1057 .orders_for_exec_algorithm(
1058 &exec_algorithm_id,
1059 venue.as_ref(),
1060 instrument_id.as_ref(),
1061 strategy_id.as_ref(),
1062 account_id.as_ref(),
1063 side,
1064 )
1065 .into_iter()
1066 .map(|o| order_any_to_pyobject(py, o.clone()))
1067 .collect()
1068 }
1069
1070 #[pyo3(name = "orders_for_exec_spawn")]
1071 fn py_orders_for_exec_spawn(
1072 &self,
1073 py: Python,
1074 exec_spawn_id: ClientOrderId,
1075 ) -> PyResult<Vec<Py<PyAny>>> {
1076 let cache = self.0.borrow();
1077 cache
1078 .orders_for_exec_spawn(&exec_spawn_id)
1079 .into_iter()
1080 .map(|o| order_any_to_pyobject(py, o.clone()))
1081 .collect()
1082 }
1083
1084 #[pyo3(name = "exec_spawn_total_quantity")]
1085 fn py_exec_spawn_total_quantity(
1086 &self,
1087 exec_spawn_id: ClientOrderId,
1088 active_only: bool,
1089 ) -> Option<Quantity> {
1090 self.0
1091 .borrow()
1092 .exec_spawn_total_quantity(&exec_spawn_id, active_only)
1093 }
1094
1095 #[pyo3(name = "exec_spawn_total_filled_qty")]
1096 fn py_exec_spawn_total_filled_qty(
1097 &self,
1098 exec_spawn_id: ClientOrderId,
1099 active_only: bool,
1100 ) -> Option<Quantity> {
1101 self.0
1102 .borrow()
1103 .exec_spawn_total_filled_qty(&exec_spawn_id, active_only)
1104 }
1105
1106 #[pyo3(name = "exec_spawn_total_leaves_qty")]
1107 fn py_exec_spawn_total_leaves_qty(
1108 &self,
1109 exec_spawn_id: ClientOrderId,
1110 active_only: bool,
1111 ) -> Option<Quantity> {
1112 self.0
1113 .borrow()
1114 .exec_spawn_total_leaves_qty(&exec_spawn_id, active_only)
1115 }
1116
1117 #[pyo3(name = "position")]
1118 fn py_position(&self, py: Python, position_id: PositionId) -> PyResult<Option<Py<PyAny>>> {
1119 let cache = self.0.borrow();
1120 match cache.position(&position_id) {
1121 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
1122 None => Ok(None),
1123 }
1124 }
1125
1126 #[pyo3(name = "position_for_order")]
1127 fn py_position_for_order(
1128 &self,
1129 py: Python,
1130 client_order_id: ClientOrderId,
1131 ) -> PyResult<Option<Py<PyAny>>> {
1132 let cache = self.0.borrow();
1133 match cache.position_for_order(&client_order_id) {
1134 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
1135 None => Ok(None),
1136 }
1137 }
1138
1139 #[pyo3(name = "position_id")]
1140 fn py_position_id(&self, client_order_id: ClientOrderId) -> Option<PositionId> {
1141 self.0.borrow().position_id(&client_order_id).copied()
1142 }
1143
1144 #[pyo3(name = "positions", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1145 fn py_positions(
1146 &self,
1147 py: Python,
1148 venue: Option<Venue>,
1149 instrument_id: Option<InstrumentId>,
1150 strategy_id: Option<StrategyId>,
1151 account_id: Option<AccountId>,
1152 side: Option<PositionSide>,
1153 ) -> PyResult<Vec<Py<PyAny>>> {
1154 let cache = self.0.borrow();
1155 cache
1156 .positions(
1157 venue.as_ref(),
1158 instrument_id.as_ref(),
1159 strategy_id.as_ref(),
1160 account_id.as_ref(),
1161 side,
1162 )
1163 .into_iter()
1164 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
1165 .collect()
1166 }
1167
1168 #[pyo3(name = "positions_open", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1169 fn py_positions_open(
1170 &self,
1171 py: Python,
1172 venue: Option<Venue>,
1173 instrument_id: Option<InstrumentId>,
1174 strategy_id: Option<StrategyId>,
1175 account_id: Option<AccountId>,
1176 side: Option<PositionSide>,
1177 ) -> PyResult<Vec<Py<PyAny>>> {
1178 let cache = self.0.borrow();
1179 cache
1180 .positions_open(
1181 venue.as_ref(),
1182 instrument_id.as_ref(),
1183 strategy_id.as_ref(),
1184 account_id.as_ref(),
1185 side,
1186 )
1187 .into_iter()
1188 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
1189 .collect()
1190 }
1191
1192 #[pyo3(name = "positions_closed", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1193 fn py_positions_closed(
1194 &self,
1195 py: Python,
1196 venue: Option<Venue>,
1197 instrument_id: Option<InstrumentId>,
1198 strategy_id: Option<StrategyId>,
1199 account_id: Option<AccountId>,
1200 side: Option<PositionSide>,
1201 ) -> PyResult<Vec<Py<PyAny>>> {
1202 let cache = self.0.borrow();
1203 cache
1204 .positions_closed(
1205 venue.as_ref(),
1206 instrument_id.as_ref(),
1207 strategy_id.as_ref(),
1208 account_id.as_ref(),
1209 side,
1210 )
1211 .into_iter()
1212 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
1213 .collect()
1214 }
1215
1216 #[pyo3(name = "position_exists")]
1217 fn py_position_exists(&self, position_id: PositionId) -> bool {
1218 self.0.borrow().position_exists(&position_id)
1219 }
1220
1221 #[pyo3(name = "is_position_open")]
1222 fn py_is_position_open(&self, position_id: PositionId) -> bool {
1223 self.0.borrow().is_position_open(&position_id)
1224 }
1225
1226 #[pyo3(name = "is_position_closed")]
1227 fn py_is_position_closed(&self, position_id: PositionId) -> bool {
1228 self.0.borrow().is_position_closed(&position_id)
1229 }
1230
1231 #[pyo3(name = "positions_open_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1232 fn py_positions_open_count(
1233 &self,
1234 venue: Option<Venue>,
1235 instrument_id: Option<InstrumentId>,
1236 strategy_id: Option<StrategyId>,
1237 account_id: Option<AccountId>,
1238 side: Option<PositionSide>,
1239 ) -> usize {
1240 self.0.borrow().positions_open_count(
1241 venue.as_ref(),
1242 instrument_id.as_ref(),
1243 strategy_id.as_ref(),
1244 account_id.as_ref(),
1245 side,
1246 )
1247 }
1248
1249 #[pyo3(name = "positions_closed_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1250 fn py_positions_closed_count(
1251 &self,
1252 venue: Option<Venue>,
1253 instrument_id: Option<InstrumentId>,
1254 strategy_id: Option<StrategyId>,
1255 account_id: Option<AccountId>,
1256 side: Option<PositionSide>,
1257 ) -> usize {
1258 self.0.borrow().positions_closed_count(
1259 venue.as_ref(),
1260 instrument_id.as_ref(),
1261 strategy_id.as_ref(),
1262 account_id.as_ref(),
1263 side,
1264 )
1265 }
1266
1267 #[pyo3(name = "positions_total_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1268 fn py_positions_total_count(
1269 &self,
1270 venue: Option<Venue>,
1271 instrument_id: Option<InstrumentId>,
1272 strategy_id: Option<StrategyId>,
1273 account_id: Option<AccountId>,
1274 side: Option<PositionSide>,
1275 ) -> usize {
1276 self.0.borrow().positions_total_count(
1277 venue.as_ref(),
1278 instrument_id.as_ref(),
1279 strategy_id.as_ref(),
1280 account_id.as_ref(),
1281 side,
1282 )
1283 }
1284
1285 #[pyo3(name = "strategy_id_for_order")]
1286 fn py_strategy_id_for_order(&self, client_order_id: ClientOrderId) -> Option<StrategyId> {
1287 self.0
1288 .borrow()
1289 .strategy_id_for_order(&client_order_id)
1290 .copied()
1291 }
1292
1293 #[pyo3(name = "strategy_id_for_position")]
1294 fn py_strategy_id_for_position(&self, position_id: PositionId) -> Option<StrategyId> {
1295 self.0
1296 .borrow()
1297 .strategy_id_for_position(&position_id)
1298 .copied()
1299 }
1300
1301 #[pyo3(name = "position_snapshot_bytes")]
1302 fn py_position_snapshot_bytes(&self, position_id: PositionId) -> Option<Vec<Vec<u8>>> {
1303 self.0.borrow().position_snapshot_bytes(&position_id)
1304 }
1305
1306 #[pyo3(name = "snapshot_position")]
1307 #[expect(clippy::needless_pass_by_value)]
1308 fn py_snapshot_position(&self, py: Python, position: Py<PyAny>) -> PyResult<()> {
1309 let position_obj = position.extract::<Position>(py)?;
1310 self.0
1311 .borrow_mut()
1312 .snapshot_position(&position_obj)
1313 .map_err(to_pyvalue_err)
1314 }
1315
1316 #[pyo3(name = "position_snapshots", signature = (position_id=None, account_id=None))]
1317 fn py_position_snapshots(
1318 &self,
1319 py: Python,
1320 position_id: Option<PositionId>,
1321 account_id: Option<AccountId>,
1322 ) -> PyResult<Vec<Py<PyAny>>> {
1323 let cache = self.0.borrow();
1324 cache
1325 .position_snapshots(position_id.as_ref(), account_id.as_ref())
1326 .into_iter()
1327 .map(|p| Ok(p.into_pyobject(py)?.into()))
1328 .collect()
1329 }
1330}
1331
1332#[cfg(test)]
1333mod tests {
1334 use nautilus_core::UnixNanos;
1335 use nautilus_model::{
1336 data::{BookOrder, stubs::stub_instrument_close},
1337 enums::{BookType, InstrumentCloseType},
1338 };
1339 use pyo3::exceptions::PyValueError;
1340 use rstest::rstest;
1341
1342 use super::*;
1343
1344 fn book_order(side: OrderSide, price: &str, size: &str, id: u64) -> BookOrder {
1345 BookOrder::new(side, Price::from(price), Quantity::from(size), id)
1346 }
1347
1348 #[rstest]
1349 fn test_top_of_book_missing() {
1350 let cache = PyCache::from_rc(Rc::new(RefCell::new(Cache::default())));
1351
1352 assert_eq!(
1353 cache.py_top_of_book(InstrumentId::from("AUD/USD.SIM")),
1354 None
1355 );
1356 }
1357
1358 #[rstest]
1359 #[case::empty(None)]
1360 #[case::bid_only(Some(OrderSide::Buy))]
1361 #[case::ask_only(Some(OrderSide::Sell))]
1362 fn test_top_of_book_incomplete(#[case] side: Option<OrderSide>) {
1363 let instrument_id = InstrumentId::from("AUD/USD.SIM");
1364 let mut book = OrderBook::new(instrument_id, BookType::L2_MBP);
1365 if let Some(side) = side {
1366 book.add(book_order(side, "0.70000", "10", 1), 0, 1, 1.into());
1367 }
1368 let mut cache = Cache::default();
1369 cache.add_order_book(book).unwrap();
1370 let cache = PyCache::from_rc(Rc::new(RefCell::new(cache)));
1371
1372 assert_eq!(cache.py_top_of_book(instrument_id), None);
1373 }
1374
1375 #[rstest]
1376 fn test_top_of_book_python_values_and_resident_updates() {
1377 let instrument_id = InstrumentId::from("AUD/USD.SIM");
1378 let mut book = OrderBook::new(instrument_id, BookType::L2_MBP);
1379 book.add(
1380 book_order(OrderSide::Buy, "0.70000", "10", 1),
1381 0,
1382 1,
1383 1.into(),
1384 );
1385 book.add(
1386 book_order(OrderSide::Sell, "0.70010", "20", 2),
1387 0,
1388 2,
1389 2.into(),
1390 );
1391 let mut cache = Cache::default();
1392 cache.add_order_book(book).unwrap();
1393 let cache = Rc::new(RefCell::new(cache));
1394
1395 Python::initialize();
1396 Python::attach(|py| {
1397 let py_cache = Py::new(py, PyCache::from_rc(cache.clone())).unwrap();
1398 let original = py_cache
1399 .call_method1(py, "top_of_book", (instrument_id,))
1400 .unwrap();
1401 let expected = (
1402 Price::from("0.70000"),
1403 Quantity::from("10"),
1404 Price::from("0.70010"),
1405 Quantity::from("20"),
1406 );
1407 assert_eq!(
1408 original
1409 .extract::<(Price, Quantity, Price, Quantity)>(py)
1410 .unwrap(),
1411 expected
1412 );
1413
1414 {
1415 let mut cache = cache.borrow_mut();
1416 let book = cache.order_book_mut(&instrument_id).unwrap();
1417 book.update(
1418 book_order(OrderSide::Buy, "0.70000", "15", 1),
1419 0,
1420 3,
1421 3.into(),
1422 );
1423 book.add(
1424 book_order(OrderSide::Sell, "0.70005", "25", 3),
1425 0,
1426 4,
1427 4.into(),
1428 );
1429 }
1430 let updated = py_cache
1431 .call_method1(py, "top_of_book", (instrument_id,))
1432 .unwrap()
1433 .extract::<(Price, Quantity, Price, Quantity)>(py)
1434 .unwrap();
1435 assert_eq!(
1436 updated,
1437 (
1438 Price::from("0.70000"),
1439 Quantity::from("15"),
1440 Price::from("0.70005"),
1441 Quantity::from("25"),
1442 )
1443 );
1444 assert_eq!(
1445 original
1446 .extract::<(Price, Quantity, Price, Quantity)>(py)
1447 .unwrap(),
1448 expected
1449 );
1450
1451 cache
1452 .borrow_mut()
1453 .order_book_mut(&instrument_id)
1454 .unwrap()
1455 .clear_asks(5, 5.into());
1456 assert!(
1457 py_cache
1458 .call_method1(py, "top_of_book", (instrument_id,))
1459 .unwrap()
1460 .is_none(py)
1461 );
1462 });
1463 }
1464
1465 #[rstest]
1466 fn test_top_of_book_l3_returns_first_order_sizes() {
1467 let instrument_id = InstrumentId::from("AUD/USD.SIM");
1468 let mut book = OrderBook::new(instrument_id, BookType::L3_MBO);
1469 let first_bid = book_order(OrderSide::Buy, "0.70000", "10", 1);
1470 let first_ask = book_order(OrderSide::Sell, "0.70010", "20", 3);
1471 book.add(first_bid, 0, 1, 1.into());
1472 book.add(
1473 book_order(OrderSide::Buy, "0.70000", "30", 2),
1474 0,
1475 2,
1476 2.into(),
1477 );
1478 book.add(first_ask, 0, 3, 3.into());
1479 book.add(
1480 book_order(OrderSide::Sell, "0.70010", "40", 4),
1481 0,
1482 4,
1483 4.into(),
1484 );
1485 let mut cache = Cache::default();
1486 cache.add_order_book(book).unwrap();
1487 let cache = PyCache::from_rc(Rc::new(RefCell::new(cache)));
1488
1489 assert_eq!(
1490 cache.py_top_of_book(instrument_id),
1491 Some((
1492 Price::from("0.70000"),
1493 Quantity::from("10"),
1494 Price::from("0.70010"),
1495 Quantity::from("20"),
1496 ))
1497 );
1498
1499 {
1500 let mut inner = cache.0.borrow_mut();
1501 let book = inner.order_book_mut(&instrument_id).unwrap();
1502 book.delete(first_bid, 0, 5, 5.into());
1503 book.delete(first_ask, 0, 6, 6.into());
1504 }
1505 assert_eq!(
1506 cache.py_top_of_book(instrument_id),
1507 Some((
1508 Price::from("0.70000"),
1509 Quantity::from("30"),
1510 Price::from("0.70010"),
1511 Quantity::from("40"),
1512 ))
1513 );
1514 }
1515
1516 fn create_order_list() -> OrderList {
1517 OrderList::new(
1518 OrderListId::from("OL-001"),
1519 InstrumentId::from("AUD/USD.SIM"),
1520 StrategyId::from("S-001"),
1521 vec![ClientOrderId::from("O-001")],
1522 UnixNanos::from(42_u64),
1523 )
1524 }
1525
1526 #[rstest]
1527 fn test_order_list_queries_preserve_concrete_type() {
1528 let order_list = create_order_list();
1529 let order_list_id = order_list.id;
1530 let cache = Rc::new(RefCell::new(Cache::default()));
1531 cache
1532 .borrow_mut()
1533 .add_order_list(order_list.clone())
1534 .unwrap();
1535 let py_cache = PyCache::from_rc(cache);
1536
1537 assert_eq!(
1538 py_cache.py_order_list(order_list_id),
1539 Some(order_list.clone()),
1540 );
1541 assert_eq!(
1542 py_cache.py_order_lists(None, None, None, None),
1543 vec![order_list],
1544 );
1545 }
1546
1547 #[rstest]
1548 fn test_add_instrument_close_replaces_existing() {
1549 let first = stub_instrument_close();
1550 let replacement = InstrumentClose::new(
1551 first.instrument_id,
1552 Price::from("0.00000"),
1553 InstrumentCloseType::EndOfSession,
1554 UnixNanos::from(3_u64),
1555 UnixNanos::from(4_u64),
1556 );
1557 let mut py_cache = PyCache::from_rc(Rc::new(RefCell::new(Cache::default())));
1558
1559 py_cache.py_add_instrument_close(first).unwrap();
1560 py_cache.py_add_instrument_close(replacement).unwrap();
1561
1562 assert_eq!(
1563 py_cache.py_instrument_close(first.instrument_id),
1564 Some(replacement)
1565 );
1566 }
1567
1568 #[rstest]
1569 fn test_py_cache_constructor_returns_value_error_for_invalid_config() {
1570 Python::initialize();
1571 let config = CacheConfig {
1572 tick_capacity: 0,
1573 ..Default::default()
1574 };
1575
1576 let err = PyCache::py_new(Some(config)).expect_err("invalid capacity must be rejected");
1577
1578 Python::attach(|py| assert!(err.is_instance_of::<PyValueError>(py)));
1579 }
1580
1581 #[rstest]
1582 fn test_native_cache_binding_returns_value_error_for_invalid_config() {
1583 Python::initialize();
1584 let config = CacheConfig {
1585 bar_capacity: 0,
1586 ..Default::default()
1587 };
1588
1589 let err = Cache::py_new(Some(config)).expect_err("invalid capacity must be rejected");
1590
1591 Python::attach(|py| assert!(err.is_instance_of::<PyValueError>(py)));
1592 }
1593}
1594
1595#[cfg(feature = "defi")]
1596#[pymethods]
1597#[pyo3_stub_gen::derive::gen_stub_pymethods]
1598impl PyCache {
1599 #[pyo3(name = "pool")]
1600 fn py_pool(&self, instrument_id: InstrumentId) -> Option<Pool> {
1601 self.0
1602 .try_borrow()
1603 .ok()
1604 .and_then(|cache| cache.pool(&instrument_id).cloned())
1605 }
1606
1607 #[pyo3(name = "pool_profiler")]
1608 fn py_pool_profiler(&self, instrument_id: InstrumentId) -> Option<PoolProfiler> {
1609 self.0
1610 .try_borrow()
1611 .ok()
1612 .and_then(|cache| cache.pool_profiler(&instrument_id).cloned())
1613 }
1614}
1615
1616#[pymethods]
1617#[pyo3_stub_gen::derive::gen_stub_pymethods]
1618impl CacheConfig {
1619 #[new]
1621 #[expect(clippy::too_many_arguments)]
1622 #[pyo3(signature = (
1623 encoding=None,
1624 timestamps_as_iso8601=None,
1625 buffer_interval_ms=None,
1626 bulk_read_batch_size=None,
1627 use_trader_prefix=None,
1628 use_instance_id=None,
1629 flush_on_start=None,
1630 drop_instruments_on_reset=None,
1631 tick_capacity=None,
1632 bar_capacity=None,
1633 save_market_data=None,
1634 persist_account_events=None,
1635 ))]
1636 fn py_new(
1637 encoding: Option<SerializationEncoding>,
1638 timestamps_as_iso8601: Option<bool>,
1639 buffer_interval_ms: Option<usize>,
1640 bulk_read_batch_size: Option<usize>,
1641 use_trader_prefix: Option<bool>,
1642 use_instance_id: Option<bool>,
1643 flush_on_start: Option<bool>,
1644 drop_instruments_on_reset: Option<bool>,
1645 tick_capacity: Option<usize>,
1646 bar_capacity: Option<usize>,
1647 save_market_data: Option<bool>,
1648 persist_account_events: Option<bool>,
1649 ) -> PyResult<Self> {
1650 let config = Self {
1651 encoding: encoding.unwrap_or_default(),
1652 timestamps_as_iso8601: timestamps_as_iso8601.unwrap_or(false),
1653 buffer_interval_ms,
1654 bulk_read_batch_size,
1655 use_trader_prefix: use_trader_prefix.unwrap_or(true),
1656 use_instance_id: use_instance_id.unwrap_or(false),
1657 flush_on_start: flush_on_start.unwrap_or(false),
1658 drop_instruments_on_reset: drop_instruments_on_reset.unwrap_or(true),
1659 tick_capacity: tick_capacity.unwrap_or(10_000),
1660 bar_capacity: bar_capacity.unwrap_or(10_000),
1661 persist_account_events: persist_account_events.unwrap_or(true),
1662 save_market_data: save_market_data.unwrap_or(false),
1663 };
1664 config.validate().map_err(config_error_to_pyvalue_err)?;
1665 Ok(config)
1666 }
1667
1668 fn __str__(&self) -> String {
1669 format!("{self:?}")
1670 }
1671
1672 fn __repr__(&self) -> String {
1673 format!("{self:?}")
1674 }
1675
1676 #[getter]
1677 fn encoding(&self) -> SerializationEncoding {
1678 self.encoding
1679 }
1680
1681 #[getter]
1682 fn timestamps_as_iso8601(&self) -> bool {
1683 self.timestamps_as_iso8601
1684 }
1685
1686 #[getter]
1687 fn buffer_interval_ms(&self) -> Option<usize> {
1688 self.buffer_interval_ms
1689 }
1690
1691 #[getter]
1692 fn bulk_read_batch_size(&self) -> Option<usize> {
1693 self.bulk_read_batch_size
1694 }
1695
1696 #[getter]
1697 fn use_trader_prefix(&self) -> bool {
1698 self.use_trader_prefix
1699 }
1700
1701 #[getter]
1702 fn use_instance_id(&self) -> bool {
1703 self.use_instance_id
1704 }
1705
1706 #[getter]
1707 fn flush_on_start(&self) -> bool {
1708 self.flush_on_start
1709 }
1710
1711 #[getter]
1712 fn drop_instruments_on_reset(&self) -> bool {
1713 self.drop_instruments_on_reset
1714 }
1715
1716 #[getter]
1717 fn tick_capacity(&self) -> usize {
1718 self.tick_capacity
1719 }
1720
1721 #[getter]
1722 fn bar_capacity(&self) -> usize {
1723 self.bar_capacity
1724 }
1725
1726 #[getter]
1727 fn persist_account_events(&self) -> bool {
1728 self.persist_account_events
1729 }
1730
1731 #[getter]
1732 fn save_market_data(&self) -> bool {
1733 self.save_market_data
1734 }
1735}
1736
1737#[pymethods]
1738impl Cache {
1739 #[new]
1741 fn py_new(config: Option<CacheConfig>) -> PyResult<Self> {
1742 Self::try_new(config, None).map_err(config_error_to_pyvalue_err)
1743 }
1744
1745 fn __repr__(&self) -> String {
1746 format!("{self:?}")
1747 }
1748
1749 #[pyo3(name = "reset")]
1757 fn py_reset(&mut self) {
1758 self.reset();
1759 }
1760
1761 #[pyo3(name = "dispose")]
1765 fn py_dispose(&mut self) {
1766 self.dispose();
1767 }
1768
1769 #[pyo3(name = "purge_closed_orders", signature = (ts_now, buffer_secs=0))]
1775 fn py_purge_closed_orders(&mut self, ts_now: u64, buffer_secs: u64) {
1776 self.purge_closed_orders(ts_now.into(), buffer_secs);
1777 }
1778
1779 #[pyo3(name = "purge_closed_positions", signature = (ts_now, buffer_secs=0))]
1781 fn py_purge_closed_positions(&mut self, ts_now: u64, buffer_secs: u64) {
1782 self.purge_closed_positions(ts_now.into(), buffer_secs);
1783 }
1784
1785 #[pyo3(name = "purge_order")]
1789 fn py_purge_order(&mut self, client_order_id: ClientOrderId) {
1790 self.purge_order(client_order_id);
1791 }
1792
1793 #[pyo3(name = "purge_position")]
1797 fn py_purge_position(&mut self, position_id: PositionId) {
1798 self.purge_position(position_id);
1799 }
1800
1801 #[pyo3(name = "purge_instrument")]
1806 fn py_purge_instrument(&mut self, instrument_id: InstrumentId) {
1807 self.purge_instrument(instrument_id);
1808 }
1809
1810 #[pyo3(name = "purge_account_events", signature = (ts_now, lookback_secs=0))]
1815 fn py_purge_account_events(&mut self, ts_now: u64, lookback_secs: u64) {
1816 self.purge_account_events(ts_now.into(), lookback_secs);
1817 }
1818
1819 #[pyo3(name = "add_currency")]
1825 fn py_add_currency(&mut self, currency: Currency) -> PyResult<()> {
1826 self.add_currency(currency).map_err(to_pyvalue_err)
1827 }
1828
1829 #[pyo3(name = "add_instrument")]
1835 fn py_add_instrument(&mut self, py: Python, instrument: Py<PyAny>) -> PyResult<()> {
1836 let instrument_any = pyobject_to_instrument_any(py, instrument)?;
1837 self.add_instrument(instrument_any).map_err(to_pyvalue_err)
1838 }
1839
1840 #[pyo3(name = "instrument")]
1842 fn py_instrument(
1843 &self,
1844 py: Python,
1845 instrument_id: InstrumentId,
1846 ) -> PyResult<Option<Py<PyAny>>> {
1847 match self.instrument(&instrument_id) {
1848 Some(instrument) => Ok(Some(instrument_any_to_pyobject(py, instrument.clone())?)),
1849 None => Ok(None),
1850 }
1851 }
1852
1853 #[pyo3(name = "instrument_ids")]
1855 fn py_instrument_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
1856 self.instrument_ids(venue.as_ref())
1857 .into_iter()
1858 .copied()
1859 .collect()
1860 }
1861
1862 #[pyo3(name = "instruments")]
1864 fn py_instruments(&self, py: Python, venue: Option<Venue>) -> PyResult<Vec<Py<PyAny>>> {
1865 let mut py_instruments = Vec::new();
1866
1867 if let Some(venue) = venue {
1868 let instruments = self.instruments(&venue, None);
1869 for instrument in instruments {
1870 py_instruments.push(instrument_any_to_pyobject(py, (*instrument).clone())?);
1871 }
1872 } else {
1873 let instrument_ids = self.instrument_ids(None);
1874 for instrument_id in instrument_ids {
1875 if let Some(instrument) = self.instrument(instrument_id) {
1876 py_instruments.push(instrument_any_to_pyobject(py, instrument.clone())?);
1877 }
1878 }
1879 }
1880
1881 Ok(py_instruments)
1882 }
1883
1884 #[pyo3(name = "add_order")]
1899 fn py_add_order(
1900 &mut self,
1901 py: Python,
1902 order: Py<PyAny>,
1903 position_id: Option<PositionId>,
1904 client_id: Option<ClientId>,
1905 replace_existing: Option<bool>,
1906 ) -> PyResult<()> {
1907 let order_any = pyobject_to_order_any(py, order)?;
1908 self.add_order(
1909 order_any,
1910 position_id,
1911 client_id,
1912 replace_existing.unwrap_or(false),
1913 )
1914 .map_err(to_pyvalue_err)
1915 }
1916
1917 #[pyo3(name = "order")]
1921 fn py_order(&self, py: Python, client_order_id: ClientOrderId) -> PyResult<Option<Py<PyAny>>> {
1922 match self.order(&client_order_id) {
1923 Some(order) => Ok(Some(order_any_to_pyobject(py, order.clone())?)),
1924 None => Ok(None),
1925 }
1926 }
1927
1928 #[pyo3(name = "order_exists")]
1930 fn py_order_exists(&self, client_order_id: ClientOrderId) -> bool {
1931 self.order_exists(&client_order_id)
1932 }
1933
1934 #[pyo3(name = "is_order_open")]
1936 fn py_is_order_open(&self, client_order_id: ClientOrderId) -> bool {
1937 self.is_order_open(&client_order_id)
1938 }
1939
1940 #[pyo3(name = "is_order_closed")]
1942 fn py_is_order_closed(&self, client_order_id: ClientOrderId) -> bool {
1943 self.is_order_closed(&client_order_id)
1944 }
1945
1946 #[pyo3(name = "is_order_active_local")]
1951 fn py_is_order_active_local(&self, client_order_id: ClientOrderId) -> bool {
1952 self.is_order_active_local(&client_order_id)
1953 }
1954
1955 #[pyo3(name = "orders_active_local")]
1959 fn py_orders_active_local(
1960 &self,
1961 py: Python,
1962 venue: Option<Venue>,
1963 instrument_id: Option<InstrumentId>,
1964 strategy_id: Option<StrategyId>,
1965 account_id: Option<AccountId>,
1966 side: Option<OrderSide>,
1967 ) -> PyResult<Vec<Py<PyAny>>> {
1968 self.orders_active_local(
1969 venue.as_ref(),
1970 instrument_id.as_ref(),
1971 strategy_id.as_ref(),
1972 account_id.as_ref(),
1973 side,
1974 )
1975 .into_iter()
1976 .map(|order| order_any_to_pyobject(py, order.clone()))
1977 .collect()
1978 }
1979
1980 #[pyo3(name = "orders_active_local_count")]
1985 fn py_orders_active_local_count(
1986 &self,
1987 venue: Option<Venue>,
1988 instrument_id: Option<InstrumentId>,
1989 strategy_id: Option<StrategyId>,
1990 account_id: Option<AccountId>,
1991 side: Option<OrderSide>,
1992 ) -> usize {
1993 self.orders_active_local_count(
1994 venue.as_ref(),
1995 instrument_id.as_ref(),
1996 strategy_id.as_ref(),
1997 account_id.as_ref(),
1998 side,
1999 )
2000 }
2001
2002 #[pyo3(name = "orders_open_count")]
2004 fn py_orders_open_count(
2005 &self,
2006 venue: Option<Venue>,
2007 instrument_id: Option<InstrumentId>,
2008 strategy_id: Option<StrategyId>,
2009 account_id: Option<AccountId>,
2010 side: Option<OrderSide>,
2011 ) -> usize {
2012 self.orders_open_count(
2013 venue.as_ref(),
2014 instrument_id.as_ref(),
2015 strategy_id.as_ref(),
2016 account_id.as_ref(),
2017 side,
2018 )
2019 }
2020
2021 #[pyo3(name = "orders_closed_count")]
2023 fn py_orders_closed_count(
2024 &self,
2025 venue: Option<Venue>,
2026 instrument_id: Option<InstrumentId>,
2027 strategy_id: Option<StrategyId>,
2028 account_id: Option<AccountId>,
2029 side: Option<OrderSide>,
2030 ) -> usize {
2031 self.orders_closed_count(
2032 venue.as_ref(),
2033 instrument_id.as_ref(),
2034 strategy_id.as_ref(),
2035 account_id.as_ref(),
2036 side,
2037 )
2038 }
2039
2040 #[pyo3(name = "orders_total_count")]
2042 fn py_orders_total_count(
2043 &self,
2044 venue: Option<Venue>,
2045 instrument_id: Option<InstrumentId>,
2046 strategy_id: Option<StrategyId>,
2047 account_id: Option<AccountId>,
2048 side: Option<OrderSide>,
2049 ) -> usize {
2050 self.orders_total_count(
2051 venue.as_ref(),
2052 instrument_id.as_ref(),
2053 strategy_id.as_ref(),
2054 account_id.as_ref(),
2055 side,
2056 )
2057 }
2058
2059 #[pyo3(name = "add_position")]
2070 #[expect(clippy::needless_pass_by_value)]
2071 fn py_add_position(
2072 &mut self,
2073 py: Python,
2074 position: Py<PyAny>,
2075 oms_type: OmsType,
2076 ) -> PyResult<()> {
2077 let position_obj = position.extract::<Position>(py)?;
2078 self.add_position(&position_obj, oms_type)
2079 .map_err(to_pyvalue_err)
2080 }
2081
2082 #[pyo3(name = "snapshot_position")]
2094 #[expect(clippy::needless_pass_by_value)]
2095 fn py_snapshot_position(&mut self, py: Python, position: Py<PyAny>) -> PyResult<()> {
2096 let position_obj = position.extract::<Position>(py)?;
2097 self.snapshot_position(&position_obj)
2098 .map_err(to_pyvalue_err)
2099 }
2100
2101 #[pyo3(name = "position")]
2105 fn py_position(&self, py: Python, position_id: PositionId) -> PyResult<Option<Py<PyAny>>> {
2106 match self.position(&position_id) {
2107 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
2108 None => Ok(None),
2109 }
2110 }
2111
2112 #[pyo3(name = "position_exists")]
2114 fn py_position_exists(&self, position_id: PositionId) -> bool {
2115 self.position_exists(&position_id)
2116 }
2117
2118 #[pyo3(name = "is_position_open")]
2120 fn py_is_position_open(&self, position_id: PositionId) -> bool {
2121 self.is_position_open(&position_id)
2122 }
2123
2124 #[pyo3(name = "is_position_closed")]
2126 fn py_is_position_closed(&self, position_id: PositionId) -> bool {
2127 self.is_position_closed(&position_id)
2128 }
2129
2130 #[pyo3(name = "positions_open_count")]
2132 fn py_positions_open_count(
2133 &self,
2134 venue: Option<Venue>,
2135 instrument_id: Option<InstrumentId>,
2136 strategy_id: Option<StrategyId>,
2137 account_id: Option<AccountId>,
2138 side: Option<PositionSide>,
2139 ) -> usize {
2140 self.positions_open_count(
2141 venue.as_ref(),
2142 instrument_id.as_ref(),
2143 strategy_id.as_ref(),
2144 account_id.as_ref(),
2145 side,
2146 )
2147 }
2148
2149 #[pyo3(name = "positions_closed_count")]
2151 fn py_positions_closed_count(
2152 &self,
2153 venue: Option<Venue>,
2154 instrument_id: Option<InstrumentId>,
2155 strategy_id: Option<StrategyId>,
2156 account_id: Option<AccountId>,
2157 side: Option<PositionSide>,
2158 ) -> usize {
2159 self.positions_closed_count(
2160 venue.as_ref(),
2161 instrument_id.as_ref(),
2162 strategy_id.as_ref(),
2163 account_id.as_ref(),
2164 side,
2165 )
2166 }
2167
2168 #[pyo3(name = "positions_total_count")]
2170 fn py_positions_total_count(
2171 &self,
2172 venue: Option<Venue>,
2173 instrument_id: Option<InstrumentId>,
2174 strategy_id: Option<StrategyId>,
2175 account_id: Option<AccountId>,
2176 side: Option<PositionSide>,
2177 ) -> usize {
2178 self.positions_total_count(
2179 venue.as_ref(),
2180 instrument_id.as_ref(),
2181 strategy_id.as_ref(),
2182 account_id.as_ref(),
2183 side,
2184 )
2185 }
2186
2187 #[pyo3(name = "add_quote")]
2193 fn py_add_quote(&mut self, quote: QuoteTick) -> PyResult<()> {
2194 self.add_quote(quote).map_err(to_pyvalue_err)
2195 }
2196
2197 #[pyo3(name = "add_trade")]
2203 fn py_add_trade(&mut self, trade: TradeTick) -> PyResult<()> {
2204 self.add_trade(trade).map_err(to_pyvalue_err)
2205 }
2206
2207 #[pyo3(name = "add_bar")]
2216 fn py_add_bar(&mut self, bar: Bar) -> PyResult<()> {
2217 self.add_bar(bar).map_err(to_pyvalue_err)
2218 }
2219
2220 #[pyo3(name = "quote")]
2222 fn py_quote(&self, instrument_id: InstrumentId) -> Option<QuoteTick> {
2223 self.quote(&instrument_id).copied()
2224 }
2225
2226 #[pyo3(name = "trade")]
2228 fn py_trade(&self, instrument_id: InstrumentId) -> Option<TradeTick> {
2229 self.trade(&instrument_id).copied()
2230 }
2231
2232 #[pyo3(name = "bar")]
2234 fn py_bar(&self, bar_type: BarType) -> Option<Bar> {
2235 self.bar(&bar_type).copied()
2236 }
2237
2238 #[pyo3(name = "quotes")]
2243 fn py_quotes(&self, instrument_id: InstrumentId) -> Option<Vec<QuoteTick>> {
2244 self.quotes(&instrument_id)
2245 }
2246
2247 #[pyo3(name = "trades")]
2252 fn py_trades(&self, instrument_id: InstrumentId) -> Option<Vec<TradeTick>> {
2253 self.trades(&instrument_id)
2254 }
2255
2256 #[pyo3(name = "bars")]
2260 fn py_bars(&self, bar_type: BarType) -> Option<Vec<Bar>> {
2261 self.bars(&bar_type)
2262 }
2263
2264 #[pyo3(name = "has_quote_ticks")]
2266 fn py_has_quote_ticks(&self, instrument_id: InstrumentId) -> bool {
2267 self.has_quote_ticks(&instrument_id)
2268 }
2269
2270 #[pyo3(name = "has_trade_ticks")]
2272 fn py_has_trade_ticks(&self, instrument_id: InstrumentId) -> bool {
2273 self.has_trade_ticks(&instrument_id)
2274 }
2275
2276 #[pyo3(name = "has_mark_prices")]
2278 fn py_has_mark_prices(&self, instrument_id: InstrumentId) -> bool {
2279 self.has_mark_prices(&instrument_id)
2280 }
2281
2282 #[pyo3(name = "has_index_prices")]
2284 fn py_has_index_prices(&self, instrument_id: InstrumentId) -> bool {
2285 self.has_index_prices(&instrument_id)
2286 }
2287
2288 #[pyo3(name = "has_funding_rates")]
2290 fn py_has_funding_rates(&self, instrument_id: InstrumentId) -> bool {
2291 self.has_funding_rates(&instrument_id)
2292 }
2293
2294 #[pyo3(name = "has_instrument_statuses")]
2296 fn py_has_instrument_statuses(&self, instrument_id: InstrumentId) -> bool {
2297 self.has_instrument_statuses(&instrument_id)
2298 }
2299
2300 #[pyo3(name = "has_instrument_close")]
2302 fn py_has_instrument_close(&self, instrument_id: InstrumentId) -> bool {
2303 self.has_instrument_close(&instrument_id)
2304 }
2305
2306 #[pyo3(name = "has_bars")]
2308 fn py_has_bars(&self, bar_type: BarType) -> bool {
2309 self.has_bars(&bar_type)
2310 }
2311
2312 #[pyo3(name = "quote_count")]
2314 fn py_quote_count(&self, instrument_id: InstrumentId) -> usize {
2315 self.quote_count(&instrument_id)
2316 }
2317
2318 #[pyo3(name = "trade_count")]
2320 fn py_trade_count(&self, instrument_id: InstrumentId) -> usize {
2321 self.trade_count(&instrument_id)
2322 }
2323
2324 #[pyo3(name = "mark_price_count")]
2326 fn py_mark_price_count(&self, instrument_id: InstrumentId) -> usize {
2327 self.mark_price_count(&instrument_id)
2328 }
2329
2330 #[pyo3(name = "index_price_count")]
2332 fn py_index_price_count(&self, instrument_id: InstrumentId) -> usize {
2333 self.index_price_count(&instrument_id)
2334 }
2335
2336 #[pyo3(name = "funding_rate_count")]
2338 fn py_funding_rate_count(&self, instrument_id: InstrumentId) -> usize {
2339 self.funding_rate_count(&instrument_id)
2340 }
2341
2342 #[pyo3(name = "instrument_status_count")]
2344 fn py_instrument_status_count(&self, instrument_id: InstrumentId) -> usize {
2345 self.instrument_status_count(&instrument_id)
2346 }
2347
2348 #[pyo3(name = "bar_count")]
2350 fn py_bar_count(&self, bar_type: BarType) -> usize {
2351 self.bar_count(&bar_type)
2352 }
2353
2354 #[pyo3(name = "mark_price")]
2356 fn py_mark_price(&self, instrument_id: InstrumentId) -> Option<MarkPriceUpdate> {
2357 self.mark_price(&instrument_id).copied()
2358 }
2359
2360 #[pyo3(name = "mark_prices")]
2365 fn py_mark_prices(&self, instrument_id: InstrumentId) -> Option<Vec<MarkPriceUpdate>> {
2366 self.mark_prices(&instrument_id)
2367 }
2368
2369 #[pyo3(name = "index_price")]
2371 fn py_index_price(&self, instrument_id: InstrumentId) -> Option<IndexPriceUpdate> {
2372 self.index_price(&instrument_id).copied()
2373 }
2374
2375 #[pyo3(name = "index_prices")]
2380 fn py_index_prices(&self, instrument_id: InstrumentId) -> Option<Vec<IndexPriceUpdate>> {
2381 self.index_prices(&instrument_id)
2382 }
2383
2384 #[pyo3(name = "funding_rate")]
2386 fn py_funding_rate(&self, instrument_id: InstrumentId) -> Option<FundingRateUpdate> {
2387 self.funding_rate(&instrument_id).copied()
2388 }
2389
2390 #[pyo3(name = "funding_rates")]
2395 fn py_funding_rates(&self, instrument_id: InstrumentId) -> Option<Vec<FundingRateUpdate>> {
2396 self.funding_rates(&instrument_id)
2397 }
2398
2399 #[pyo3(name = "instrument_status")]
2401 fn py_instrument_status(&self, instrument_id: InstrumentId) -> Option<InstrumentStatus> {
2402 self.instrument_status(&instrument_id).copied()
2403 }
2404
2405 #[pyo3(name = "instrument_statuses")]
2410 fn py_instrument_statuses(&self, instrument_id: InstrumentId) -> Option<Vec<InstrumentStatus>> {
2411 self.instrument_statuses(&instrument_id)
2412 }
2413
2414 #[pyo3(name = "instrument_close")]
2418 fn py_instrument_close(&self, instrument_id: InstrumentId) -> Option<InstrumentClose> {
2419 self.instrument_close(&instrument_id).copied()
2420 }
2421
2422 #[pyo3(name = "order_book")]
2426 fn py_order_book(&self, instrument_id: InstrumentId) -> Option<OrderBook> {
2427 self.order_book(&instrument_id).cloned()
2428 }
2429
2430 #[pyo3(name = "has_order_book")]
2432 fn py_has_order_book(&self, instrument_id: InstrumentId) -> bool {
2433 self.has_order_book(&instrument_id)
2434 }
2435
2436 #[pyo3(name = "book_update_count")]
2438 fn py_book_update_count(&self, instrument_id: InstrumentId) -> usize {
2439 self.book_update_count(&instrument_id)
2440 }
2441
2442 #[pyo3(name = "synthetic")]
2444 fn py_synthetic(&self, instrument_id: InstrumentId) -> Option<SyntheticInstrument> {
2445 self.synthetic(&instrument_id).cloned()
2446 }
2447
2448 #[pyo3(name = "synthetic_ids")]
2450 fn py_synthetic_ids(&self) -> Vec<InstrumentId> {
2451 self.synthetic_ids().into_iter().copied().collect()
2452 }
2453
2454 #[pyo3(name = "client_order_ids")]
2456 fn py_client_order_ids(
2457 &self,
2458 venue: Option<Venue>,
2459 instrument_id: Option<InstrumentId>,
2460 strategy_id: Option<StrategyId>,
2461 account_id: Option<AccountId>,
2462 ) -> Vec<ClientOrderId> {
2463 self.client_order_ids(
2464 venue.as_ref(),
2465 instrument_id.as_ref(),
2466 strategy_id.as_ref(),
2467 account_id.as_ref(),
2468 )
2469 .into_iter()
2470 .collect()
2471 }
2472
2473 #[pyo3(name = "client_order_ids_open")]
2475 fn py_client_order_ids_open(
2476 &self,
2477 venue: Option<Venue>,
2478 instrument_id: Option<InstrumentId>,
2479 strategy_id: Option<StrategyId>,
2480 account_id: Option<AccountId>,
2481 ) -> Vec<ClientOrderId> {
2482 self.client_order_ids_open(
2483 venue.as_ref(),
2484 instrument_id.as_ref(),
2485 strategy_id.as_ref(),
2486 account_id.as_ref(),
2487 )
2488 .into_iter()
2489 .collect()
2490 }
2491
2492 #[pyo3(name = "client_order_ids_closed")]
2494 fn py_client_order_ids_closed(
2495 &self,
2496 venue: Option<Venue>,
2497 instrument_id: Option<InstrumentId>,
2498 strategy_id: Option<StrategyId>,
2499 account_id: Option<AccountId>,
2500 ) -> Vec<ClientOrderId> {
2501 self.client_order_ids_closed(
2502 venue.as_ref(),
2503 instrument_id.as_ref(),
2504 strategy_id.as_ref(),
2505 account_id.as_ref(),
2506 )
2507 .into_iter()
2508 .collect()
2509 }
2510
2511 #[pyo3(name = "client_order_ids_emulated")]
2513 fn py_client_order_ids_emulated(
2514 &self,
2515 venue: Option<Venue>,
2516 instrument_id: Option<InstrumentId>,
2517 strategy_id: Option<StrategyId>,
2518 account_id: Option<AccountId>,
2519 ) -> Vec<ClientOrderId> {
2520 self.client_order_ids_emulated(
2521 venue.as_ref(),
2522 instrument_id.as_ref(),
2523 strategy_id.as_ref(),
2524 account_id.as_ref(),
2525 )
2526 .into_iter()
2527 .collect()
2528 }
2529
2530 #[pyo3(name = "client_order_ids_inflight")]
2532 fn py_client_order_ids_inflight(
2533 &self,
2534 venue: Option<Venue>,
2535 instrument_id: Option<InstrumentId>,
2536 strategy_id: Option<StrategyId>,
2537 account_id: Option<AccountId>,
2538 ) -> Vec<ClientOrderId> {
2539 self.client_order_ids_inflight(
2540 venue.as_ref(),
2541 instrument_id.as_ref(),
2542 strategy_id.as_ref(),
2543 account_id.as_ref(),
2544 )
2545 .into_iter()
2546 .collect()
2547 }
2548
2549 #[pyo3(name = "position_ids")]
2551 fn py_position_ids(
2552 &self,
2553 venue: Option<Venue>,
2554 instrument_id: Option<InstrumentId>,
2555 strategy_id: Option<StrategyId>,
2556 account_id: Option<AccountId>,
2557 ) -> Vec<PositionId> {
2558 self.position_ids(
2559 venue.as_ref(),
2560 instrument_id.as_ref(),
2561 strategy_id.as_ref(),
2562 account_id.as_ref(),
2563 )
2564 .into_iter()
2565 .collect()
2566 }
2567
2568 #[pyo3(name = "position_open_ids")]
2570 fn py_position_open_ids(
2571 &self,
2572 venue: Option<Venue>,
2573 instrument_id: Option<InstrumentId>,
2574 strategy_id: Option<StrategyId>,
2575 account_id: Option<AccountId>,
2576 ) -> Vec<PositionId> {
2577 self.position_open_ids(
2578 venue.as_ref(),
2579 instrument_id.as_ref(),
2580 strategy_id.as_ref(),
2581 account_id.as_ref(),
2582 )
2583 .into_iter()
2584 .collect()
2585 }
2586
2587 #[pyo3(name = "position_closed_ids")]
2589 fn py_position_closed_ids(
2590 &self,
2591 venue: Option<Venue>,
2592 instrument_id: Option<InstrumentId>,
2593 strategy_id: Option<StrategyId>,
2594 account_id: Option<AccountId>,
2595 ) -> Vec<PositionId> {
2596 self.position_closed_ids(
2597 venue.as_ref(),
2598 instrument_id.as_ref(),
2599 strategy_id.as_ref(),
2600 account_id.as_ref(),
2601 )
2602 .into_iter()
2603 .collect()
2604 }
2605
2606 #[pyo3(name = "strategy_ids")]
2608 fn py_strategy_ids(&self) -> Vec<StrategyId> {
2609 self.strategy_ids().into_iter().collect()
2610 }
2611
2612 #[pyo3(name = "exec_algorithm_ids")]
2614 fn py_exec_algorithm_ids(&self) -> Vec<ExecAlgorithmId> {
2615 self.exec_algorithm_ids().into_iter().collect()
2616 }
2617
2618 #[pyo3(name = "client_order_id")]
2620 fn py_client_order_id(&self, venue_order_id: VenueOrderId) -> Option<ClientOrderId> {
2621 self.client_order_id(&venue_order_id).copied()
2622 }
2623
2624 #[pyo3(name = "venue_order_id")]
2626 fn py_venue_order_id(&self, client_order_id: ClientOrderId) -> Option<VenueOrderId> {
2627 self.venue_order_id(&client_order_id).copied()
2628 }
2629
2630 #[pyo3(name = "client_id")]
2632 fn py_client_id(&self, client_order_id: ClientOrderId) -> Option<ClientId> {
2633 self.client_id(&client_order_id).copied()
2634 }
2635
2636 #[pyo3(name = "orders")]
2640 fn py_orders(
2641 &self,
2642 py: Python,
2643 venue: Option<Venue>,
2644 instrument_id: Option<InstrumentId>,
2645 strategy_id: Option<StrategyId>,
2646 account_id: Option<AccountId>,
2647 side: Option<OrderSide>,
2648 ) -> PyResult<Vec<Py<PyAny>>> {
2649 self.orders(
2650 venue.as_ref(),
2651 instrument_id.as_ref(),
2652 strategy_id.as_ref(),
2653 account_id.as_ref(),
2654 side,
2655 )
2656 .into_iter()
2657 .map(|o| order_any_to_pyobject(py, o.clone()))
2658 .collect()
2659 }
2660
2661 #[pyo3(name = "orders_open")]
2665 fn py_orders_open(
2666 &self,
2667 py: Python,
2668 venue: Option<Venue>,
2669 instrument_id: Option<InstrumentId>,
2670 strategy_id: Option<StrategyId>,
2671 account_id: Option<AccountId>,
2672 side: Option<OrderSide>,
2673 ) -> PyResult<Vec<Py<PyAny>>> {
2674 self.orders_open(
2675 venue.as_ref(),
2676 instrument_id.as_ref(),
2677 strategy_id.as_ref(),
2678 account_id.as_ref(),
2679 side,
2680 )
2681 .into_iter()
2682 .map(|o| order_any_to_pyobject(py, o.clone()))
2683 .collect()
2684 }
2685
2686 #[pyo3(name = "orders_closed")]
2690 fn py_orders_closed(
2691 &self,
2692 py: Python,
2693 venue: Option<Venue>,
2694 instrument_id: Option<InstrumentId>,
2695 strategy_id: Option<StrategyId>,
2696 account_id: Option<AccountId>,
2697 side: Option<OrderSide>,
2698 ) -> PyResult<Vec<Py<PyAny>>> {
2699 self.orders_closed(
2700 venue.as_ref(),
2701 instrument_id.as_ref(),
2702 strategy_id.as_ref(),
2703 account_id.as_ref(),
2704 side,
2705 )
2706 .into_iter()
2707 .map(|o| order_any_to_pyobject(py, o.clone()))
2708 .collect()
2709 }
2710
2711 #[pyo3(name = "orders_emulated")]
2715 fn py_orders_emulated(
2716 &self,
2717 py: Python,
2718 venue: Option<Venue>,
2719 instrument_id: Option<InstrumentId>,
2720 strategy_id: Option<StrategyId>,
2721 account_id: Option<AccountId>,
2722 side: Option<OrderSide>,
2723 ) -> PyResult<Vec<Py<PyAny>>> {
2724 self.orders_emulated(
2725 venue.as_ref(),
2726 instrument_id.as_ref(),
2727 strategy_id.as_ref(),
2728 account_id.as_ref(),
2729 side,
2730 )
2731 .into_iter()
2732 .map(|o| order_any_to_pyobject(py, o.clone()))
2733 .collect()
2734 }
2735
2736 #[pyo3(name = "orders_inflight")]
2740 fn py_orders_inflight(
2741 &self,
2742 py: Python,
2743 venue: Option<Venue>,
2744 instrument_id: Option<InstrumentId>,
2745 strategy_id: Option<StrategyId>,
2746 account_id: Option<AccountId>,
2747 side: Option<OrderSide>,
2748 ) -> PyResult<Vec<Py<PyAny>>> {
2749 self.orders_inflight(
2750 venue.as_ref(),
2751 instrument_id.as_ref(),
2752 strategy_id.as_ref(),
2753 account_id.as_ref(),
2754 side,
2755 )
2756 .into_iter()
2757 .map(|o| order_any_to_pyobject(py, o.clone()))
2758 .collect()
2759 }
2760
2761 #[pyo3(name = "orders_for_position")]
2763 fn py_orders_for_position(
2764 &self,
2765 py: Python,
2766 position_id: PositionId,
2767 ) -> PyResult<Vec<Py<PyAny>>> {
2768 self.orders_for_position(&position_id)
2769 .into_iter()
2770 .map(|o| order_any_to_pyobject(py, o.clone()))
2771 .collect()
2772 }
2773
2774 #[pyo3(name = "is_order_emulated")]
2776 fn py_is_order_emulated(&self, client_order_id: ClientOrderId) -> bool {
2777 self.is_order_emulated(&client_order_id)
2778 }
2779
2780 #[pyo3(name = "is_order_inflight")]
2782 fn py_is_order_inflight(&self, client_order_id: ClientOrderId) -> bool {
2783 self.is_order_inflight(&client_order_id)
2784 }
2785
2786 #[pyo3(name = "is_order_pending_cancel_local")]
2788 fn py_is_order_pending_cancel_local(&self, client_order_id: ClientOrderId) -> bool {
2789 self.is_order_pending_cancel_local(&client_order_id)
2790 }
2791
2792 #[pyo3(name = "orders_emulated_count")]
2794 fn py_orders_emulated_count(
2795 &self,
2796 venue: Option<Venue>,
2797 instrument_id: Option<InstrumentId>,
2798 strategy_id: Option<StrategyId>,
2799 account_id: Option<AccountId>,
2800 side: Option<OrderSide>,
2801 ) -> usize {
2802 self.orders_emulated_count(
2803 venue.as_ref(),
2804 instrument_id.as_ref(),
2805 strategy_id.as_ref(),
2806 account_id.as_ref(),
2807 side,
2808 )
2809 }
2810
2811 #[pyo3(name = "orders_inflight_count")]
2813 fn py_orders_inflight_count(
2814 &self,
2815 venue: Option<Venue>,
2816 instrument_id: Option<InstrumentId>,
2817 strategy_id: Option<StrategyId>,
2818 account_id: Option<AccountId>,
2819 side: Option<OrderSide>,
2820 ) -> usize {
2821 self.orders_inflight_count(
2822 venue.as_ref(),
2823 instrument_id.as_ref(),
2824 strategy_id.as_ref(),
2825 account_id.as_ref(),
2826 side,
2827 )
2828 }
2829
2830 #[pyo3(name = "order_list")]
2832 fn py_order_list(&self, order_list_id: OrderListId) -> Option<OrderList> {
2833 self.order_list(&order_list_id).cloned()
2834 }
2835
2836 #[pyo3(name = "order_lists")]
2838 fn py_order_lists(
2839 &self,
2840 venue: Option<Venue>,
2841 instrument_id: Option<InstrumentId>,
2842 strategy_id: Option<StrategyId>,
2843 account_id: Option<AccountId>,
2844 ) -> Vec<OrderList> {
2845 self.order_lists(
2846 venue.as_ref(),
2847 instrument_id.as_ref(),
2848 strategy_id.as_ref(),
2849 account_id.as_ref(),
2850 )
2851 .into_iter()
2852 .cloned()
2853 .collect()
2854 }
2855
2856 #[pyo3(name = "order_list_exists")]
2858 fn py_order_list_exists(&self, order_list_id: OrderListId) -> bool {
2859 self.order_list_exists(&order_list_id)
2860 }
2861
2862 #[pyo3(name = "orders_for_exec_algorithm")]
2865 #[expect(clippy::too_many_arguments)]
2866 fn py_orders_for_exec_algorithm(
2867 &self,
2868 py: Python,
2869 exec_algorithm_id: ExecAlgorithmId,
2870 venue: Option<Venue>,
2871 instrument_id: Option<InstrumentId>,
2872 strategy_id: Option<StrategyId>,
2873 account_id: Option<AccountId>,
2874 side: Option<OrderSide>,
2875 ) -> PyResult<Vec<Py<PyAny>>> {
2876 self.orders_for_exec_algorithm(
2877 &exec_algorithm_id,
2878 venue.as_ref(),
2879 instrument_id.as_ref(),
2880 strategy_id.as_ref(),
2881 account_id.as_ref(),
2882 side,
2883 )
2884 .into_iter()
2885 .map(|o| order_any_to_pyobject(py, o.clone()))
2886 .collect()
2887 }
2888
2889 #[pyo3(name = "orders_for_exec_spawn")]
2891 fn py_orders_for_exec_spawn(
2892 &self,
2893 py: Python,
2894 exec_spawn_id: ClientOrderId,
2895 ) -> PyResult<Vec<Py<PyAny>>> {
2896 self.orders_for_exec_spawn(&exec_spawn_id)
2897 .into_iter()
2898 .map(|o| order_any_to_pyobject(py, o.clone()))
2899 .collect()
2900 }
2901
2902 #[pyo3(name = "exec_spawn_total_quantity")]
2904 fn py_exec_spawn_total_quantity(
2905 &self,
2906 exec_spawn_id: ClientOrderId,
2907 active_only: bool,
2908 ) -> Option<Quantity> {
2909 self.exec_spawn_total_quantity(&exec_spawn_id, active_only)
2910 }
2911
2912 #[pyo3(name = "exec_spawn_total_filled_qty")]
2914 fn py_exec_spawn_total_filled_qty(
2915 &self,
2916 exec_spawn_id: ClientOrderId,
2917 active_only: bool,
2918 ) -> Option<Quantity> {
2919 self.exec_spawn_total_filled_qty(&exec_spawn_id, active_only)
2920 }
2921
2922 #[pyo3(name = "exec_spawn_total_leaves_qty")]
2924 fn py_exec_spawn_total_leaves_qty(
2925 &self,
2926 exec_spawn_id: ClientOrderId,
2927 active_only: bool,
2928 ) -> Option<Quantity> {
2929 self.exec_spawn_total_leaves_qty(&exec_spawn_id, active_only)
2930 }
2931
2932 #[pyo3(name = "position_for_order")]
2936 fn py_position_for_order(
2937 &self,
2938 py: Python,
2939 client_order_id: ClientOrderId,
2940 ) -> PyResult<Option<Py<PyAny>>> {
2941 match self.position_for_order(&client_order_id) {
2942 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
2943 None => Ok(None),
2944 }
2945 }
2946
2947 #[pyo3(name = "position_id")]
2949 fn py_position_id(&self, client_order_id: ClientOrderId) -> Option<PositionId> {
2950 self.position_id(&client_order_id).copied()
2951 }
2952
2953 #[pyo3(name = "positions")]
2957 fn py_positions(
2958 &self,
2959 py: Python,
2960 venue: Option<Venue>,
2961 instrument_id: Option<InstrumentId>,
2962 strategy_id: Option<StrategyId>,
2963 account_id: Option<AccountId>,
2964 side: Option<PositionSide>,
2965 ) -> PyResult<Vec<Py<PyAny>>> {
2966 self.positions(
2967 venue.as_ref(),
2968 instrument_id.as_ref(),
2969 strategy_id.as_ref(),
2970 account_id.as_ref(),
2971 side,
2972 )
2973 .into_iter()
2974 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
2975 .collect()
2976 }
2977
2978 #[pyo3(name = "positions_open")]
2982 fn py_positions_open(
2983 &self,
2984 py: Python,
2985 venue: Option<Venue>,
2986 instrument_id: Option<InstrumentId>,
2987 strategy_id: Option<StrategyId>,
2988 account_id: Option<AccountId>,
2989 side: Option<PositionSide>,
2990 ) -> PyResult<Vec<Py<PyAny>>> {
2991 self.positions_open(
2992 venue.as_ref(),
2993 instrument_id.as_ref(),
2994 strategy_id.as_ref(),
2995 account_id.as_ref(),
2996 side,
2997 )
2998 .into_iter()
2999 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
3000 .collect()
3001 }
3002
3003 #[pyo3(name = "positions_closed")]
3007 fn py_positions_closed(
3008 &self,
3009 py: Python,
3010 venue: Option<Venue>,
3011 instrument_id: Option<InstrumentId>,
3012 strategy_id: Option<StrategyId>,
3013 account_id: Option<AccountId>,
3014 side: Option<PositionSide>,
3015 ) -> PyResult<Vec<Py<PyAny>>> {
3016 self.positions_closed(
3017 venue.as_ref(),
3018 instrument_id.as_ref(),
3019 strategy_id.as_ref(),
3020 account_id.as_ref(),
3021 side,
3022 )
3023 .into_iter()
3024 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
3025 .collect()
3026 }
3027
3028 #[pyo3(name = "strategy_id_for_order")]
3030 fn py_strategy_id_for_order(&self, client_order_id: ClientOrderId) -> Option<StrategyId> {
3031 self.strategy_id_for_order(&client_order_id).copied()
3032 }
3033
3034 #[pyo3(name = "strategy_id_for_position")]
3036 fn py_strategy_id_for_position(&self, position_id: PositionId) -> Option<StrategyId> {
3037 self.strategy_id_for_position(&position_id).copied()
3038 }
3039
3040 #[pyo3(name = "position_snapshot_bytes")]
3045 fn py_position_snapshot_bytes(&self, position_id: PositionId) -> Option<Vec<Vec<u8>>> {
3046 self.position_snapshot_bytes(&position_id)
3047 }
3048
3049 #[pyo3(name = "position_snapshots", signature = (position_id=None, account_id=None))]
3054 fn py_position_snapshots(
3055 &self,
3056 py: Python,
3057 position_id: Option<PositionId>,
3058 account_id: Option<AccountId>,
3059 ) -> PyResult<Vec<Py<PyAny>>> {
3060 self.position_snapshots(position_id.as_ref(), account_id.as_ref())
3061 .into_iter()
3062 .map(|p| Ok(p.into_pyobject(py)?.into()))
3063 .collect()
3064 }
3065
3066 #[pyo3(name = "account")]
3070 fn py_account(&self, py: Python, account_id: AccountId) -> PyResult<Option<Py<PyAny>>> {
3071 match self.account(&account_id) {
3072 Some(account) => Ok(Some(account_any_to_pyobject(py, account.clone())?)),
3073 None => Ok(None),
3074 }
3075 }
3076
3077 #[pyo3(name = "account_for_venue")]
3082 fn py_account_for_venue(&self, py: Python, venue: Venue) -> PyResult<Option<Py<PyAny>>> {
3083 match self.account_for_venue(&venue) {
3084 Some(account) => Ok(Some(account_any_to_pyobject(py, account.clone())?)),
3085 None => Ok(None),
3086 }
3087 }
3088
3089 #[pyo3(name = "account_id")]
3093 fn py_account_id(&self, venue: Venue) -> Option<AccountId> {
3094 self.account_id(&venue).copied()
3095 }
3096
3097 #[pyo3(name = "get")]
3103 fn py_get(&self, key: &str) -> PyResult<Option<Vec<u8>>> {
3104 match self.get(key).map_err(to_pyvalue_err)? {
3105 Some(bytes) => Ok(Some(bytes.to_vec())),
3106 None => Ok(None),
3107 }
3108 }
3109
3110 #[pyo3(name = "add")]
3112 fn py_add_general(&mut self, key: &str, value: Vec<u8>) -> PyResult<()> {
3113 self.add(key, Bytes::from(value)).map_err(to_pyvalue_err)
3114 }
3115
3116 #[pyo3(name = "price")]
3124 fn py_price(&self, instrument_id: InstrumentId, price_type: PriceType) -> Option<Price> {
3125 self.price(&instrument_id, price_type)
3126 }
3127
3128 #[pyo3(name = "get_xrate")]
3130 fn py_get_xrate(
3131 &self,
3132 venue: Venue,
3133 from_currency: Currency,
3134 to_currency: Currency,
3135 price_type: PriceType,
3136 ) -> Option<f64> {
3137 self.get_xrate(venue, from_currency, to_currency, price_type)
3138 .and_then(|rate| rate.to_f64())
3139 }
3140
3141 #[pyo3(name = "get_mark_xrate")]
3143 fn py_get_mark_xrate(&self, from_currency: Currency, to_currency: Currency) -> Option<f64> {
3144 self.get_mark_xrate(from_currency, to_currency)
3145 }
3146
3147 #[pyo3(name = "set_mark_xrate")]
3149 fn py_set_mark_xrate(&mut self, from_currency: Currency, to_currency: Currency, xrate: f64) {
3150 self.set_mark_xrate(from_currency, to_currency, xrate);
3151 }
3152
3153 #[pyo3(name = "clear_mark_xrate")]
3159 fn py_clear_mark_xrate(&mut self, from_currency: Currency, to_currency: Currency) {
3160 self.clear_mark_xrate(from_currency, to_currency);
3161 }
3162
3163 #[pyo3(name = "clear_mark_xrates")]
3165 fn py_clear_mark_xrates(&mut self) {
3166 self.clear_mark_xrates();
3167 }
3168
3169 #[pyo3(name = "calculate_unrealized_pnl")]
3171 #[expect(clippy::needless_pass_by_value)]
3172 fn py_calculate_unrealized_pnl(
3173 &self,
3174 py: Python,
3175 position: Py<PyAny>,
3176 ) -> PyResult<Option<Money>> {
3177 let position = position.extract::<Position>(py)?;
3178 Ok(self.calculate_unrealized_pnl(&position))
3179 }
3180
3181 #[pyo3(name = "own_order_book")]
3183 fn py_own_order_book(&self, instrument_id: InstrumentId) -> Option<OwnOrderBook> {
3184 self.own_order_book(&instrument_id).cloned()
3185 }
3186
3187 #[pyo3(name = "update_own_order_book")]
3195 fn py_update_own_order_book(&mut self, py: Python, order: Py<PyAny>) -> PyResult<()> {
3196 let order_any = pyobject_to_order_any(py, order)?;
3197 self.update_own_order_book(&order_any);
3198 Ok(())
3199 }
3200
3201 #[pyo3(name = "force_remove_from_own_order_book")]
3207 fn py_force_remove_from_own_order_book(&mut self, client_order_id: ClientOrderId) {
3208 self.force_remove_from_own_order_book(&client_order_id);
3209 }
3210
3211 #[pyo3(name = "audit_own_order_books")]
3216 fn py_audit_own_order_books(&mut self) {
3217 self.audit_own_order_books();
3218 }
3219}
3220
3221#[cfg(feature = "defi")]
3222#[pymethods]
3223impl Cache {
3224 #[pyo3(name = "add_pool")]
3228 fn py_add_pool(&mut self, pool: Pool) -> PyResult<()> {
3229 self.add_pool(pool).map_err(to_pyvalue_err)
3230 }
3231
3232 #[pyo3(name = "pool")]
3234 fn py_pool(&self, instrument_id: InstrumentId) -> Option<Pool> {
3235 self.pool(&instrument_id).cloned()
3236 }
3237
3238 #[pyo3(name = "pool_ids")]
3240 fn py_pool_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
3241 self.pool_ids(venue.as_ref())
3242 }
3243
3244 #[pyo3(name = "pools")]
3246 fn py_pools(&self, venue: Option<Venue>) -> Vec<Pool> {
3247 self.pools(venue.as_ref()).into_iter().cloned().collect()
3248 }
3249
3250 #[pyo3(name = "add_pool_profiler")]
3254 fn py_add_pool_profiler(&mut self, pool_profiler: PoolProfiler) -> PyResult<()> {
3255 self.add_pool_profiler(pool_profiler)
3256 .map_err(to_pyvalue_err)
3257 }
3258
3259 #[pyo3(name = "pool_profiler")]
3261 fn py_pool_profiler(&self, instrument_id: InstrumentId) -> Option<PoolProfiler> {
3262 self.pool_profiler(&instrument_id).cloned()
3263 }
3264
3265 #[pyo3(name = "pool_profiler_ids")]
3267 fn py_pool_profiler_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
3268 self.pool_profiler_ids(venue.as_ref())
3269 }
3270
3271 #[pyo3(name = "pool_profilers")]
3273 fn py_pool_profilers(&self, venue: Option<Venue>) -> Vec<PoolProfiler> {
3274 self.pool_profilers(venue.as_ref())
3275 .into_iter()
3276 .cloned()
3277 .collect()
3278 }
3279}