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