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