pub mod book;
pub mod level;
pub mod own;
#[cfg(test)]
mod tests {
use std::sync::Once;
use pyo3::{exceptions::PyValueError, prelude::*, types::PyDict};
use rstest::rstest;
use rust_decimal::Decimal;
use crate::{
enums::BookType,
identifiers::InstrumentId,
orderbook::{OrderBook, own::OwnOrderBook},
};
fn ensure_python_initialized() {
static INIT: Once = Once::new();
INIT.call_once(Python::initialize);
}
#[rstest]
#[case::own_bids_to_dict("bids_to_dict", false, false)]
#[case::own_asks_to_dict("asks_to_dict", false, false)]
#[case::own_bid_quantity("bid_quantity", false, false)]
#[case::own_ask_quantity("ask_quantity", false, false)]
#[case::book_bids_filtered_to_dict("bids_filtered_to_dict", true, false)]
#[case::book_asks_filtered_to_dict("asks_filtered_to_dict", true, false)]
#[case::book_group_bids_filtered("group_bids_filtered", true, true)]
#[case::book_group_asks_filtered("group_asks_filtered", true, true)]
#[case::book_filtered_view("filtered_view", true, false)]
fn test_python_acceptance_filter_requires_ts_now(
#[case] method_name: &str,
#[case] filtered_book_method: bool,
#[case] requires_group_size: bool,
) {
ensure_python_initialized();
Python::attach(|py| {
let instrument_id = InstrumentId::from("AAPL.XNAS");
let own_book = Py::new(py, OwnOrderBook::new(instrument_id)).unwrap();
let kwargs = PyDict::new(py);
kwargs.set_item("accepted_buffer_ns", 1).unwrap();
let py_err = if filtered_book_method {
kwargs.set_item("own_book", own_book.bind(py)).unwrap();
let book = Py::new(py, OrderBook::new(instrument_id, BookType::L2_MBP)).unwrap();
let result = if requires_group_size {
book.bind(py)
.call_method(method_name, (Decimal::ONE,), Some(&kwargs))
} else {
book.bind(py).call_method(method_name, (), Some(&kwargs))
};
result.unwrap_err()
} else {
own_book
.bind(py)
.call_method(method_name, (), Some(&kwargs))
.unwrap_err()
};
assert!(
py_err.is_instance_of::<PyValueError>(py),
"expected PyValueError, received {}",
py_err.get_type(py).name().unwrap().to_str().unwrap()
);
assert_eq!(
py_err.value(py).to_string(),
"ts_now must be provided when accepted_buffer_ns > 0"
);
});
}
#[rstest]
#[case::bids_filtered_to_dict("bids_filtered_to_dict", false)]
#[case::asks_filtered_to_dict("asks_filtered_to_dict", false)]
#[case::group_bids_filtered("group_bids_filtered", true)]
#[case::group_asks_filtered("group_asks_filtered", true)]
#[case::filtered_view("filtered_view", false)]
fn test_python_filtered_book_requires_ts_now_without_own_book(
#[case] method_name: &str,
#[case] requires_group_size: bool,
) {
ensure_python_initialized();
Python::attach(|py| {
let instrument_id = InstrumentId::from("AAPL.XNAS");
let book = Py::new(py, OrderBook::new(instrument_id, BookType::L2_MBP)).unwrap();
let kwargs = PyDict::new(py);
kwargs.set_item("accepted_buffer_ns", 1).unwrap();
let result = if requires_group_size {
book.bind(py)
.call_method(method_name, (Decimal::ONE,), Some(&kwargs))
} else {
book.bind(py).call_method(method_name, (), Some(&kwargs))
};
let py_err = result.unwrap_err();
assert!(
py_err.is_instance_of::<PyValueError>(py),
"expected PyValueError, received {}",
py_err.get_type(py).name().unwrap().to_str().unwrap()
);
assert_eq!(
py_err.value(py).to_string(),
"ts_now must be provided when accepted_buffer_ns > 0"
);
});
}
}