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, InstrumentStatus, QuoteTick, TradeTick,
27 prices::{IndexPriceUpdate, MarkPriceUpdate},
28 },
29 enums::{AggregationSource, OmsType, OrderSide, PositionSide, PriceType},
30 identifiers::{
31 AccountId, ClientId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
32 StrategyId, Venue, VenueOrderId,
33 },
34 instruments::SyntheticInstrument,
35 orderbook::{OrderBook, own::OwnOrderBook},
36 orders::OrderList,
37 position::Position,
38 python::{
39 account::account_any_to_pyobject,
40 instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
41 orders::{order_any_to_pyobject, pyobject_to_order_any},
42 },
43 types::{Currency, Money, Price, Quantity},
44};
45use pyo3::prelude::*;
46use rust_decimal::prelude::ToPrimitive;
47
48use crate::{
49 cache::{Cache, CacheConfig, database::CacheDatabaseFactory},
50 enums::SerializationEncoding,
51 python::{config_error_to_pyvalue_err, factory::FactoryRegistry},
52};
53
54pub type CacheDatabaseFactoryRegistry = FactoryRegistry<dyn CacheDatabaseFactory>;
56
57static GLOBAL_CACHE_DATABASE_FACTORY_REGISTRY: LazyLock<CacheDatabaseFactoryRegistry> =
58 LazyLock::new(|| CacheDatabaseFactoryRegistry::new("cache database factory"));
59
60#[must_use]
62pub fn get_global_cache_database_factory_registry() -> &'static CacheDatabaseFactoryRegistry {
63 &GLOBAL_CACHE_DATABASE_FACTORY_REGISTRY
64}
65
66#[allow(non_camel_case_types)]
71#[pyo3::pyclass(
72 module = "nautilus_trader.common",
73 name = "Cache",
74 unsendable,
75 from_py_object
76)]
77#[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.common")]
78#[derive(Debug, Clone)]
79pub struct PyCache(Rc<RefCell<Cache>>);
80
81impl PyCache {
82 #[must_use]
84 pub fn from_rc(rc: Rc<RefCell<Cache>>) -> Self {
85 Self(rc)
86 }
87
88 #[must_use]
90 pub fn cache_rc(&self) -> Rc<RefCell<Cache>> {
91 self.0.clone()
92 }
93}
94
95#[pymethods]
96#[pyo3_stub_gen::derive::gen_stub_pymethods]
97impl PyCache {
98 #[new]
99 #[pyo3(signature = (config=None))]
100 fn py_new(config: Option<CacheConfig>) -> Self {
101 Self(Rc::new(RefCell::new(Cache::new(config, None))))
102 }
103
104 #[pyo3(name = "reset")]
105 fn py_reset(&mut self) {
106 self.0.borrow_mut().reset();
107 }
108
109 #[pyo3(name = "dispose")]
110 fn py_dispose(&mut self) {
111 self.0.borrow_mut().dispose();
112 }
113
114 #[pyo3(name = "purge_closed_orders", signature = (ts_now, buffer_secs=0))]
115 fn py_purge_closed_orders(&mut self, ts_now: u64, buffer_secs: u64) {
116 self.0
117 .borrow_mut()
118 .purge_closed_orders(ts_now.into(), buffer_secs);
119 }
120
121 #[pyo3(name = "purge_closed_positions", signature = (ts_now, buffer_secs=0))]
122 fn py_purge_closed_positions(&mut self, ts_now: u64, buffer_secs: u64) {
123 self.0
124 .borrow_mut()
125 .purge_closed_positions(ts_now.into(), buffer_secs);
126 }
127
128 #[pyo3(name = "purge_order")]
129 fn py_purge_order(&mut self, client_order_id: ClientOrderId) {
130 self.0.borrow_mut().purge_order(client_order_id);
131 }
132
133 #[pyo3(name = "purge_position")]
134 fn py_purge_position(&mut self, position_id: PositionId) {
135 self.0.borrow_mut().purge_position(position_id);
136 }
137
138 #[pyo3(name = "purge_instrument")]
139 fn py_purge_instrument(&mut self, instrument_id: InstrumentId) {
140 self.0.borrow_mut().purge_instrument(instrument_id);
141 }
142
143 #[pyo3(name = "purge_account_events", signature = (ts_now, lookback_secs=0))]
144 fn py_purge_account_events(&mut self, ts_now: u64, lookback_secs: u64) {
145 self.0
146 .borrow_mut()
147 .purge_account_events(ts_now.into(), lookback_secs);
148 }
149
150 #[pyo3(name = "get")]
151 fn py_get(&self, key: &str) -> PyResult<Option<Vec<u8>>> {
152 match self.0.borrow().get(key).map_err(to_pyvalue_err)? {
153 Some(bytes) => Ok(Some(bytes.to_vec())),
154 None => Ok(None),
155 }
156 }
157
158 #[pyo3(name = "add")]
159 fn py_add_general(&mut self, key: &str, value: Vec<u8>) -> PyResult<()> {
160 self.0
161 .borrow_mut()
162 .add(key, Bytes::from(value))
163 .map_err(to_pyvalue_err)
164 }
165
166 #[pyo3(name = "quote", signature = (instrument_id, index=0))]
167 fn py_quote(&self, instrument_id: InstrumentId, index: usize) -> Option<QuoteTick> {
168 self.0
169 .borrow()
170 .quote_at_index(&instrument_id, index)
171 .copied()
172 }
173
174 #[pyo3(name = "trade", signature = (instrument_id, index=0))]
175 fn py_trade(&self, instrument_id: InstrumentId, index: usize) -> Option<TradeTick> {
176 self.0
177 .borrow()
178 .trade_at_index(&instrument_id, index)
179 .copied()
180 }
181
182 #[pyo3(name = "bar", signature = (bar_type, index=0))]
183 fn py_bar(&self, bar_type: BarType, index: usize) -> Option<Bar> {
184 self.0.borrow().bar_at_index(&bar_type, index).copied()
185 }
186
187 #[pyo3(name = "quotes")]
188 fn py_quotes(&self, instrument_id: InstrumentId) -> Option<Vec<QuoteTick>> {
189 self.0.borrow().quotes(&instrument_id)
190 }
191
192 #[pyo3(name = "trades")]
193 fn py_trades(&self, instrument_id: InstrumentId) -> Option<Vec<TradeTick>> {
194 self.0.borrow().trades(&instrument_id)
195 }
196
197 #[pyo3(name = "bars")]
198 fn py_bars(&self, bar_type: BarType) -> Option<Vec<Bar>> {
199 self.0.borrow().bars(&bar_type)
200 }
201
202 #[pyo3(name = "bar_types", signature = (aggregation_source, instrument_id=None, price_type=None))]
203 fn py_bar_types(
204 &self,
205 aggregation_source: AggregationSource,
206 instrument_id: Option<InstrumentId>,
207 price_type: Option<PriceType>,
208 ) -> Vec<BarType> {
209 self.0
210 .borrow()
211 .bar_types(
212 instrument_id.as_ref(),
213 price_type.as_ref(),
214 aggregation_source,
215 )
216 .into_iter()
217 .copied()
218 .collect()
219 }
220
221 #[pyo3(name = "mark_price")]
222 fn py_mark_price(&self, instrument_id: InstrumentId) -> Option<MarkPriceUpdate> {
223 self.0.borrow().mark_price(&instrument_id).copied()
224 }
225
226 #[pyo3(name = "mark_prices")]
227 fn py_mark_prices(&self, instrument_id: InstrumentId) -> Option<Vec<MarkPriceUpdate>> {
228 self.0.borrow().mark_prices(&instrument_id)
229 }
230
231 #[pyo3(name = "index_price")]
232 fn py_index_price(&self, instrument_id: InstrumentId) -> Option<IndexPriceUpdate> {
233 self.0.borrow().index_price(&instrument_id).copied()
234 }
235
236 #[pyo3(name = "index_prices")]
237 fn py_index_prices(&self, instrument_id: InstrumentId) -> Option<Vec<IndexPriceUpdate>> {
238 self.0.borrow().index_prices(&instrument_id)
239 }
240
241 #[pyo3(name = "funding_rate")]
242 fn py_funding_rate(&self, instrument_id: InstrumentId) -> Option<FundingRateUpdate> {
243 self.0.borrow().funding_rate(&instrument_id).copied()
244 }
245
246 #[pyo3(name = "funding_rates")]
247 fn py_funding_rates(&self, instrument_id: InstrumentId) -> Option<Vec<FundingRateUpdate>> {
248 self.0.borrow().funding_rates(&instrument_id)
249 }
250
251 #[pyo3(name = "instrument_status")]
252 fn py_instrument_status(&self, instrument_id: InstrumentId) -> Option<InstrumentStatus> {
253 self.0.borrow().instrument_status(&instrument_id).copied()
254 }
255
256 #[pyo3(name = "instrument_statuses")]
257 fn py_instrument_statuses(&self, instrument_id: InstrumentId) -> Option<Vec<InstrumentStatus>> {
258 self.0.borrow().instrument_statuses(&instrument_id)
259 }
260
261 #[pyo3(name = "price")]
262 fn py_price(&self, instrument_id: InstrumentId, price_type: PriceType) -> Option<Price> {
263 self.0.borrow().price(&instrument_id, price_type)
264 }
265
266 #[pyo3(name = "order_book")]
267 fn py_order_book(&self, instrument_id: InstrumentId) -> Option<OrderBook> {
268 self.0.borrow().order_book(&instrument_id).cloned()
269 }
270
271 #[pyo3(name = "has_order_book")]
272 fn py_has_order_book(&self, instrument_id: InstrumentId) -> bool {
273 self.0.borrow().has_order_book(&instrument_id)
274 }
275
276 #[pyo3(name = "book_update_count")]
277 fn py_book_update_count(&self, instrument_id: InstrumentId) -> usize {
278 self.0.borrow().book_update_count(&instrument_id)
279 }
280
281 #[pyo3(name = "has_quote_ticks")]
282 fn py_has_quote_ticks(&self, instrument_id: InstrumentId) -> bool {
283 self.0.borrow().has_quote_ticks(&instrument_id)
284 }
285
286 #[pyo3(name = "has_trade_ticks")]
287 fn py_has_trade_ticks(&self, instrument_id: InstrumentId) -> bool {
288 self.0.borrow().has_trade_ticks(&instrument_id)
289 }
290
291 #[pyo3(name = "has_mark_prices")]
292 fn py_has_mark_prices(&self, instrument_id: InstrumentId) -> bool {
293 self.0.borrow().has_mark_prices(&instrument_id)
294 }
295
296 #[pyo3(name = "has_index_prices")]
297 fn py_has_index_prices(&self, instrument_id: InstrumentId) -> bool {
298 self.0.borrow().has_index_prices(&instrument_id)
299 }
300
301 #[pyo3(name = "has_funding_rates")]
302 fn py_has_funding_rates(&self, instrument_id: InstrumentId) -> bool {
303 self.0.borrow().has_funding_rates(&instrument_id)
304 }
305
306 #[pyo3(name = "has_instrument_statuses")]
307 fn py_has_instrument_statuses(&self, instrument_id: InstrumentId) -> bool {
308 self.0.borrow().has_instrument_statuses(&instrument_id)
309 }
310
311 #[pyo3(name = "has_bars")]
312 fn py_has_bars(&self, bar_type: BarType) -> bool {
313 self.0.borrow().has_bars(&bar_type)
314 }
315
316 #[pyo3(name = "quote_count")]
317 fn py_quote_count(&self, instrument_id: InstrumentId) -> usize {
318 self.0.borrow().quote_count(&instrument_id)
319 }
320
321 #[pyo3(name = "trade_count")]
322 fn py_trade_count(&self, instrument_id: InstrumentId) -> usize {
323 self.0.borrow().trade_count(&instrument_id)
324 }
325
326 #[pyo3(name = "mark_price_count")]
327 fn py_mark_price_count(&self, instrument_id: InstrumentId) -> usize {
328 self.0.borrow().mark_price_count(&instrument_id)
329 }
330
331 #[pyo3(name = "index_price_count")]
332 fn py_index_price_count(&self, instrument_id: InstrumentId) -> usize {
333 self.0.borrow().index_price_count(&instrument_id)
334 }
335
336 #[pyo3(name = "funding_rate_count")]
337 fn py_funding_rate_count(&self, instrument_id: InstrumentId) -> usize {
338 self.0.borrow().funding_rate_count(&instrument_id)
339 }
340
341 #[pyo3(name = "instrument_status_count")]
342 fn py_instrument_status_count(&self, instrument_id: InstrumentId) -> usize {
343 self.0.borrow().instrument_status_count(&instrument_id)
344 }
345
346 #[pyo3(name = "bar_count")]
347 fn py_bar_count(&self, bar_type: BarType) -> usize {
348 self.0.borrow().bar_count(&bar_type)
349 }
350
351 #[pyo3(name = "get_xrate")]
352 fn py_get_xrate(
353 &self,
354 venue: Venue,
355 from_currency: Currency,
356 to_currency: Currency,
357 price_type: PriceType,
358 ) -> Option<f64> {
359 self.0
360 .borrow()
361 .get_xrate(venue, from_currency, to_currency, price_type)
362 .and_then(|rate| rate.to_f64())
363 }
364
365 #[pyo3(name = "get_mark_xrate")]
366 fn py_get_mark_xrate(&self, from_currency: Currency, to_currency: Currency) -> Option<f64> {
367 self.0.borrow().get_mark_xrate(from_currency, to_currency)
368 }
369
370 #[pyo3(name = "own_order_book")]
371 fn py_own_order_book(&self, instrument_id: InstrumentId) -> Option<OwnOrderBook> {
372 self.0.borrow().own_order_book(&instrument_id).cloned()
373 }
374
375 #[pyo3(name = "instrument")]
376 fn py_instrument(
377 &self,
378 py: Python,
379 instrument_id: InstrumentId,
380 ) -> PyResult<Option<Py<PyAny>>> {
381 let cache = self.0.borrow();
382 match cache.instrument(&instrument_id) {
383 Some(instrument) => Ok(Some(instrument_any_to_pyobject(py, instrument.clone())?)),
384 None => Ok(None),
385 }
386 }
387
388 #[pyo3(name = "instrument_ids", signature = (venue=None))]
389 fn py_instrument_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
390 self.0
391 .borrow()
392 .instrument_ids(venue.as_ref())
393 .into_iter()
394 .copied()
395 .collect()
396 }
397
398 #[pyo3(name = "instruments", signature = (venue=None))]
399 fn py_instruments(&self, py: Python, venue: Option<Venue>) -> PyResult<Vec<Py<PyAny>>> {
400 let cache = self.0.borrow();
401 let mut py_instruments = Vec::new();
402
403 match venue {
404 Some(venue) => {
405 for instrument in cache.instruments(&venue, None) {
406 py_instruments.push(instrument_any_to_pyobject(py, (*instrument).clone())?);
407 }
408 }
409 None => {
410 for instrument_id in cache.instrument_ids(None) {
411 if let Some(instrument) = cache.instrument(instrument_id) {
412 py_instruments.push(instrument_any_to_pyobject(py, instrument.clone())?);
413 }
414 }
415 }
416 }
417 Ok(py_instruments)
418 }
419
420 #[pyo3(name = "synthetic")]
421 fn py_synthetic(&self, instrument_id: InstrumentId) -> Option<SyntheticInstrument> {
422 self.0.borrow().synthetic(&instrument_id).cloned()
423 }
424
425 #[pyo3(name = "synthetic_ids")]
426 fn py_synthetic_ids(&self) -> Vec<InstrumentId> {
427 self.0
428 .borrow()
429 .synthetic_ids()
430 .into_iter()
431 .copied()
432 .collect()
433 }
434
435 #[pyo3(name = "account")]
436 fn py_account(&self, py: Python, account_id: AccountId) -> PyResult<Option<Py<PyAny>>> {
437 let cache = self.0.borrow();
438 match cache.account(&account_id) {
439 Some(account) => Ok(Some(account_any_to_pyobject(py, account.clone())?)),
440 None => Ok(None),
441 }
442 }
443
444 #[pyo3(name = "account_for_venue")]
445 fn py_account_for_venue(&self, py: Python, venue: Venue) -> PyResult<Option<Py<PyAny>>> {
446 let cache = self.0.borrow();
447 match cache.account_for_venue(&venue) {
448 Some(account) => Ok(Some(account_any_to_pyobject(py, account.clone())?)),
449 None => Ok(None),
450 }
451 }
452
453 #[pyo3(name = "account_id")]
454 fn py_account_id(&self, venue: Venue) -> Option<AccountId> {
455 self.0.borrow().account_id(&venue).copied()
456 }
457
458 #[pyo3(name = "client_order_ids", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
459 fn py_client_order_ids(
460 &self,
461 venue: Option<Venue>,
462 instrument_id: Option<InstrumentId>,
463 strategy_id: Option<StrategyId>,
464 account_id: Option<AccountId>,
465 ) -> Vec<ClientOrderId> {
466 self.0
467 .borrow()
468 .client_order_ids(
469 venue.as_ref(),
470 instrument_id.as_ref(),
471 strategy_id.as_ref(),
472 account_id.as_ref(),
473 )
474 .into_iter()
475 .collect()
476 }
477
478 #[pyo3(name = "client_order_ids_open", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
479 fn py_client_order_ids_open(
480 &self,
481 venue: Option<Venue>,
482 instrument_id: Option<InstrumentId>,
483 strategy_id: Option<StrategyId>,
484 account_id: Option<AccountId>,
485 ) -> Vec<ClientOrderId> {
486 self.0
487 .borrow()
488 .client_order_ids_open(
489 venue.as_ref(),
490 instrument_id.as_ref(),
491 strategy_id.as_ref(),
492 account_id.as_ref(),
493 )
494 .into_iter()
495 .collect()
496 }
497
498 #[pyo3(name = "client_order_ids_closed", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
499 fn py_client_order_ids_closed(
500 &self,
501 venue: Option<Venue>,
502 instrument_id: Option<InstrumentId>,
503 strategy_id: Option<StrategyId>,
504 account_id: Option<AccountId>,
505 ) -> Vec<ClientOrderId> {
506 self.0
507 .borrow()
508 .client_order_ids_closed(
509 venue.as_ref(),
510 instrument_id.as_ref(),
511 strategy_id.as_ref(),
512 account_id.as_ref(),
513 )
514 .into_iter()
515 .collect()
516 }
517
518 #[pyo3(name = "client_order_ids_emulated", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
519 fn py_client_order_ids_emulated(
520 &self,
521 venue: Option<Venue>,
522 instrument_id: Option<InstrumentId>,
523 strategy_id: Option<StrategyId>,
524 account_id: Option<AccountId>,
525 ) -> Vec<ClientOrderId> {
526 self.0
527 .borrow()
528 .client_order_ids_emulated(
529 venue.as_ref(),
530 instrument_id.as_ref(),
531 strategy_id.as_ref(),
532 account_id.as_ref(),
533 )
534 .into_iter()
535 .collect()
536 }
537
538 #[pyo3(name = "client_order_ids_inflight", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
539 fn py_client_order_ids_inflight(
540 &self,
541 venue: Option<Venue>,
542 instrument_id: Option<InstrumentId>,
543 strategy_id: Option<StrategyId>,
544 account_id: Option<AccountId>,
545 ) -> Vec<ClientOrderId> {
546 self.0
547 .borrow()
548 .client_order_ids_inflight(
549 venue.as_ref(),
550 instrument_id.as_ref(),
551 strategy_id.as_ref(),
552 account_id.as_ref(),
553 )
554 .into_iter()
555 .collect()
556 }
557
558 #[pyo3(name = "position_ids", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
559 fn py_position_ids(
560 &self,
561 venue: Option<Venue>,
562 instrument_id: Option<InstrumentId>,
563 strategy_id: Option<StrategyId>,
564 account_id: Option<AccountId>,
565 ) -> Vec<PositionId> {
566 self.0
567 .borrow()
568 .position_ids(
569 venue.as_ref(),
570 instrument_id.as_ref(),
571 strategy_id.as_ref(),
572 account_id.as_ref(),
573 )
574 .into_iter()
575 .collect()
576 }
577
578 #[pyo3(name = "position_open_ids", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
579 fn py_position_open_ids(
580 &self,
581 venue: Option<Venue>,
582 instrument_id: Option<InstrumentId>,
583 strategy_id: Option<StrategyId>,
584 account_id: Option<AccountId>,
585 ) -> Vec<PositionId> {
586 self.0
587 .borrow()
588 .position_open_ids(
589 venue.as_ref(),
590 instrument_id.as_ref(),
591 strategy_id.as_ref(),
592 account_id.as_ref(),
593 )
594 .into_iter()
595 .collect()
596 }
597
598 #[pyo3(name = "position_closed_ids", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
599 fn py_position_closed_ids(
600 &self,
601 venue: Option<Venue>,
602 instrument_id: Option<InstrumentId>,
603 strategy_id: Option<StrategyId>,
604 account_id: Option<AccountId>,
605 ) -> Vec<PositionId> {
606 self.0
607 .borrow()
608 .position_closed_ids(
609 venue.as_ref(),
610 instrument_id.as_ref(),
611 strategy_id.as_ref(),
612 account_id.as_ref(),
613 )
614 .into_iter()
615 .collect()
616 }
617
618 #[pyo3(name = "strategy_ids")]
619 fn py_strategy_ids(&self) -> Vec<StrategyId> {
620 self.0.borrow().strategy_ids().into_iter().collect()
621 }
622
623 #[pyo3(name = "exec_algorithm_ids")]
624 fn py_exec_algorithm_ids(&self) -> Vec<ExecAlgorithmId> {
625 self.0.borrow().exec_algorithm_ids().into_iter().collect()
626 }
627
628 #[pyo3(name = "order")]
629 fn py_order(&self, py: Python, client_order_id: ClientOrderId) -> PyResult<Option<Py<PyAny>>> {
630 let cache = self.0.borrow();
631 match cache.order(&client_order_id) {
632 Some(order) => Ok(Some(order_any_to_pyobject(py, order.clone())?)),
633 None => Ok(None),
634 }
635 }
636
637 #[pyo3(name = "client_order_id")]
638 fn py_client_order_id(&self, venue_order_id: VenueOrderId) -> Option<ClientOrderId> {
639 self.0.borrow().client_order_id(&venue_order_id).copied()
640 }
641
642 #[pyo3(name = "venue_order_id")]
643 fn py_venue_order_id(&self, client_order_id: ClientOrderId) -> Option<VenueOrderId> {
644 self.0.borrow().venue_order_id(&client_order_id).copied()
645 }
646
647 #[pyo3(name = "client_id")]
648 fn py_client_id(&self, client_order_id: ClientOrderId) -> Option<ClientId> {
649 self.0.borrow().client_id(&client_order_id).copied()
650 }
651
652 #[pyo3(name = "orders", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
653 fn py_orders(
654 &self,
655 py: Python,
656 venue: Option<Venue>,
657 instrument_id: Option<InstrumentId>,
658 strategy_id: Option<StrategyId>,
659 account_id: Option<AccountId>,
660 side: Option<OrderSide>,
661 ) -> PyResult<Vec<Py<PyAny>>> {
662 let cache = self.0.borrow();
663 cache
664 .orders(
665 venue.as_ref(),
666 instrument_id.as_ref(),
667 strategy_id.as_ref(),
668 account_id.as_ref(),
669 side,
670 )
671 .into_iter()
672 .map(|o| order_any_to_pyobject(py, o.clone()))
673 .collect()
674 }
675
676 #[pyo3(name = "orders_open", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
677 fn py_orders_open(
678 &self,
679 py: Python,
680 venue: Option<Venue>,
681 instrument_id: Option<InstrumentId>,
682 strategy_id: Option<StrategyId>,
683 account_id: Option<AccountId>,
684 side: Option<OrderSide>,
685 ) -> PyResult<Vec<Py<PyAny>>> {
686 let cache = self.0.borrow();
687 cache
688 .orders_open(
689 venue.as_ref(),
690 instrument_id.as_ref(),
691 strategy_id.as_ref(),
692 account_id.as_ref(),
693 side,
694 )
695 .into_iter()
696 .map(|o| order_any_to_pyobject(py, o.clone()))
697 .collect()
698 }
699
700 #[pyo3(name = "orders_closed", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
701 fn py_orders_closed(
702 &self,
703 py: Python,
704 venue: Option<Venue>,
705 instrument_id: Option<InstrumentId>,
706 strategy_id: Option<StrategyId>,
707 account_id: Option<AccountId>,
708 side: Option<OrderSide>,
709 ) -> PyResult<Vec<Py<PyAny>>> {
710 let cache = self.0.borrow();
711 cache
712 .orders_closed(
713 venue.as_ref(),
714 instrument_id.as_ref(),
715 strategy_id.as_ref(),
716 account_id.as_ref(),
717 side,
718 )
719 .into_iter()
720 .map(|o| order_any_to_pyobject(py, o.clone()))
721 .collect()
722 }
723
724 #[pyo3(name = "orders_emulated", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
725 fn py_orders_emulated(
726 &self,
727 py: Python,
728 venue: Option<Venue>,
729 instrument_id: Option<InstrumentId>,
730 strategy_id: Option<StrategyId>,
731 account_id: Option<AccountId>,
732 side: Option<OrderSide>,
733 ) -> PyResult<Vec<Py<PyAny>>> {
734 let cache = self.0.borrow();
735 cache
736 .orders_emulated(
737 venue.as_ref(),
738 instrument_id.as_ref(),
739 strategy_id.as_ref(),
740 account_id.as_ref(),
741 side,
742 )
743 .into_iter()
744 .map(|o| order_any_to_pyobject(py, o.clone()))
745 .collect()
746 }
747
748 #[pyo3(name = "orders_inflight", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
749 fn py_orders_inflight(
750 &self,
751 py: Python,
752 venue: Option<Venue>,
753 instrument_id: Option<InstrumentId>,
754 strategy_id: Option<StrategyId>,
755 account_id: Option<AccountId>,
756 side: Option<OrderSide>,
757 ) -> PyResult<Vec<Py<PyAny>>> {
758 let cache = self.0.borrow();
759 cache
760 .orders_inflight(
761 venue.as_ref(),
762 instrument_id.as_ref(),
763 strategy_id.as_ref(),
764 account_id.as_ref(),
765 side,
766 )
767 .into_iter()
768 .map(|o| order_any_to_pyobject(py, o.clone()))
769 .collect()
770 }
771
772 #[pyo3(name = "orders_for_position")]
773 fn py_orders_for_position(
774 &self,
775 py: Python,
776 position_id: PositionId,
777 ) -> PyResult<Vec<Py<PyAny>>> {
778 let cache = self.0.borrow();
779 cache
780 .orders_for_position(&position_id)
781 .into_iter()
782 .map(|o| order_any_to_pyobject(py, o.clone()))
783 .collect()
784 }
785
786 #[pyo3(name = "order_exists")]
787 fn py_order_exists(&self, client_order_id: ClientOrderId) -> bool {
788 self.0.borrow().order_exists(&client_order_id)
789 }
790
791 #[pyo3(name = "is_order_open")]
792 fn py_is_order_open(&self, client_order_id: ClientOrderId) -> bool {
793 self.0.borrow().is_order_open(&client_order_id)
794 }
795
796 #[pyo3(name = "is_order_closed")]
797 fn py_is_order_closed(&self, client_order_id: ClientOrderId) -> bool {
798 self.0.borrow().is_order_closed(&client_order_id)
799 }
800
801 #[pyo3(name = "is_order_emulated")]
802 fn py_is_order_emulated(&self, client_order_id: ClientOrderId) -> bool {
803 self.0.borrow().is_order_emulated(&client_order_id)
804 }
805
806 #[pyo3(name = "is_order_inflight")]
807 fn py_is_order_inflight(&self, client_order_id: ClientOrderId) -> bool {
808 self.0.borrow().is_order_inflight(&client_order_id)
809 }
810
811 #[pyo3(name = "is_order_pending_cancel_local")]
812 fn py_is_order_pending_cancel_local(&self, client_order_id: ClientOrderId) -> bool {
813 self.0
814 .borrow()
815 .is_order_pending_cancel_local(&client_order_id)
816 }
817
818 #[pyo3(name = "orders_open_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
819 fn py_orders_open_count(
820 &self,
821 venue: Option<Venue>,
822 instrument_id: Option<InstrumentId>,
823 strategy_id: Option<StrategyId>,
824 account_id: Option<AccountId>,
825 side: Option<OrderSide>,
826 ) -> usize {
827 self.0.borrow().orders_open_count(
828 venue.as_ref(),
829 instrument_id.as_ref(),
830 strategy_id.as_ref(),
831 account_id.as_ref(),
832 side,
833 )
834 }
835
836 #[pyo3(name = "orders_closed_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
837 fn py_orders_closed_count(
838 &self,
839 venue: Option<Venue>,
840 instrument_id: Option<InstrumentId>,
841 strategy_id: Option<StrategyId>,
842 account_id: Option<AccountId>,
843 side: Option<OrderSide>,
844 ) -> usize {
845 self.0.borrow().orders_closed_count(
846 venue.as_ref(),
847 instrument_id.as_ref(),
848 strategy_id.as_ref(),
849 account_id.as_ref(),
850 side,
851 )
852 }
853
854 #[pyo3(name = "orders_emulated_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
855 fn py_orders_emulated_count(
856 &self,
857 venue: Option<Venue>,
858 instrument_id: Option<InstrumentId>,
859 strategy_id: Option<StrategyId>,
860 account_id: Option<AccountId>,
861 side: Option<OrderSide>,
862 ) -> usize {
863 self.0.borrow().orders_emulated_count(
864 venue.as_ref(),
865 instrument_id.as_ref(),
866 strategy_id.as_ref(),
867 account_id.as_ref(),
868 side,
869 )
870 }
871
872 #[pyo3(name = "orders_inflight_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
873 fn py_orders_inflight_count(
874 &self,
875 venue: Option<Venue>,
876 instrument_id: Option<InstrumentId>,
877 strategy_id: Option<StrategyId>,
878 account_id: Option<AccountId>,
879 side: Option<OrderSide>,
880 ) -> usize {
881 self.0.borrow().orders_inflight_count(
882 venue.as_ref(),
883 instrument_id.as_ref(),
884 strategy_id.as_ref(),
885 account_id.as_ref(),
886 side,
887 )
888 }
889
890 #[pyo3(name = "orders_total_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
891 fn py_orders_total_count(
892 &self,
893 venue: Option<Venue>,
894 instrument_id: Option<InstrumentId>,
895 strategy_id: Option<StrategyId>,
896 account_id: Option<AccountId>,
897 side: Option<OrderSide>,
898 ) -> usize {
899 self.0.borrow().orders_total_count(
900 venue.as_ref(),
901 instrument_id.as_ref(),
902 strategy_id.as_ref(),
903 account_id.as_ref(),
904 side,
905 )
906 }
907
908 #[pyo3(name = "order_list")]
909 fn py_order_list(&self, order_list_id: OrderListId) -> Option<OrderList> {
910 self.0.borrow().order_list(&order_list_id).cloned()
911 }
912
913 #[pyo3(name = "order_lists", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None))]
914 fn py_order_lists(
915 &self,
916 venue: Option<Venue>,
917 instrument_id: Option<InstrumentId>,
918 strategy_id: Option<StrategyId>,
919 account_id: Option<AccountId>,
920 ) -> Vec<OrderList> {
921 let cache = self.0.borrow();
922 cache
923 .order_lists(
924 venue.as_ref(),
925 instrument_id.as_ref(),
926 strategy_id.as_ref(),
927 account_id.as_ref(),
928 )
929 .into_iter()
930 .cloned()
931 .collect()
932 }
933
934 #[pyo3(name = "order_list_exists")]
935 fn py_order_list_exists(&self, order_list_id: OrderListId) -> bool {
936 self.0.borrow().order_list_exists(&order_list_id)
937 }
938
939 #[pyo3(name = "orders_for_exec_algorithm", signature = (exec_algorithm_id, venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
940 #[expect(clippy::too_many_arguments)]
941 fn py_orders_for_exec_algorithm(
942 &self,
943 py: Python,
944 exec_algorithm_id: ExecAlgorithmId,
945 venue: Option<Venue>,
946 instrument_id: Option<InstrumentId>,
947 strategy_id: Option<StrategyId>,
948 account_id: Option<AccountId>,
949 side: Option<OrderSide>,
950 ) -> PyResult<Vec<Py<PyAny>>> {
951 let cache = self.0.borrow();
952 cache
953 .orders_for_exec_algorithm(
954 &exec_algorithm_id,
955 venue.as_ref(),
956 instrument_id.as_ref(),
957 strategy_id.as_ref(),
958 account_id.as_ref(),
959 side,
960 )
961 .into_iter()
962 .map(|o| order_any_to_pyobject(py, o.clone()))
963 .collect()
964 }
965
966 #[pyo3(name = "orders_for_exec_spawn")]
967 fn py_orders_for_exec_spawn(
968 &self,
969 py: Python,
970 exec_spawn_id: ClientOrderId,
971 ) -> PyResult<Vec<Py<PyAny>>> {
972 let cache = self.0.borrow();
973 cache
974 .orders_for_exec_spawn(&exec_spawn_id)
975 .into_iter()
976 .map(|o| order_any_to_pyobject(py, o.clone()))
977 .collect()
978 }
979
980 #[pyo3(name = "exec_spawn_total_quantity")]
981 fn py_exec_spawn_total_quantity(
982 &self,
983 exec_spawn_id: ClientOrderId,
984 active_only: bool,
985 ) -> Option<Quantity> {
986 self.0
987 .borrow()
988 .exec_spawn_total_quantity(&exec_spawn_id, active_only)
989 }
990
991 #[pyo3(name = "exec_spawn_total_filled_qty")]
992 fn py_exec_spawn_total_filled_qty(
993 &self,
994 exec_spawn_id: ClientOrderId,
995 active_only: bool,
996 ) -> Option<Quantity> {
997 self.0
998 .borrow()
999 .exec_spawn_total_filled_qty(&exec_spawn_id, active_only)
1000 }
1001
1002 #[pyo3(name = "exec_spawn_total_leaves_qty")]
1003 fn py_exec_spawn_total_leaves_qty(
1004 &self,
1005 exec_spawn_id: ClientOrderId,
1006 active_only: bool,
1007 ) -> Option<Quantity> {
1008 self.0
1009 .borrow()
1010 .exec_spawn_total_leaves_qty(&exec_spawn_id, active_only)
1011 }
1012
1013 #[pyo3(name = "position")]
1014 fn py_position(&self, py: Python, position_id: PositionId) -> PyResult<Option<Py<PyAny>>> {
1015 let cache = self.0.borrow();
1016 match cache.position(&position_id) {
1017 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
1018 None => Ok(None),
1019 }
1020 }
1021
1022 #[pyo3(name = "position_for_order")]
1023 fn py_position_for_order(
1024 &self,
1025 py: Python,
1026 client_order_id: ClientOrderId,
1027 ) -> PyResult<Option<Py<PyAny>>> {
1028 let cache = self.0.borrow();
1029 match cache.position_for_order(&client_order_id) {
1030 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
1031 None => Ok(None),
1032 }
1033 }
1034
1035 #[pyo3(name = "position_id")]
1036 fn py_position_id(&self, client_order_id: ClientOrderId) -> Option<PositionId> {
1037 self.0.borrow().position_id(&client_order_id).copied()
1038 }
1039
1040 #[pyo3(name = "positions", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1041 fn py_positions(
1042 &self,
1043 py: Python,
1044 venue: Option<Venue>,
1045 instrument_id: Option<InstrumentId>,
1046 strategy_id: Option<StrategyId>,
1047 account_id: Option<AccountId>,
1048 side: Option<PositionSide>,
1049 ) -> PyResult<Vec<Py<PyAny>>> {
1050 let cache = self.0.borrow();
1051 cache
1052 .positions(
1053 venue.as_ref(),
1054 instrument_id.as_ref(),
1055 strategy_id.as_ref(),
1056 account_id.as_ref(),
1057 side,
1058 )
1059 .into_iter()
1060 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
1061 .collect()
1062 }
1063
1064 #[pyo3(name = "positions_open", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1065 fn py_positions_open(
1066 &self,
1067 py: Python,
1068 venue: Option<Venue>,
1069 instrument_id: Option<InstrumentId>,
1070 strategy_id: Option<StrategyId>,
1071 account_id: Option<AccountId>,
1072 side: Option<PositionSide>,
1073 ) -> PyResult<Vec<Py<PyAny>>> {
1074 let cache = self.0.borrow();
1075 cache
1076 .positions_open(
1077 venue.as_ref(),
1078 instrument_id.as_ref(),
1079 strategy_id.as_ref(),
1080 account_id.as_ref(),
1081 side,
1082 )
1083 .into_iter()
1084 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
1085 .collect()
1086 }
1087
1088 #[pyo3(name = "positions_closed", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1089 fn py_positions_closed(
1090 &self,
1091 py: Python,
1092 venue: Option<Venue>,
1093 instrument_id: Option<InstrumentId>,
1094 strategy_id: Option<StrategyId>,
1095 account_id: Option<AccountId>,
1096 side: Option<PositionSide>,
1097 ) -> PyResult<Vec<Py<PyAny>>> {
1098 let cache = self.0.borrow();
1099 cache
1100 .positions_closed(
1101 venue.as_ref(),
1102 instrument_id.as_ref(),
1103 strategy_id.as_ref(),
1104 account_id.as_ref(),
1105 side,
1106 )
1107 .into_iter()
1108 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
1109 .collect()
1110 }
1111
1112 #[pyo3(name = "position_exists")]
1113 fn py_position_exists(&self, position_id: PositionId) -> bool {
1114 self.0.borrow().position_exists(&position_id)
1115 }
1116
1117 #[pyo3(name = "is_position_open")]
1118 fn py_is_position_open(&self, position_id: PositionId) -> bool {
1119 self.0.borrow().is_position_open(&position_id)
1120 }
1121
1122 #[pyo3(name = "is_position_closed")]
1123 fn py_is_position_closed(&self, position_id: PositionId) -> bool {
1124 self.0.borrow().is_position_closed(&position_id)
1125 }
1126
1127 #[pyo3(name = "positions_open_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1128 fn py_positions_open_count(
1129 &self,
1130 venue: Option<Venue>,
1131 instrument_id: Option<InstrumentId>,
1132 strategy_id: Option<StrategyId>,
1133 account_id: Option<AccountId>,
1134 side: Option<PositionSide>,
1135 ) -> usize {
1136 self.0.borrow().positions_open_count(
1137 venue.as_ref(),
1138 instrument_id.as_ref(),
1139 strategy_id.as_ref(),
1140 account_id.as_ref(),
1141 side,
1142 )
1143 }
1144
1145 #[pyo3(name = "positions_closed_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1146 fn py_positions_closed_count(
1147 &self,
1148 venue: Option<Venue>,
1149 instrument_id: Option<InstrumentId>,
1150 strategy_id: Option<StrategyId>,
1151 account_id: Option<AccountId>,
1152 side: Option<PositionSide>,
1153 ) -> usize {
1154 self.0.borrow().positions_closed_count(
1155 venue.as_ref(),
1156 instrument_id.as_ref(),
1157 strategy_id.as_ref(),
1158 account_id.as_ref(),
1159 side,
1160 )
1161 }
1162
1163 #[pyo3(name = "positions_total_count", signature = (venue=None, instrument_id=None, strategy_id=None, account_id=None, side=None))]
1164 fn py_positions_total_count(
1165 &self,
1166 venue: Option<Venue>,
1167 instrument_id: Option<InstrumentId>,
1168 strategy_id: Option<StrategyId>,
1169 account_id: Option<AccountId>,
1170 side: Option<PositionSide>,
1171 ) -> usize {
1172 self.0.borrow().positions_total_count(
1173 venue.as_ref(),
1174 instrument_id.as_ref(),
1175 strategy_id.as_ref(),
1176 account_id.as_ref(),
1177 side,
1178 )
1179 }
1180
1181 #[pyo3(name = "strategy_id_for_order")]
1182 fn py_strategy_id_for_order(&self, client_order_id: ClientOrderId) -> Option<StrategyId> {
1183 self.0
1184 .borrow()
1185 .strategy_id_for_order(&client_order_id)
1186 .copied()
1187 }
1188
1189 #[pyo3(name = "strategy_id_for_position")]
1190 fn py_strategy_id_for_position(&self, position_id: PositionId) -> Option<StrategyId> {
1191 self.0
1192 .borrow()
1193 .strategy_id_for_position(&position_id)
1194 .copied()
1195 }
1196
1197 #[pyo3(name = "position_snapshot_bytes")]
1198 fn py_position_snapshot_bytes(&self, position_id: PositionId) -> Option<Vec<Vec<u8>>> {
1199 self.0.borrow().position_snapshot_bytes(&position_id)
1200 }
1201
1202 #[pyo3(name = "snapshot_position")]
1203 #[expect(clippy::needless_pass_by_value)]
1204 fn py_snapshot_position(&self, py: Python, position: Py<PyAny>) -> PyResult<()> {
1205 let position_obj = position.extract::<Position>(py)?;
1206 self.0
1207 .borrow_mut()
1208 .snapshot_position(&position_obj)
1209 .map_err(to_pyvalue_err)
1210 }
1211
1212 #[pyo3(name = "position_snapshots", signature = (position_id=None, account_id=None))]
1213 fn py_position_snapshots(
1214 &self,
1215 py: Python,
1216 position_id: Option<PositionId>,
1217 account_id: Option<AccountId>,
1218 ) -> PyResult<Vec<Py<PyAny>>> {
1219 let cache = self.0.borrow();
1220 cache
1221 .position_snapshots(position_id.as_ref(), account_id.as_ref())
1222 .into_iter()
1223 .map(|p| Ok(p.into_pyobject(py)?.into()))
1224 .collect()
1225 }
1226}
1227
1228#[cfg(test)]
1229mod tests {
1230 use nautilus_core::UnixNanos;
1231 use rstest::rstest;
1232
1233 use super::*;
1234
1235 fn create_order_list() -> OrderList {
1236 OrderList::new(
1237 OrderListId::from("OL-001"),
1238 InstrumentId::from("AUD/USD.SIM"),
1239 StrategyId::from("S-001"),
1240 vec![ClientOrderId::from("O-001")],
1241 UnixNanos::from(42_u64),
1242 )
1243 }
1244
1245 #[rstest]
1246 fn test_order_list_queries_preserve_concrete_type() {
1247 let order_list = create_order_list();
1248 let order_list_id = order_list.id;
1249 let cache = Rc::new(RefCell::new(Cache::default()));
1250 cache
1251 .borrow_mut()
1252 .add_order_list(order_list.clone())
1253 .unwrap();
1254 let py_cache = PyCache::from_rc(cache);
1255
1256 assert_eq!(
1257 py_cache.py_order_list(order_list_id),
1258 Some(order_list.clone()),
1259 );
1260 assert_eq!(
1261 py_cache.py_order_lists(None, None, None, None),
1262 vec![order_list],
1263 );
1264 }
1265}
1266
1267#[cfg(feature = "defi")]
1268#[pymethods]
1269#[pyo3_stub_gen::derive::gen_stub_pymethods]
1270impl PyCache {
1271 #[pyo3(name = "pool")]
1272 fn py_pool(&self, instrument_id: InstrumentId) -> Option<Pool> {
1273 self.0
1274 .try_borrow()
1275 .ok()
1276 .and_then(|cache| cache.pool(&instrument_id).cloned())
1277 }
1278
1279 #[pyo3(name = "pool_profiler")]
1280 fn py_pool_profiler(&self, instrument_id: InstrumentId) -> Option<PoolProfiler> {
1281 self.0
1282 .try_borrow()
1283 .ok()
1284 .and_then(|cache| cache.pool_profiler(&instrument_id).cloned())
1285 }
1286}
1287
1288#[pymethods]
1289#[pyo3_stub_gen::derive::gen_stub_pymethods]
1290impl CacheConfig {
1291 #[new]
1293 #[expect(clippy::too_many_arguments)]
1294 #[pyo3(signature = (
1295 encoding=None,
1296 timestamps_as_iso8601=None,
1297 buffer_interval_ms=None,
1298 bulk_read_batch_size=None,
1299 use_trader_prefix=None,
1300 use_instance_id=None,
1301 flush_on_start=None,
1302 drop_instruments_on_reset=None,
1303 tick_capacity=None,
1304 bar_capacity=None,
1305 save_market_data=None,
1306 persist_account_events=None,
1307 ))]
1308 fn py_new(
1309 encoding: Option<SerializationEncoding>,
1310 timestamps_as_iso8601: Option<bool>,
1311 buffer_interval_ms: Option<usize>,
1312 bulk_read_batch_size: Option<usize>,
1313 use_trader_prefix: Option<bool>,
1314 use_instance_id: Option<bool>,
1315 flush_on_start: Option<bool>,
1316 drop_instruments_on_reset: Option<bool>,
1317 tick_capacity: Option<usize>,
1318 bar_capacity: Option<usize>,
1319 save_market_data: Option<bool>,
1320 persist_account_events: Option<bool>,
1321 ) -> PyResult<Self> {
1322 let config = Self {
1323 encoding: encoding.unwrap_or_default(),
1324 timestamps_as_iso8601: timestamps_as_iso8601.unwrap_or(false),
1325 buffer_interval_ms,
1326 bulk_read_batch_size,
1327 use_trader_prefix: use_trader_prefix.unwrap_or(true),
1328 use_instance_id: use_instance_id.unwrap_or(false),
1329 flush_on_start: flush_on_start.unwrap_or(false),
1330 drop_instruments_on_reset: drop_instruments_on_reset.unwrap_or(true),
1331 tick_capacity: tick_capacity.unwrap_or(10_000),
1332 bar_capacity: bar_capacity.unwrap_or(10_000),
1333 persist_account_events: persist_account_events.unwrap_or(true),
1334 save_market_data: save_market_data.unwrap_or(false),
1335 };
1336 config.validate().map_err(config_error_to_pyvalue_err)?;
1337 Ok(config)
1338 }
1339
1340 fn __str__(&self) -> String {
1341 format!("{self:?}")
1342 }
1343
1344 fn __repr__(&self) -> String {
1345 format!("{self:?}")
1346 }
1347
1348 #[getter]
1349 fn encoding(&self) -> SerializationEncoding {
1350 self.encoding
1351 }
1352
1353 #[getter]
1354 fn timestamps_as_iso8601(&self) -> bool {
1355 self.timestamps_as_iso8601
1356 }
1357
1358 #[getter]
1359 fn buffer_interval_ms(&self) -> Option<usize> {
1360 self.buffer_interval_ms
1361 }
1362
1363 #[getter]
1364 fn bulk_read_batch_size(&self) -> Option<usize> {
1365 self.bulk_read_batch_size
1366 }
1367
1368 #[getter]
1369 fn use_trader_prefix(&self) -> bool {
1370 self.use_trader_prefix
1371 }
1372
1373 #[getter]
1374 fn use_instance_id(&self) -> bool {
1375 self.use_instance_id
1376 }
1377
1378 #[getter]
1379 fn flush_on_start(&self) -> bool {
1380 self.flush_on_start
1381 }
1382
1383 #[getter]
1384 fn drop_instruments_on_reset(&self) -> bool {
1385 self.drop_instruments_on_reset
1386 }
1387
1388 #[getter]
1389 fn tick_capacity(&self) -> usize {
1390 self.tick_capacity
1391 }
1392
1393 #[getter]
1394 fn bar_capacity(&self) -> usize {
1395 self.bar_capacity
1396 }
1397
1398 #[getter]
1399 fn persist_account_events(&self) -> bool {
1400 self.persist_account_events
1401 }
1402
1403 #[getter]
1404 fn save_market_data(&self) -> bool {
1405 self.save_market_data
1406 }
1407}
1408
1409#[pymethods]
1410impl Cache {
1411 #[new]
1413 fn py_new(config: Option<CacheConfig>) -> Self {
1414 Self::new(config, None)
1415 }
1416
1417 fn __repr__(&self) -> String {
1418 format!("{self:?}")
1419 }
1420
1421 #[pyo3(name = "reset")]
1427 fn py_reset(&mut self) {
1428 self.reset();
1429 }
1430
1431 #[pyo3(name = "dispose")]
1435 fn py_dispose(&mut self) {
1436 self.dispose();
1437 }
1438
1439 #[pyo3(name = "purge_closed_orders", signature = (ts_now, buffer_secs=0))]
1445 fn py_purge_closed_orders(&mut self, ts_now: u64, buffer_secs: u64) {
1446 self.purge_closed_orders(ts_now.into(), buffer_secs);
1447 }
1448
1449 #[pyo3(name = "purge_closed_positions", signature = (ts_now, buffer_secs=0))]
1451 fn py_purge_closed_positions(&mut self, ts_now: u64, buffer_secs: u64) {
1452 self.purge_closed_positions(ts_now.into(), buffer_secs);
1453 }
1454
1455 #[pyo3(name = "purge_order")]
1459 fn py_purge_order(&mut self, client_order_id: ClientOrderId) {
1460 self.purge_order(client_order_id);
1461 }
1462
1463 #[pyo3(name = "purge_position")]
1467 fn py_purge_position(&mut self, position_id: PositionId) {
1468 self.purge_position(position_id);
1469 }
1470
1471 #[pyo3(name = "purge_instrument")]
1476 fn py_purge_instrument(&mut self, instrument_id: InstrumentId) {
1477 self.purge_instrument(instrument_id);
1478 }
1479
1480 #[pyo3(name = "purge_account_events", signature = (ts_now, lookback_secs=0))]
1485 fn py_purge_account_events(&mut self, ts_now: u64, lookback_secs: u64) {
1486 self.purge_account_events(ts_now.into(), lookback_secs);
1487 }
1488
1489 #[pyo3(name = "add_currency")]
1495 fn py_add_currency(&mut self, currency: Currency) -> PyResult<()> {
1496 self.add_currency(currency).map_err(to_pyvalue_err)
1497 }
1498
1499 #[pyo3(name = "add_instrument")]
1505 fn py_add_instrument(&mut self, py: Python, instrument: Py<PyAny>) -> PyResult<()> {
1506 let instrument_any = pyobject_to_instrument_any(py, instrument)?;
1507 self.add_instrument(instrument_any).map_err(to_pyvalue_err)
1508 }
1509
1510 #[pyo3(name = "instrument")]
1512 fn py_instrument(
1513 &self,
1514 py: Python,
1515 instrument_id: InstrumentId,
1516 ) -> PyResult<Option<Py<PyAny>>> {
1517 match self.instrument(&instrument_id) {
1518 Some(instrument) => Ok(Some(instrument_any_to_pyobject(py, instrument.clone())?)),
1519 None => Ok(None),
1520 }
1521 }
1522
1523 #[pyo3(name = "instrument_ids")]
1525 fn py_instrument_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
1526 self.instrument_ids(venue.as_ref())
1527 .into_iter()
1528 .copied()
1529 .collect()
1530 }
1531
1532 #[pyo3(name = "instruments")]
1534 fn py_instruments(&self, py: Python, venue: Option<Venue>) -> PyResult<Vec<Py<PyAny>>> {
1535 let mut py_instruments = Vec::new();
1536
1537 if let Some(venue) = venue {
1538 let instruments = self.instruments(&venue, None);
1539 for instrument in instruments {
1540 py_instruments.push(instrument_any_to_pyobject(py, (*instrument).clone())?);
1541 }
1542 } else {
1543 let instrument_ids = self.instrument_ids(None);
1544 for instrument_id in instrument_ids {
1545 if let Some(instrument) = self.instrument(instrument_id) {
1546 py_instruments.push(instrument_any_to_pyobject(py, instrument.clone())?);
1547 }
1548 }
1549 }
1550
1551 Ok(py_instruments)
1552 }
1553
1554 #[pyo3(name = "add_order")]
1569 fn py_add_order(
1570 &mut self,
1571 py: Python,
1572 order: Py<PyAny>,
1573 position_id: Option<PositionId>,
1574 client_id: Option<ClientId>,
1575 replace_existing: Option<bool>,
1576 ) -> PyResult<()> {
1577 let order_any = pyobject_to_order_any(py, order)?;
1578 self.add_order(
1579 order_any,
1580 position_id,
1581 client_id,
1582 replace_existing.unwrap_or(false),
1583 )
1584 .map_err(to_pyvalue_err)
1585 }
1586
1587 #[pyo3(name = "order")]
1591 fn py_order(&self, py: Python, client_order_id: ClientOrderId) -> PyResult<Option<Py<PyAny>>> {
1592 match self.order(&client_order_id) {
1593 Some(order) => Ok(Some(order_any_to_pyobject(py, order.clone())?)),
1594 None => Ok(None),
1595 }
1596 }
1597
1598 #[pyo3(name = "order_exists")]
1600 fn py_order_exists(&self, client_order_id: ClientOrderId) -> bool {
1601 self.order_exists(&client_order_id)
1602 }
1603
1604 #[pyo3(name = "is_order_open")]
1606 fn py_is_order_open(&self, client_order_id: ClientOrderId) -> bool {
1607 self.is_order_open(&client_order_id)
1608 }
1609
1610 #[pyo3(name = "is_order_closed")]
1612 fn py_is_order_closed(&self, client_order_id: ClientOrderId) -> bool {
1613 self.is_order_closed(&client_order_id)
1614 }
1615
1616 #[pyo3(name = "is_order_active_local")]
1621 fn py_is_order_active_local(&self, client_order_id: ClientOrderId) -> bool {
1622 self.is_order_active_local(&client_order_id)
1623 }
1624
1625 #[pyo3(name = "orders_active_local")]
1629 fn py_orders_active_local(
1630 &self,
1631 py: Python,
1632 venue: Option<Venue>,
1633 instrument_id: Option<InstrumentId>,
1634 strategy_id: Option<StrategyId>,
1635 account_id: Option<AccountId>,
1636 side: Option<OrderSide>,
1637 ) -> PyResult<Vec<Py<PyAny>>> {
1638 self.orders_active_local(
1639 venue.as_ref(),
1640 instrument_id.as_ref(),
1641 strategy_id.as_ref(),
1642 account_id.as_ref(),
1643 side,
1644 )
1645 .into_iter()
1646 .map(|order| order_any_to_pyobject(py, order.clone()))
1647 .collect()
1648 }
1649
1650 #[pyo3(name = "orders_active_local_count")]
1655 fn py_orders_active_local_count(
1656 &self,
1657 venue: Option<Venue>,
1658 instrument_id: Option<InstrumentId>,
1659 strategy_id: Option<StrategyId>,
1660 account_id: Option<AccountId>,
1661 side: Option<OrderSide>,
1662 ) -> usize {
1663 self.orders_active_local_count(
1664 venue.as_ref(),
1665 instrument_id.as_ref(),
1666 strategy_id.as_ref(),
1667 account_id.as_ref(),
1668 side,
1669 )
1670 }
1671
1672 #[pyo3(name = "orders_open_count")]
1674 fn py_orders_open_count(
1675 &self,
1676 venue: Option<Venue>,
1677 instrument_id: Option<InstrumentId>,
1678 strategy_id: Option<StrategyId>,
1679 account_id: Option<AccountId>,
1680 side: Option<OrderSide>,
1681 ) -> usize {
1682 self.orders_open_count(
1683 venue.as_ref(),
1684 instrument_id.as_ref(),
1685 strategy_id.as_ref(),
1686 account_id.as_ref(),
1687 side,
1688 )
1689 }
1690
1691 #[pyo3(name = "orders_closed_count")]
1693 fn py_orders_closed_count(
1694 &self,
1695 venue: Option<Venue>,
1696 instrument_id: Option<InstrumentId>,
1697 strategy_id: Option<StrategyId>,
1698 account_id: Option<AccountId>,
1699 side: Option<OrderSide>,
1700 ) -> usize {
1701 self.orders_closed_count(
1702 venue.as_ref(),
1703 instrument_id.as_ref(),
1704 strategy_id.as_ref(),
1705 account_id.as_ref(),
1706 side,
1707 )
1708 }
1709
1710 #[pyo3(name = "orders_total_count")]
1712 fn py_orders_total_count(
1713 &self,
1714 venue: Option<Venue>,
1715 instrument_id: Option<InstrumentId>,
1716 strategy_id: Option<StrategyId>,
1717 account_id: Option<AccountId>,
1718 side: Option<OrderSide>,
1719 ) -> usize {
1720 self.orders_total_count(
1721 venue.as_ref(),
1722 instrument_id.as_ref(),
1723 strategy_id.as_ref(),
1724 account_id.as_ref(),
1725 side,
1726 )
1727 }
1728
1729 #[pyo3(name = "add_position")]
1737 #[expect(clippy::needless_pass_by_value)]
1738 fn py_add_position(
1739 &mut self,
1740 py: Python,
1741 position: Py<PyAny>,
1742 oms_type: OmsType,
1743 ) -> PyResult<()> {
1744 let position_obj = position.extract::<Position>(py)?;
1745 self.add_position(&position_obj, oms_type)
1746 .map_err(to_pyvalue_err)
1747 }
1748
1749 #[pyo3(name = "snapshot_position")]
1761 #[expect(clippy::needless_pass_by_value)]
1762 fn py_snapshot_position(&mut self, py: Python, position: Py<PyAny>) -> PyResult<()> {
1763 let position_obj = position.extract::<Position>(py)?;
1764 self.snapshot_position(&position_obj)
1765 .map_err(to_pyvalue_err)
1766 }
1767
1768 #[pyo3(name = "position")]
1772 fn py_position(&self, py: Python, position_id: PositionId) -> PyResult<Option<Py<PyAny>>> {
1773 match self.position(&position_id) {
1774 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
1775 None => Ok(None),
1776 }
1777 }
1778
1779 #[pyo3(name = "position_exists")]
1781 fn py_position_exists(&self, position_id: PositionId) -> bool {
1782 self.position_exists(&position_id)
1783 }
1784
1785 #[pyo3(name = "is_position_open")]
1787 fn py_is_position_open(&self, position_id: PositionId) -> bool {
1788 self.is_position_open(&position_id)
1789 }
1790
1791 #[pyo3(name = "is_position_closed")]
1793 fn py_is_position_closed(&self, position_id: PositionId) -> bool {
1794 self.is_position_closed(&position_id)
1795 }
1796
1797 #[pyo3(name = "positions_open_count")]
1799 fn py_positions_open_count(
1800 &self,
1801 venue: Option<Venue>,
1802 instrument_id: Option<InstrumentId>,
1803 strategy_id: Option<StrategyId>,
1804 account_id: Option<AccountId>,
1805 side: Option<PositionSide>,
1806 ) -> usize {
1807 self.positions_open_count(
1808 venue.as_ref(),
1809 instrument_id.as_ref(),
1810 strategy_id.as_ref(),
1811 account_id.as_ref(),
1812 side,
1813 )
1814 }
1815
1816 #[pyo3(name = "positions_closed_count")]
1818 fn py_positions_closed_count(
1819 &self,
1820 venue: Option<Venue>,
1821 instrument_id: Option<InstrumentId>,
1822 strategy_id: Option<StrategyId>,
1823 account_id: Option<AccountId>,
1824 side: Option<PositionSide>,
1825 ) -> usize {
1826 self.positions_closed_count(
1827 venue.as_ref(),
1828 instrument_id.as_ref(),
1829 strategy_id.as_ref(),
1830 account_id.as_ref(),
1831 side,
1832 )
1833 }
1834
1835 #[pyo3(name = "positions_total_count")]
1837 fn py_positions_total_count(
1838 &self,
1839 venue: Option<Venue>,
1840 instrument_id: Option<InstrumentId>,
1841 strategy_id: Option<StrategyId>,
1842 account_id: Option<AccountId>,
1843 side: Option<PositionSide>,
1844 ) -> usize {
1845 self.positions_total_count(
1846 venue.as_ref(),
1847 instrument_id.as_ref(),
1848 strategy_id.as_ref(),
1849 account_id.as_ref(),
1850 side,
1851 )
1852 }
1853
1854 #[pyo3(name = "add_quote")]
1860 fn py_add_quote(&mut self, quote: QuoteTick) -> PyResult<()> {
1861 self.add_quote(quote).map_err(to_pyvalue_err)
1862 }
1863
1864 #[pyo3(name = "add_trade")]
1870 fn py_add_trade(&mut self, trade: TradeTick) -> PyResult<()> {
1871 self.add_trade(trade).map_err(to_pyvalue_err)
1872 }
1873
1874 #[pyo3(name = "add_bar")]
1880 fn py_add_bar(&mut self, bar: Bar) -> PyResult<()> {
1881 self.add_bar(bar).map_err(to_pyvalue_err)
1882 }
1883
1884 #[pyo3(name = "quote")]
1886 fn py_quote(&self, instrument_id: InstrumentId) -> Option<QuoteTick> {
1887 self.quote(&instrument_id).copied()
1888 }
1889
1890 #[pyo3(name = "trade")]
1892 fn py_trade(&self, instrument_id: InstrumentId) -> Option<TradeTick> {
1893 self.trade(&instrument_id).copied()
1894 }
1895
1896 #[pyo3(name = "bar")]
1898 fn py_bar(&self, bar_type: BarType) -> Option<Bar> {
1899 self.bar(&bar_type).copied()
1900 }
1901
1902 #[pyo3(name = "quotes")]
1904 fn py_quotes(&self, instrument_id: InstrumentId) -> Option<Vec<QuoteTick>> {
1905 self.quotes(&instrument_id)
1906 }
1907
1908 #[pyo3(name = "trades")]
1910 fn py_trades(&self, instrument_id: InstrumentId) -> Option<Vec<TradeTick>> {
1911 self.trades(&instrument_id)
1912 }
1913
1914 #[pyo3(name = "bars")]
1916 fn py_bars(&self, bar_type: BarType) -> Option<Vec<Bar>> {
1917 self.bars(&bar_type)
1918 }
1919
1920 #[pyo3(name = "has_quote_ticks")]
1922 fn py_has_quote_ticks(&self, instrument_id: InstrumentId) -> bool {
1923 self.has_quote_ticks(&instrument_id)
1924 }
1925
1926 #[pyo3(name = "has_trade_ticks")]
1928 fn py_has_trade_ticks(&self, instrument_id: InstrumentId) -> bool {
1929 self.has_trade_ticks(&instrument_id)
1930 }
1931
1932 #[pyo3(name = "has_mark_prices")]
1934 fn py_has_mark_prices(&self, instrument_id: InstrumentId) -> bool {
1935 self.has_mark_prices(&instrument_id)
1936 }
1937
1938 #[pyo3(name = "has_index_prices")]
1940 fn py_has_index_prices(&self, instrument_id: InstrumentId) -> bool {
1941 self.has_index_prices(&instrument_id)
1942 }
1943
1944 #[pyo3(name = "has_funding_rates")]
1946 fn py_has_funding_rates(&self, instrument_id: InstrumentId) -> bool {
1947 self.has_funding_rates(&instrument_id)
1948 }
1949
1950 #[pyo3(name = "has_instrument_statuses")]
1952 fn py_has_instrument_statuses(&self, instrument_id: InstrumentId) -> bool {
1953 self.has_instrument_statuses(&instrument_id)
1954 }
1955
1956 #[pyo3(name = "has_bars")]
1958 fn py_has_bars(&self, bar_type: BarType) -> bool {
1959 self.has_bars(&bar_type)
1960 }
1961
1962 #[pyo3(name = "quote_count")]
1964 fn py_quote_count(&self, instrument_id: InstrumentId) -> usize {
1965 self.quote_count(&instrument_id)
1966 }
1967
1968 #[pyo3(name = "trade_count")]
1970 fn py_trade_count(&self, instrument_id: InstrumentId) -> usize {
1971 self.trade_count(&instrument_id)
1972 }
1973
1974 #[pyo3(name = "mark_price_count")]
1976 fn py_mark_price_count(&self, instrument_id: InstrumentId) -> usize {
1977 self.mark_price_count(&instrument_id)
1978 }
1979
1980 #[pyo3(name = "index_price_count")]
1982 fn py_index_price_count(&self, instrument_id: InstrumentId) -> usize {
1983 self.index_price_count(&instrument_id)
1984 }
1985
1986 #[pyo3(name = "funding_rate_count")]
1988 fn py_funding_rate_count(&self, instrument_id: InstrumentId) -> usize {
1989 self.funding_rate_count(&instrument_id)
1990 }
1991
1992 #[pyo3(name = "instrument_status_count")]
1994 fn py_instrument_status_count(&self, instrument_id: InstrumentId) -> usize {
1995 self.instrument_status_count(&instrument_id)
1996 }
1997
1998 #[pyo3(name = "bar_count")]
2000 fn py_bar_count(&self, bar_type: BarType) -> usize {
2001 self.bar_count(&bar_type)
2002 }
2003
2004 #[pyo3(name = "mark_price")]
2006 fn py_mark_price(&self, instrument_id: InstrumentId) -> Option<MarkPriceUpdate> {
2007 self.mark_price(&instrument_id).copied()
2008 }
2009
2010 #[pyo3(name = "mark_prices")]
2012 fn py_mark_prices(&self, instrument_id: InstrumentId) -> Option<Vec<MarkPriceUpdate>> {
2013 self.mark_prices(&instrument_id)
2014 }
2015
2016 #[pyo3(name = "index_price")]
2018 fn py_index_price(&self, instrument_id: InstrumentId) -> Option<IndexPriceUpdate> {
2019 self.index_price(&instrument_id).copied()
2020 }
2021
2022 #[pyo3(name = "index_prices")]
2024 fn py_index_prices(&self, instrument_id: InstrumentId) -> Option<Vec<IndexPriceUpdate>> {
2025 self.index_prices(&instrument_id)
2026 }
2027
2028 #[pyo3(name = "funding_rate")]
2030 fn py_funding_rate(&self, instrument_id: InstrumentId) -> Option<FundingRateUpdate> {
2031 self.funding_rate(&instrument_id).copied()
2032 }
2033
2034 #[pyo3(name = "funding_rates")]
2036 fn py_funding_rates(&self, instrument_id: InstrumentId) -> Option<Vec<FundingRateUpdate>> {
2037 self.funding_rates(&instrument_id)
2038 }
2039
2040 #[pyo3(name = "instrument_status")]
2042 fn py_instrument_status(&self, instrument_id: InstrumentId) -> Option<InstrumentStatus> {
2043 self.instrument_status(&instrument_id).copied()
2044 }
2045
2046 #[pyo3(name = "instrument_statuses")]
2048 fn py_instrument_statuses(&self, instrument_id: InstrumentId) -> Option<Vec<InstrumentStatus>> {
2049 self.instrument_statuses(&instrument_id)
2050 }
2051
2052 #[pyo3(name = "order_book")]
2054 fn py_order_book(&self, instrument_id: InstrumentId) -> Option<OrderBook> {
2055 self.order_book(&instrument_id).cloned()
2056 }
2057
2058 #[pyo3(name = "has_order_book")]
2060 fn py_has_order_book(&self, instrument_id: InstrumentId) -> bool {
2061 self.has_order_book(&instrument_id)
2062 }
2063
2064 #[pyo3(name = "book_update_count")]
2066 fn py_book_update_count(&self, instrument_id: InstrumentId) -> usize {
2067 self.book_update_count(&instrument_id)
2068 }
2069
2070 #[pyo3(name = "synthetic")]
2072 fn py_synthetic(&self, instrument_id: InstrumentId) -> Option<SyntheticInstrument> {
2073 self.synthetic(&instrument_id).cloned()
2074 }
2075
2076 #[pyo3(name = "synthetic_ids")]
2078 fn py_synthetic_ids(&self) -> Vec<InstrumentId> {
2079 self.synthetic_ids().into_iter().copied().collect()
2080 }
2081
2082 #[pyo3(name = "client_order_ids")]
2084 fn py_client_order_ids(
2085 &self,
2086 venue: Option<Venue>,
2087 instrument_id: Option<InstrumentId>,
2088 strategy_id: Option<StrategyId>,
2089 account_id: Option<AccountId>,
2090 ) -> Vec<ClientOrderId> {
2091 self.client_order_ids(
2092 venue.as_ref(),
2093 instrument_id.as_ref(),
2094 strategy_id.as_ref(),
2095 account_id.as_ref(),
2096 )
2097 .into_iter()
2098 .collect()
2099 }
2100
2101 #[pyo3(name = "client_order_ids_open")]
2103 fn py_client_order_ids_open(
2104 &self,
2105 venue: Option<Venue>,
2106 instrument_id: Option<InstrumentId>,
2107 strategy_id: Option<StrategyId>,
2108 account_id: Option<AccountId>,
2109 ) -> Vec<ClientOrderId> {
2110 self.client_order_ids_open(
2111 venue.as_ref(),
2112 instrument_id.as_ref(),
2113 strategy_id.as_ref(),
2114 account_id.as_ref(),
2115 )
2116 .into_iter()
2117 .collect()
2118 }
2119
2120 #[pyo3(name = "client_order_ids_closed")]
2122 fn py_client_order_ids_closed(
2123 &self,
2124 venue: Option<Venue>,
2125 instrument_id: Option<InstrumentId>,
2126 strategy_id: Option<StrategyId>,
2127 account_id: Option<AccountId>,
2128 ) -> Vec<ClientOrderId> {
2129 self.client_order_ids_closed(
2130 venue.as_ref(),
2131 instrument_id.as_ref(),
2132 strategy_id.as_ref(),
2133 account_id.as_ref(),
2134 )
2135 .into_iter()
2136 .collect()
2137 }
2138
2139 #[pyo3(name = "client_order_ids_emulated")]
2141 fn py_client_order_ids_emulated(
2142 &self,
2143 venue: Option<Venue>,
2144 instrument_id: Option<InstrumentId>,
2145 strategy_id: Option<StrategyId>,
2146 account_id: Option<AccountId>,
2147 ) -> Vec<ClientOrderId> {
2148 self.client_order_ids_emulated(
2149 venue.as_ref(),
2150 instrument_id.as_ref(),
2151 strategy_id.as_ref(),
2152 account_id.as_ref(),
2153 )
2154 .into_iter()
2155 .collect()
2156 }
2157
2158 #[pyo3(name = "client_order_ids_inflight")]
2160 fn py_client_order_ids_inflight(
2161 &self,
2162 venue: Option<Venue>,
2163 instrument_id: Option<InstrumentId>,
2164 strategy_id: Option<StrategyId>,
2165 account_id: Option<AccountId>,
2166 ) -> Vec<ClientOrderId> {
2167 self.client_order_ids_inflight(
2168 venue.as_ref(),
2169 instrument_id.as_ref(),
2170 strategy_id.as_ref(),
2171 account_id.as_ref(),
2172 )
2173 .into_iter()
2174 .collect()
2175 }
2176
2177 #[pyo3(name = "position_ids")]
2179 fn py_position_ids(
2180 &self,
2181 venue: Option<Venue>,
2182 instrument_id: Option<InstrumentId>,
2183 strategy_id: Option<StrategyId>,
2184 account_id: Option<AccountId>,
2185 ) -> Vec<PositionId> {
2186 self.position_ids(
2187 venue.as_ref(),
2188 instrument_id.as_ref(),
2189 strategy_id.as_ref(),
2190 account_id.as_ref(),
2191 )
2192 .into_iter()
2193 .collect()
2194 }
2195
2196 #[pyo3(name = "position_open_ids")]
2198 fn py_position_open_ids(
2199 &self,
2200 venue: Option<Venue>,
2201 instrument_id: Option<InstrumentId>,
2202 strategy_id: Option<StrategyId>,
2203 account_id: Option<AccountId>,
2204 ) -> Vec<PositionId> {
2205 self.position_open_ids(
2206 venue.as_ref(),
2207 instrument_id.as_ref(),
2208 strategy_id.as_ref(),
2209 account_id.as_ref(),
2210 )
2211 .into_iter()
2212 .collect()
2213 }
2214
2215 #[pyo3(name = "position_closed_ids")]
2217 fn py_position_closed_ids(
2218 &self,
2219 venue: Option<Venue>,
2220 instrument_id: Option<InstrumentId>,
2221 strategy_id: Option<StrategyId>,
2222 account_id: Option<AccountId>,
2223 ) -> Vec<PositionId> {
2224 self.position_closed_ids(
2225 venue.as_ref(),
2226 instrument_id.as_ref(),
2227 strategy_id.as_ref(),
2228 account_id.as_ref(),
2229 )
2230 .into_iter()
2231 .collect()
2232 }
2233
2234 #[pyo3(name = "strategy_ids")]
2236 fn py_strategy_ids(&self) -> Vec<StrategyId> {
2237 self.strategy_ids().into_iter().collect()
2238 }
2239
2240 #[pyo3(name = "exec_algorithm_ids")]
2242 fn py_exec_algorithm_ids(&self) -> Vec<ExecAlgorithmId> {
2243 self.exec_algorithm_ids().into_iter().collect()
2244 }
2245
2246 #[pyo3(name = "client_order_id")]
2248 fn py_client_order_id(&self, venue_order_id: VenueOrderId) -> Option<ClientOrderId> {
2249 self.client_order_id(&venue_order_id).copied()
2250 }
2251
2252 #[pyo3(name = "venue_order_id")]
2254 fn py_venue_order_id(&self, client_order_id: ClientOrderId) -> Option<VenueOrderId> {
2255 self.venue_order_id(&client_order_id).copied()
2256 }
2257
2258 #[pyo3(name = "client_id")]
2260 fn py_client_id(&self, client_order_id: ClientOrderId) -> Option<ClientId> {
2261 self.client_id(&client_order_id).copied()
2262 }
2263
2264 #[pyo3(name = "orders")]
2268 fn py_orders(
2269 &self,
2270 py: Python,
2271 venue: Option<Venue>,
2272 instrument_id: Option<InstrumentId>,
2273 strategy_id: Option<StrategyId>,
2274 account_id: Option<AccountId>,
2275 side: Option<OrderSide>,
2276 ) -> PyResult<Vec<Py<PyAny>>> {
2277 self.orders(
2278 venue.as_ref(),
2279 instrument_id.as_ref(),
2280 strategy_id.as_ref(),
2281 account_id.as_ref(),
2282 side,
2283 )
2284 .into_iter()
2285 .map(|o| order_any_to_pyobject(py, o.clone()))
2286 .collect()
2287 }
2288
2289 #[pyo3(name = "orders_open")]
2293 fn py_orders_open(
2294 &self,
2295 py: Python,
2296 venue: Option<Venue>,
2297 instrument_id: Option<InstrumentId>,
2298 strategy_id: Option<StrategyId>,
2299 account_id: Option<AccountId>,
2300 side: Option<OrderSide>,
2301 ) -> PyResult<Vec<Py<PyAny>>> {
2302 self.orders_open(
2303 venue.as_ref(),
2304 instrument_id.as_ref(),
2305 strategy_id.as_ref(),
2306 account_id.as_ref(),
2307 side,
2308 )
2309 .into_iter()
2310 .map(|o| order_any_to_pyobject(py, o.clone()))
2311 .collect()
2312 }
2313
2314 #[pyo3(name = "orders_closed")]
2318 fn py_orders_closed(
2319 &self,
2320 py: Python,
2321 venue: Option<Venue>,
2322 instrument_id: Option<InstrumentId>,
2323 strategy_id: Option<StrategyId>,
2324 account_id: Option<AccountId>,
2325 side: Option<OrderSide>,
2326 ) -> PyResult<Vec<Py<PyAny>>> {
2327 self.orders_closed(
2328 venue.as_ref(),
2329 instrument_id.as_ref(),
2330 strategy_id.as_ref(),
2331 account_id.as_ref(),
2332 side,
2333 )
2334 .into_iter()
2335 .map(|o| order_any_to_pyobject(py, o.clone()))
2336 .collect()
2337 }
2338
2339 #[pyo3(name = "orders_emulated")]
2343 fn py_orders_emulated(
2344 &self,
2345 py: Python,
2346 venue: Option<Venue>,
2347 instrument_id: Option<InstrumentId>,
2348 strategy_id: Option<StrategyId>,
2349 account_id: Option<AccountId>,
2350 side: Option<OrderSide>,
2351 ) -> PyResult<Vec<Py<PyAny>>> {
2352 self.orders_emulated(
2353 venue.as_ref(),
2354 instrument_id.as_ref(),
2355 strategy_id.as_ref(),
2356 account_id.as_ref(),
2357 side,
2358 )
2359 .into_iter()
2360 .map(|o| order_any_to_pyobject(py, o.clone()))
2361 .collect()
2362 }
2363
2364 #[pyo3(name = "orders_inflight")]
2368 fn py_orders_inflight(
2369 &self,
2370 py: Python,
2371 venue: Option<Venue>,
2372 instrument_id: Option<InstrumentId>,
2373 strategy_id: Option<StrategyId>,
2374 account_id: Option<AccountId>,
2375 side: Option<OrderSide>,
2376 ) -> PyResult<Vec<Py<PyAny>>> {
2377 self.orders_inflight(
2378 venue.as_ref(),
2379 instrument_id.as_ref(),
2380 strategy_id.as_ref(),
2381 account_id.as_ref(),
2382 side,
2383 )
2384 .into_iter()
2385 .map(|o| order_any_to_pyobject(py, o.clone()))
2386 .collect()
2387 }
2388
2389 #[pyo3(name = "orders_for_position")]
2391 fn py_orders_for_position(
2392 &self,
2393 py: Python,
2394 position_id: PositionId,
2395 ) -> PyResult<Vec<Py<PyAny>>> {
2396 self.orders_for_position(&position_id)
2397 .into_iter()
2398 .map(|o| order_any_to_pyobject(py, o.clone()))
2399 .collect()
2400 }
2401
2402 #[pyo3(name = "is_order_emulated")]
2404 fn py_is_order_emulated(&self, client_order_id: ClientOrderId) -> bool {
2405 self.is_order_emulated(&client_order_id)
2406 }
2407
2408 #[pyo3(name = "is_order_inflight")]
2410 fn py_is_order_inflight(&self, client_order_id: ClientOrderId) -> bool {
2411 self.is_order_inflight(&client_order_id)
2412 }
2413
2414 #[pyo3(name = "is_order_pending_cancel_local")]
2416 fn py_is_order_pending_cancel_local(&self, client_order_id: ClientOrderId) -> bool {
2417 self.is_order_pending_cancel_local(&client_order_id)
2418 }
2419
2420 #[pyo3(name = "orders_emulated_count")]
2422 fn py_orders_emulated_count(
2423 &self,
2424 venue: Option<Venue>,
2425 instrument_id: Option<InstrumentId>,
2426 strategy_id: Option<StrategyId>,
2427 account_id: Option<AccountId>,
2428 side: Option<OrderSide>,
2429 ) -> usize {
2430 self.orders_emulated_count(
2431 venue.as_ref(),
2432 instrument_id.as_ref(),
2433 strategy_id.as_ref(),
2434 account_id.as_ref(),
2435 side,
2436 )
2437 }
2438
2439 #[pyo3(name = "orders_inflight_count")]
2441 fn py_orders_inflight_count(
2442 &self,
2443 venue: Option<Venue>,
2444 instrument_id: Option<InstrumentId>,
2445 strategy_id: Option<StrategyId>,
2446 account_id: Option<AccountId>,
2447 side: Option<OrderSide>,
2448 ) -> usize {
2449 self.orders_inflight_count(
2450 venue.as_ref(),
2451 instrument_id.as_ref(),
2452 strategy_id.as_ref(),
2453 account_id.as_ref(),
2454 side,
2455 )
2456 }
2457
2458 #[pyo3(name = "order_list")]
2460 fn py_order_list(&self, order_list_id: OrderListId) -> Option<OrderList> {
2461 self.order_list(&order_list_id).cloned()
2462 }
2463
2464 #[pyo3(name = "order_lists")]
2466 fn py_order_lists(
2467 &self,
2468 venue: Option<Venue>,
2469 instrument_id: Option<InstrumentId>,
2470 strategy_id: Option<StrategyId>,
2471 account_id: Option<AccountId>,
2472 ) -> Vec<OrderList> {
2473 self.order_lists(
2474 venue.as_ref(),
2475 instrument_id.as_ref(),
2476 strategy_id.as_ref(),
2477 account_id.as_ref(),
2478 )
2479 .into_iter()
2480 .cloned()
2481 .collect()
2482 }
2483
2484 #[pyo3(name = "order_list_exists")]
2486 fn py_order_list_exists(&self, order_list_id: OrderListId) -> bool {
2487 self.order_list_exists(&order_list_id)
2488 }
2489
2490 #[pyo3(name = "orders_for_exec_algorithm")]
2493 #[expect(clippy::too_many_arguments)]
2494 fn py_orders_for_exec_algorithm(
2495 &self,
2496 py: Python,
2497 exec_algorithm_id: ExecAlgorithmId,
2498 venue: Option<Venue>,
2499 instrument_id: Option<InstrumentId>,
2500 strategy_id: Option<StrategyId>,
2501 account_id: Option<AccountId>,
2502 side: Option<OrderSide>,
2503 ) -> PyResult<Vec<Py<PyAny>>> {
2504 self.orders_for_exec_algorithm(
2505 &exec_algorithm_id,
2506 venue.as_ref(),
2507 instrument_id.as_ref(),
2508 strategy_id.as_ref(),
2509 account_id.as_ref(),
2510 side,
2511 )
2512 .into_iter()
2513 .map(|o| order_any_to_pyobject(py, o.clone()))
2514 .collect()
2515 }
2516
2517 #[pyo3(name = "orders_for_exec_spawn")]
2519 fn py_orders_for_exec_spawn(
2520 &self,
2521 py: Python,
2522 exec_spawn_id: ClientOrderId,
2523 ) -> PyResult<Vec<Py<PyAny>>> {
2524 self.orders_for_exec_spawn(&exec_spawn_id)
2525 .into_iter()
2526 .map(|o| order_any_to_pyobject(py, o.clone()))
2527 .collect()
2528 }
2529
2530 #[pyo3(name = "exec_spawn_total_quantity")]
2532 fn py_exec_spawn_total_quantity(
2533 &self,
2534 exec_spawn_id: ClientOrderId,
2535 active_only: bool,
2536 ) -> Option<Quantity> {
2537 self.exec_spawn_total_quantity(&exec_spawn_id, active_only)
2538 }
2539
2540 #[pyo3(name = "exec_spawn_total_filled_qty")]
2542 fn py_exec_spawn_total_filled_qty(
2543 &self,
2544 exec_spawn_id: ClientOrderId,
2545 active_only: bool,
2546 ) -> Option<Quantity> {
2547 self.exec_spawn_total_filled_qty(&exec_spawn_id, active_only)
2548 }
2549
2550 #[pyo3(name = "exec_spawn_total_leaves_qty")]
2552 fn py_exec_spawn_total_leaves_qty(
2553 &self,
2554 exec_spawn_id: ClientOrderId,
2555 active_only: bool,
2556 ) -> Option<Quantity> {
2557 self.exec_spawn_total_leaves_qty(&exec_spawn_id, active_only)
2558 }
2559
2560 #[pyo3(name = "position_for_order")]
2564 fn py_position_for_order(
2565 &self,
2566 py: Python,
2567 client_order_id: ClientOrderId,
2568 ) -> PyResult<Option<Py<PyAny>>> {
2569 match self.position_for_order(&client_order_id) {
2570 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
2571 None => Ok(None),
2572 }
2573 }
2574
2575 #[pyo3(name = "position_id")]
2577 fn py_position_id(&self, client_order_id: ClientOrderId) -> Option<PositionId> {
2578 self.position_id(&client_order_id).copied()
2579 }
2580
2581 #[pyo3(name = "positions")]
2585 fn py_positions(
2586 &self,
2587 py: Python,
2588 venue: Option<Venue>,
2589 instrument_id: Option<InstrumentId>,
2590 strategy_id: Option<StrategyId>,
2591 account_id: Option<AccountId>,
2592 side: Option<PositionSide>,
2593 ) -> PyResult<Vec<Py<PyAny>>> {
2594 self.positions(
2595 venue.as_ref(),
2596 instrument_id.as_ref(),
2597 strategy_id.as_ref(),
2598 account_id.as_ref(),
2599 side,
2600 )
2601 .into_iter()
2602 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
2603 .collect()
2604 }
2605
2606 #[pyo3(name = "positions_open")]
2610 fn py_positions_open(
2611 &self,
2612 py: Python,
2613 venue: Option<Venue>,
2614 instrument_id: Option<InstrumentId>,
2615 strategy_id: Option<StrategyId>,
2616 account_id: Option<AccountId>,
2617 side: Option<PositionSide>,
2618 ) -> PyResult<Vec<Py<PyAny>>> {
2619 self.positions_open(
2620 venue.as_ref(),
2621 instrument_id.as_ref(),
2622 strategy_id.as_ref(),
2623 account_id.as_ref(),
2624 side,
2625 )
2626 .into_iter()
2627 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
2628 .collect()
2629 }
2630
2631 #[pyo3(name = "positions_closed")]
2635 fn py_positions_closed(
2636 &self,
2637 py: Python,
2638 venue: Option<Venue>,
2639 instrument_id: Option<InstrumentId>,
2640 strategy_id: Option<StrategyId>,
2641 account_id: Option<AccountId>,
2642 side: Option<PositionSide>,
2643 ) -> PyResult<Vec<Py<PyAny>>> {
2644 self.positions_closed(
2645 venue.as_ref(),
2646 instrument_id.as_ref(),
2647 strategy_id.as_ref(),
2648 account_id.as_ref(),
2649 side,
2650 )
2651 .into_iter()
2652 .map(|p| Ok(p.clone().into_pyobject(py)?.into()))
2653 .collect()
2654 }
2655
2656 #[pyo3(name = "strategy_id_for_order")]
2658 fn py_strategy_id_for_order(&self, client_order_id: ClientOrderId) -> Option<StrategyId> {
2659 self.strategy_id_for_order(&client_order_id).copied()
2660 }
2661
2662 #[pyo3(name = "strategy_id_for_position")]
2664 fn py_strategy_id_for_position(&self, position_id: PositionId) -> Option<StrategyId> {
2665 self.strategy_id_for_position(&position_id).copied()
2666 }
2667
2668 #[pyo3(name = "position_snapshot_bytes")]
2673 fn py_position_snapshot_bytes(&self, position_id: PositionId) -> Option<Vec<Vec<u8>>> {
2674 self.position_snapshot_bytes(&position_id)
2675 }
2676
2677 #[pyo3(name = "position_snapshots", signature = (position_id=None, account_id=None))]
2682 fn py_position_snapshots(
2683 &self,
2684 py: Python,
2685 position_id: Option<PositionId>,
2686 account_id: Option<AccountId>,
2687 ) -> PyResult<Vec<Py<PyAny>>> {
2688 self.position_snapshots(position_id.as_ref(), account_id.as_ref())
2689 .into_iter()
2690 .map(|p| Ok(p.into_pyobject(py)?.into()))
2691 .collect()
2692 }
2693
2694 #[pyo3(name = "account")]
2698 fn py_account(&self, py: Python, account_id: AccountId) -> PyResult<Option<Py<PyAny>>> {
2699 match self.account(&account_id) {
2700 Some(account) => Ok(Some(account_any_to_pyobject(py, account.clone())?)),
2701 None => Ok(None),
2702 }
2703 }
2704
2705 #[pyo3(name = "account_for_venue")]
2707 fn py_account_for_venue(&self, py: Python, venue: Venue) -> PyResult<Option<Py<PyAny>>> {
2708 match self.account_for_venue(&venue) {
2709 Some(account) => Ok(Some(account_any_to_pyobject(py, account.clone())?)),
2710 None => Ok(None),
2711 }
2712 }
2713
2714 #[pyo3(name = "account_id")]
2716 fn py_account_id(&self, venue: Venue) -> Option<AccountId> {
2717 self.account_id(&venue).copied()
2718 }
2719
2720 #[pyo3(name = "get")]
2726 fn py_get(&self, key: &str) -> PyResult<Option<Vec<u8>>> {
2727 match self.get(key).map_err(to_pyvalue_err)? {
2728 Some(bytes) => Ok(Some(bytes.to_vec())),
2729 None => Ok(None),
2730 }
2731 }
2732
2733 #[pyo3(name = "add")]
2735 fn py_add_general(&mut self, key: &str, value: Vec<u8>) -> PyResult<()> {
2736 self.add(key, Bytes::from(value)).map_err(to_pyvalue_err)
2737 }
2738
2739 #[pyo3(name = "price")]
2741 fn py_price(&self, instrument_id: InstrumentId, price_type: PriceType) -> Option<Price> {
2742 self.price(&instrument_id, price_type)
2743 }
2744
2745 #[pyo3(name = "get_xrate")]
2747 fn py_get_xrate(
2748 &self,
2749 venue: Venue,
2750 from_currency: Currency,
2751 to_currency: Currency,
2752 price_type: PriceType,
2753 ) -> Option<f64> {
2754 self.get_xrate(venue, from_currency, to_currency, price_type)
2755 .and_then(|rate| rate.to_f64())
2756 }
2757
2758 #[pyo3(name = "get_mark_xrate")]
2760 fn py_get_mark_xrate(&self, from_currency: Currency, to_currency: Currency) -> Option<f64> {
2761 self.get_mark_xrate(from_currency, to_currency)
2762 }
2763
2764 #[pyo3(name = "set_mark_xrate")]
2766 fn py_set_mark_xrate(&mut self, from_currency: Currency, to_currency: Currency, xrate: f64) {
2767 self.set_mark_xrate(from_currency, to_currency, xrate);
2768 }
2769
2770 #[pyo3(name = "clear_mark_xrate")]
2776 fn py_clear_mark_xrate(&mut self, from_currency: Currency, to_currency: Currency) {
2777 self.clear_mark_xrate(from_currency, to_currency);
2778 }
2779
2780 #[pyo3(name = "clear_mark_xrates")]
2782 fn py_clear_mark_xrates(&mut self) {
2783 self.clear_mark_xrates();
2784 }
2785
2786 #[pyo3(name = "calculate_unrealized_pnl")]
2788 #[expect(clippy::needless_pass_by_value)]
2789 fn py_calculate_unrealized_pnl(
2790 &self,
2791 py: Python,
2792 position: Py<PyAny>,
2793 ) -> PyResult<Option<Money>> {
2794 let position = position.extract::<Position>(py)?;
2795 Ok(self.calculate_unrealized_pnl(&position))
2796 }
2797
2798 #[pyo3(name = "own_order_book")]
2800 fn py_own_order_book(&self, instrument_id: InstrumentId) -> Option<OwnOrderBook> {
2801 self.own_order_book(&instrument_id).cloned()
2802 }
2803
2804 #[pyo3(name = "update_own_order_book")]
2812 fn py_update_own_order_book(&mut self, py: Python, order: Py<PyAny>) -> PyResult<()> {
2813 let order_any = pyobject_to_order_any(py, order)?;
2814 self.update_own_order_book(&order_any);
2815 Ok(())
2816 }
2817
2818 #[pyo3(name = "force_remove_from_own_order_book")]
2824 fn py_force_remove_from_own_order_book(&mut self, client_order_id: ClientOrderId) {
2825 self.force_remove_from_own_order_book(&client_order_id);
2826 }
2827
2828 #[pyo3(name = "audit_own_order_books")]
2833 fn py_audit_own_order_books(&mut self) {
2834 self.audit_own_order_books();
2835 }
2836}
2837
2838#[cfg(feature = "defi")]
2839#[pymethods]
2840impl Cache {
2841 #[pyo3(name = "add_pool")]
2847 fn py_add_pool(&mut self, pool: Pool) -> PyResult<()> {
2848 self.add_pool(pool).map_err(to_pyvalue_err)
2849 }
2850
2851 #[pyo3(name = "pool")]
2853 fn py_pool(&self, instrument_id: InstrumentId) -> Option<Pool> {
2854 self.pool(&instrument_id).cloned()
2855 }
2856
2857 #[pyo3(name = "pool_ids")]
2859 fn py_pool_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
2860 self.pool_ids(venue.as_ref())
2861 }
2862
2863 #[pyo3(name = "pools")]
2865 fn py_pools(&self, venue: Option<Venue>) -> Vec<Pool> {
2866 self.pools(venue.as_ref()).into_iter().cloned().collect()
2867 }
2868
2869 #[pyo3(name = "add_pool_profiler")]
2875 fn py_add_pool_profiler(&mut self, pool_profiler: PoolProfiler) -> PyResult<()> {
2876 self.add_pool_profiler(pool_profiler)
2877 .map_err(to_pyvalue_err)
2878 }
2879
2880 #[pyo3(name = "pool_profiler")]
2882 fn py_pool_profiler(&self, instrument_id: InstrumentId) -> Option<PoolProfiler> {
2883 self.pool_profiler(&instrument_id).cloned()
2884 }
2885
2886 #[pyo3(name = "pool_profiler_ids")]
2888 fn py_pool_profiler_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
2889 self.pool_profiler_ids(venue.as_ref())
2890 }
2891
2892 #[pyo3(name = "pool_profilers")]
2894 fn py_pool_profilers(&self, venue: Option<Venue>) -> Vec<PoolProfiler> {
2895 self.pool_profilers(venue.as_ref())
2896 .into_iter()
2897 .cloned()
2898 .collect()
2899 }
2900}