nautilus_model/python/orderbook/
mod.rs1pub mod book;
19pub mod level;
20pub mod own;
21
22#[cfg(test)]
23mod tests {
24 use std::sync::Once;
25
26 use pyo3::{exceptions::PyValueError, prelude::*, types::PyDict};
27 use rstest::rstest;
28 use rust_decimal::Decimal;
29
30 use crate::{
31 enums::BookType,
32 identifiers::InstrumentId,
33 orderbook::{OrderBook, own::OwnOrderBook},
34 };
35
36 fn ensure_python_initialized() {
37 static INIT: Once = Once::new();
38 INIT.call_once(Python::initialize);
39 }
40
41 #[rstest]
42 #[case::own_bids_to_dict("bids_to_dict", false, false)]
43 #[case::own_asks_to_dict("asks_to_dict", false, false)]
44 #[case::own_bid_quantity("bid_quantity", false, false)]
45 #[case::own_ask_quantity("ask_quantity", false, false)]
46 #[case::book_bids_filtered_to_dict("bids_filtered_to_dict", true, false)]
47 #[case::book_asks_filtered_to_dict("asks_filtered_to_dict", true, false)]
48 #[case::book_group_bids_filtered("group_bids_filtered", true, true)]
49 #[case::book_group_asks_filtered("group_asks_filtered", true, true)]
50 #[case::book_filtered_view("filtered_view", true, false)]
51 fn test_python_acceptance_filter_requires_ts_now(
52 #[case] method_name: &str,
53 #[case] filtered_book_method: bool,
54 #[case] requires_group_size: bool,
55 ) {
56 ensure_python_initialized();
57
58 Python::attach(|py| {
59 let instrument_id = InstrumentId::from("AAPL.XNAS");
60 let own_book = Py::new(py, OwnOrderBook::new(instrument_id)).unwrap();
61 let kwargs = PyDict::new(py);
62 kwargs.set_item("accepted_buffer_ns", 1).unwrap();
63
64 let py_err = if filtered_book_method {
65 kwargs.set_item("own_book", own_book.bind(py)).unwrap();
66 let book = Py::new(py, OrderBook::new(instrument_id, BookType::L2_MBP)).unwrap();
67 let result = if requires_group_size {
68 book.bind(py)
69 .call_method(method_name, (Decimal::ONE,), Some(&kwargs))
70 } else {
71 book.bind(py).call_method(method_name, (), Some(&kwargs))
72 };
73 result.unwrap_err()
74 } else {
75 own_book
76 .bind(py)
77 .call_method(method_name, (), Some(&kwargs))
78 .unwrap_err()
79 };
80
81 assert!(
82 py_err.is_instance_of::<PyValueError>(py),
83 "expected PyValueError, received {}",
84 py_err.get_type(py).name().unwrap().to_str().unwrap()
85 );
86 assert_eq!(
87 py_err.value(py).to_string(),
88 "ts_now must be provided when accepted_buffer_ns > 0"
89 );
90 });
91 }
92
93 #[rstest]
101 #[case::bids_filtered_to_dict("bids_filtered_to_dict", false)]
102 #[case::asks_filtered_to_dict("asks_filtered_to_dict", false)]
103 #[case::group_bids_filtered("group_bids_filtered", true)]
104 #[case::group_asks_filtered("group_asks_filtered", true)]
105 #[case::filtered_view("filtered_view", false)]
106 fn test_python_filtered_book_requires_ts_now_without_own_book(
107 #[case] method_name: &str,
108 #[case] requires_group_size: bool,
109 ) {
110 ensure_python_initialized();
111
112 Python::attach(|py| {
113 let instrument_id = InstrumentId::from("AAPL.XNAS");
114 let book = Py::new(py, OrderBook::new(instrument_id, BookType::L2_MBP)).unwrap();
115 let kwargs = PyDict::new(py);
116 kwargs.set_item("accepted_buffer_ns", 1).unwrap();
117
118 let result = if requires_group_size {
119 book.bind(py)
120 .call_method(method_name, (Decimal::ONE,), Some(&kwargs))
121 } else {
122 book.bind(py).call_method(method_name, (), Some(&kwargs))
123 };
124
125 let py_err = result.unwrap_err();
126
127 assert!(
128 py_err.is_instance_of::<PyValueError>(py),
129 "expected PyValueError, received {}",
130 py_err.get_type(py).name().unwrap().to_str().unwrap()
131 );
132 assert_eq!(
133 py_err.value(py).to_string(),
134 "ts_now must be provided when accepted_buffer_ns > 0"
135 );
136 });
137 }
138}