1use nautilus_core::python::to_pyvalue_err;
19use nautilus_model::{
20 data::{
21 Bar, BarType, FundingRateUpdate, QuoteTick, TradeTick,
22 prices::{IndexPriceUpdate, MarkPriceUpdate},
23 },
24 enums::{OmsType, OrderSide, PositionSide},
25 identifiers::{ClientId, ClientOrderId, InstrumentId, PositionId, StrategyId, Venue},
26 instruments::SyntheticInstrument,
27 orderbook::OrderBook,
28 position::Position,
29 python::{
30 instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
31 orders::{order_any_to_pyobject, pyobject_to_order_any},
32 },
33 types::Currency,
34};
35use pyo3::prelude::*;
36
37use crate::{
38 cache::{Cache, CacheConfig},
39 enums::SerializationEncoding,
40};
41
42#[pymethods]
43impl CacheConfig {
44 #[new]
45 #[allow(clippy::too_many_arguments)]
46 fn py_new(
47 encoding: Option<SerializationEncoding>,
48 timestamps_as_iso8601: Option<bool>,
49 buffer_interval_ms: Option<usize>,
50 use_trader_prefix: Option<bool>,
51 use_instance_id: Option<bool>,
52 flush_on_start: Option<bool>,
53 drop_instruments_on_reset: Option<bool>,
54 tick_capacity: Option<usize>,
55 bar_capacity: Option<usize>,
56 save_market_data: Option<bool>,
57 ) -> Self {
58 Self::new(
59 None, encoding.unwrap_or(SerializationEncoding::MsgPack),
61 timestamps_as_iso8601.unwrap_or(false),
62 buffer_interval_ms,
63 use_trader_prefix.unwrap_or(true),
64 use_instance_id.unwrap_or(false),
65 flush_on_start.unwrap_or(false),
66 drop_instruments_on_reset.unwrap_or(true),
67 tick_capacity.unwrap_or(10_000),
68 bar_capacity.unwrap_or(10_000),
69 save_market_data.unwrap_or(false),
70 )
71 }
72
73 fn __str__(&self) -> String {
74 format!("{self:?}")
75 }
76
77 fn __repr__(&self) -> String {
78 format!("{self:?}")
79 }
80
81 #[getter]
82 fn encoding(&self) -> SerializationEncoding {
83 self.encoding
84 }
85
86 #[getter]
87 fn timestamps_as_iso8601(&self) -> bool {
88 self.timestamps_as_iso8601
89 }
90
91 #[getter]
92 fn buffer_interval_ms(&self) -> Option<usize> {
93 self.buffer_interval_ms
94 }
95
96 #[getter]
97 fn use_trader_prefix(&self) -> bool {
98 self.use_trader_prefix
99 }
100
101 #[getter]
102 fn use_instance_id(&self) -> bool {
103 self.use_instance_id
104 }
105
106 #[getter]
107 fn flush_on_start(&self) -> bool {
108 self.flush_on_start
109 }
110
111 #[getter]
112 fn drop_instruments_on_reset(&self) -> bool {
113 self.drop_instruments_on_reset
114 }
115
116 #[getter]
117 fn tick_capacity(&self) -> usize {
118 self.tick_capacity
119 }
120
121 #[getter]
122 fn bar_capacity(&self) -> usize {
123 self.bar_capacity
124 }
125
126 #[getter]
127 fn save_market_data(&self) -> bool {
128 self.save_market_data
129 }
130}
131
132#[pymethods]
133impl Cache {
134 #[new]
135 fn py_new(config: Option<CacheConfig>) -> Self {
136 Self::new(config, None)
137 }
138
139 fn __repr__(&self) -> String {
140 format!("{self:?}")
141 }
142
143 #[pyo3(name = "reset")]
144 fn py_reset(&mut self) {
145 self.reset();
146 }
147
148 #[pyo3(name = "dispose")]
149 fn py_dispose(&mut self) {
150 self.dispose();
151 }
152
153 #[pyo3(name = "add_currency")]
154 fn py_add_currency(&mut self, currency: Currency) -> PyResult<()> {
155 self.add_currency(currency).map_err(to_pyvalue_err)
156 }
157
158 #[pyo3(name = "add_instrument")]
159 fn py_add_instrument(&mut self, py: Python, instrument: PyObject) -> PyResult<()> {
160 let instrument_any = pyobject_to_instrument_any(py, instrument)?;
161 self.add_instrument(instrument_any).map_err(to_pyvalue_err)
162 }
163
164 #[pyo3(name = "instrument")]
165 fn py_instrument(&self, py: Python, instrument_id: InstrumentId) -> PyResult<Option<PyObject>> {
166 match self.instrument(&instrument_id) {
167 Some(instrument) => Ok(Some(instrument_any_to_pyobject(py, instrument.clone())?)),
168 None => Ok(None),
169 }
170 }
171
172 #[pyo3(name = "instrument_ids")]
173 fn py_instrument_ids(&self, venue: Option<Venue>) -> Vec<InstrumentId> {
174 self.instrument_ids(venue.as_ref())
175 .into_iter()
176 .cloned()
177 .collect()
178 }
179
180 #[pyo3(name = "instruments")]
181 fn py_instruments(&self, py: Python, venue: Option<Venue>) -> PyResult<Vec<PyObject>> {
182 let mut py_instruments = Vec::new();
183
184 match venue {
185 Some(venue) => {
186 let instruments = self.instruments(&venue, None);
187 for instrument in instruments {
188 py_instruments.push(instrument_any_to_pyobject(py, (*instrument).clone())?);
189 }
190 }
191 None => {
192 let instrument_ids = self.instrument_ids(None);
194 for instrument_id in instrument_ids {
195 if let Some(instrument) = self.instrument(instrument_id) {
196 py_instruments.push(instrument_any_to_pyobject(py, instrument.clone())?);
197 }
198 }
199 }
200 }
201
202 Ok(py_instruments)
203 }
204
205 #[pyo3(name = "add_order")]
206 fn py_add_order(
207 &mut self,
208 py: Python,
209 order: PyObject,
210 position_id: Option<PositionId>,
211 client_id: Option<ClientId>,
212 replace_existing: Option<bool>,
213 ) -> PyResult<()> {
214 let order_any = pyobject_to_order_any(py, order)?;
215 self.add_order(
216 order_any,
217 position_id,
218 client_id,
219 replace_existing.unwrap_or(false),
220 )
221 .map_err(to_pyvalue_err)
222 }
223
224 #[pyo3(name = "order")]
225 fn py_order(&self, py: Python, client_order_id: ClientOrderId) -> PyResult<Option<PyObject>> {
226 match self.order(&client_order_id) {
227 Some(order) => Ok(Some(order_any_to_pyobject(py, order.clone())?)),
228 None => Ok(None),
229 }
230 }
231
232 #[pyo3(name = "order_exists")]
233 fn py_order_exists(&self, client_order_id: ClientOrderId) -> bool {
234 self.order_exists(&client_order_id)
235 }
236
237 #[pyo3(name = "is_order_open")]
238 fn py_is_order_open(&self, client_order_id: ClientOrderId) -> bool {
239 self.is_order_open(&client_order_id)
240 }
241
242 #[pyo3(name = "is_order_closed")]
243 fn py_is_order_closed(&self, client_order_id: ClientOrderId) -> bool {
244 self.is_order_closed(&client_order_id)
245 }
246
247 #[pyo3(name = "orders_open_count")]
248 fn py_orders_open_count(
249 &self,
250 venue: Option<Venue>,
251 instrument_id: Option<InstrumentId>,
252 strategy_id: Option<StrategyId>,
253 side: Option<OrderSide>,
254 ) -> usize {
255 self.orders_open_count(
256 venue.as_ref(),
257 instrument_id.as_ref(),
258 strategy_id.as_ref(),
259 side,
260 )
261 }
262
263 #[pyo3(name = "orders_closed_count")]
264 fn py_orders_closed_count(
265 &self,
266 venue: Option<Venue>,
267 instrument_id: Option<InstrumentId>,
268 strategy_id: Option<StrategyId>,
269 side: Option<OrderSide>,
270 ) -> usize {
271 self.orders_closed_count(
272 venue.as_ref(),
273 instrument_id.as_ref(),
274 strategy_id.as_ref(),
275 side,
276 )
277 }
278
279 #[pyo3(name = "orders_total_count")]
280 fn py_orders_total_count(
281 &self,
282 venue: Option<Venue>,
283 instrument_id: Option<InstrumentId>,
284 strategy_id: Option<StrategyId>,
285 side: Option<OrderSide>,
286 ) -> usize {
287 self.orders_total_count(
288 venue.as_ref(),
289 instrument_id.as_ref(),
290 strategy_id.as_ref(),
291 side,
292 )
293 }
294
295 #[pyo3(name = "add_position")]
296 fn py_add_position(
297 &mut self,
298 py: Python,
299 position: PyObject,
300 oms_type: OmsType,
301 ) -> PyResult<()> {
302 let position_obj = position.extract::<Position>(py)?;
303 self.add_position(position_obj, oms_type)
304 .map_err(to_pyvalue_err)
305 }
306
307 #[pyo3(name = "position")]
308 fn py_position(&self, py: Python, position_id: PositionId) -> PyResult<Option<PyObject>> {
309 match self.position(&position_id) {
310 Some(position) => Ok(Some(position.clone().into_pyobject(py)?.into())),
311 None => Ok(None),
312 }
313 }
314
315 #[pyo3(name = "position_exists")]
316 fn py_position_exists(&self, position_id: PositionId) -> bool {
317 self.position_exists(&position_id)
318 }
319
320 #[pyo3(name = "is_position_open")]
321 fn py_is_position_open(&self, position_id: PositionId) -> bool {
322 self.is_position_open(&position_id)
323 }
324
325 #[pyo3(name = "is_position_closed")]
326 fn py_is_position_closed(&self, position_id: PositionId) -> bool {
327 self.is_position_closed(&position_id)
328 }
329
330 #[pyo3(name = "positions_open_count")]
331 fn py_positions_open_count(
332 &self,
333 venue: Option<Venue>,
334 instrument_id: Option<InstrumentId>,
335 strategy_id: Option<StrategyId>,
336 side: Option<PositionSide>,
337 ) -> usize {
338 self.positions_open_count(
339 venue.as_ref(),
340 instrument_id.as_ref(),
341 strategy_id.as_ref(),
342 side,
343 )
344 }
345
346 #[pyo3(name = "positions_closed_count")]
347 fn py_positions_closed_count(
348 &self,
349 venue: Option<Venue>,
350 instrument_id: Option<InstrumentId>,
351 strategy_id: Option<StrategyId>,
352 side: Option<PositionSide>,
353 ) -> usize {
354 self.positions_closed_count(
355 venue.as_ref(),
356 instrument_id.as_ref(),
357 strategy_id.as_ref(),
358 side,
359 )
360 }
361
362 #[pyo3(name = "positions_total_count")]
363 fn py_positions_total_count(
364 &self,
365 venue: Option<Venue>,
366 instrument_id: Option<InstrumentId>,
367 strategy_id: Option<StrategyId>,
368 side: Option<PositionSide>,
369 ) -> usize {
370 self.positions_total_count(
371 venue.as_ref(),
372 instrument_id.as_ref(),
373 strategy_id.as_ref(),
374 side,
375 )
376 }
377
378 #[pyo3(name = "add_quote")]
379 fn py_add_quote(&mut self, quote: QuoteTick) -> PyResult<()> {
380 self.add_quote(quote).map_err(to_pyvalue_err)
381 }
382
383 #[pyo3(name = "add_trade")]
384 fn py_add_trade(&mut self, trade: TradeTick) -> PyResult<()> {
385 self.add_trade(trade).map_err(to_pyvalue_err)
386 }
387
388 #[pyo3(name = "add_bar")]
389 fn py_add_bar(&mut self, bar: Bar) -> PyResult<()> {
390 self.add_bar(bar).map_err(to_pyvalue_err)
391 }
392
393 #[pyo3(name = "quote")]
394 fn py_quote(&self, instrument_id: InstrumentId) -> Option<QuoteTick> {
395 self.quote(&instrument_id).cloned()
396 }
397
398 #[pyo3(name = "trade")]
399 fn py_trade(&self, instrument_id: InstrumentId) -> Option<TradeTick> {
400 self.trade(&instrument_id).cloned()
401 }
402
403 #[pyo3(name = "bar")]
404 fn py_bar(&self, bar_type: BarType) -> Option<Bar> {
405 self.bar(&bar_type).cloned()
406 }
407
408 #[pyo3(name = "quotes")]
409 fn py_quotes(&self, instrument_id: InstrumentId) -> Option<Vec<QuoteTick>> {
410 self.quotes(&instrument_id).map(|deque| deque.to_vec())
411 }
412
413 #[pyo3(name = "trades")]
414 fn py_trades(&self, instrument_id: InstrumentId) -> Option<Vec<TradeTick>> {
415 self.trades(&instrument_id).map(|deque| deque.to_vec())
416 }
417
418 #[pyo3(name = "bars")]
419 fn py_bars(&self, bar_type: BarType) -> Option<Vec<Bar>> {
420 self.bars(&bar_type).map(|deque| deque.to_vec())
421 }
422
423 #[pyo3(name = "has_quote_ticks")]
424 fn py_has_quote_ticks(&self, instrument_id: InstrumentId) -> bool {
425 self.has_quote_ticks(&instrument_id)
426 }
427
428 #[pyo3(name = "has_trade_ticks")]
429 fn py_has_trade_ticks(&self, instrument_id: InstrumentId) -> bool {
430 self.has_trade_ticks(&instrument_id)
431 }
432
433 #[pyo3(name = "has_bars")]
434 fn py_has_bars(&self, bar_type: BarType) -> bool {
435 self.has_bars(&bar_type)
436 }
437
438 #[pyo3(name = "quote_count")]
439 fn py_quote_count(&self, instrument_id: InstrumentId) -> usize {
440 self.quote_count(&instrument_id)
441 }
442
443 #[pyo3(name = "trade_count")]
444 fn py_trade_count(&self, instrument_id: InstrumentId) -> usize {
445 self.trade_count(&instrument_id)
446 }
447
448 #[pyo3(name = "bar_count")]
449 fn py_bar_count(&self, bar_type: BarType) -> usize {
450 self.bar_count(&bar_type)
451 }
452
453 #[pyo3(name = "mark_price")]
454 fn py_mark_price(&self, instrument_id: InstrumentId) -> Option<MarkPriceUpdate> {
455 self.mark_price(&instrument_id).cloned()
456 }
457
458 #[pyo3(name = "mark_prices")]
459 fn py_mark_prices(&self, instrument_id: InstrumentId) -> Option<Vec<MarkPriceUpdate>> {
460 self.mark_prices(&instrument_id)
461 }
462
463 #[pyo3(name = "index_price")]
464 fn py_index_price(&self, instrument_id: InstrumentId) -> Option<IndexPriceUpdate> {
465 self.index_price(&instrument_id).cloned()
466 }
467
468 #[pyo3(name = "index_prices")]
469 fn py_index_prices(&self, instrument_id: InstrumentId) -> Option<Vec<IndexPriceUpdate>> {
470 self.index_prices(&instrument_id)
471 }
472
473 #[pyo3(name = "funding_rate")]
474 fn py_funding_rate(&self, instrument_id: InstrumentId) -> Option<FundingRateUpdate> {
475 self.funding_rate(&instrument_id).cloned()
476 }
477
478 #[pyo3(name = "order_book")]
479 fn py_order_book(&self, instrument_id: InstrumentId) -> Option<OrderBook> {
480 self.order_book(&instrument_id).cloned()
481 }
482
483 #[pyo3(name = "has_order_book")]
484 fn py_has_order_book(&self, instrument_id: InstrumentId) -> bool {
485 self.has_order_book(&instrument_id)
486 }
487
488 #[pyo3(name = "book_update_count")]
489 fn py_book_update_count(&self, instrument_id: InstrumentId) -> usize {
490 self.book_update_count(&instrument_id)
491 }
492
493 #[pyo3(name = "synthetic")]
494 fn py_synthetic(&self, instrument_id: InstrumentId) -> Option<SyntheticInstrument> {
495 self.synthetic(&instrument_id).cloned()
496 }
497
498 #[pyo3(name = "synthetic_ids")]
499 fn py_synthetic_ids(&self) -> Vec<InstrumentId> {
500 self.synthetic_ids().into_iter().copied().collect()
501 }
502}