From 7896810c88f4fe345cde836e1a6d8590c4962e96 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 27 Apr 2024 10:13:18 +1000 Subject: [PATCH] Continue Cache in Rust --- nautilus_core/common/src/cache/mod.rs | 616 ++++++++++++++++++++---- nautilus_core/model/src/orders/base.rs | 84 +++- nautilus_core/model/src/polymorphism.rs | 22 +- 3 files changed, 613 insertions(+), 109 deletions(-) diff --git a/nautilus_core/common/src/cache/mod.rs b/nautilus_core/common/src/cache/mod.rs index 54cd41f8e50b..9d833a0d8d71 100644 --- a/nautilus_core/common/src/cache/mod.rs +++ b/nautilus_core/common/src/cache/mod.rs @@ -31,7 +31,7 @@ use nautilus_model::{ quote::QuoteTick, trade::TradeTick, }, - enums::{OrderSide, PositionSide, PriceType}, + enums::{AggregationSource, OrderSide, PositionSide, PriceType}, identifiers::{ account_id::AccountId, client_id::ClientId, client_order_id::ClientOrderId, component_id::ComponentId, exec_algorithm_id::ExecAlgorithmId, instrument_id::InstrumentId, @@ -42,11 +42,11 @@ use nautilus_model::{ orderbook::book::OrderBook, orders::{base::OrderAny, list::OrderList}, polymorphism::{ - GetClientOrderId, GetExecAlgorithmId, GetExecSpawnId, GetInstrumentId, GetOrderSide, - GetStrategyId, + GetClientOrderId, GetExecAlgorithmId, GetExecSpawnId, GetInstrumentId, GetOrderFilledQty, + GetOrderLeavesQty, GetOrderQuantity, GetOrderSide, GetStrategyId, IsClosed, }, position::Position, - types::{currency::Currency, price::Price}, + types::{currency::Currency, price::Price, quantity::Quantity}, }; use ustr::Ustr; @@ -122,7 +122,7 @@ pub struct CacheIndex { strategy_orders: HashMap>, strategy_positions: HashMap>, exec_algorithm_orders: HashMap>, - exec_spawn_orders: HashMap>, + exec_spawn_orders: HashMap>, orders: HashSet, orders_open: HashSet, orders_closed: HashSet, @@ -185,7 +185,7 @@ pub struct Cache { synthetics: HashMap, accounts: HashMap>, orders: HashMap, - order_lists: HashMap>, + order_lists: HashMap, positions: HashMap, position_snapshots: HashMap>, } @@ -249,7 +249,7 @@ impl Cache { } } - // -- COMMANDS ------------------------------------------------------------ + // -- COMMANDS -------------------------------------------------------------------------------- pub fn cache_general(&mut self) -> anyhow::Result<()> { self.general = match &self.database { @@ -684,13 +684,13 @@ impl Cache { Ok(()) } - // -- IDENTIFIER QUERIES -------------------------------------------------- + // -- IDENTIFIER QUERIES ---------------------------------------------------------------------- fn build_order_query_filter_set( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> Option> { let mut query: Option> = None; @@ -698,7 +698,7 @@ impl Cache { query = Some( self.index .venue_orders - .get(&venue) + .get(venue) .map_or(HashSet::new(), |o| o.iter().copied().collect()), ); }; @@ -707,7 +707,7 @@ impl Cache { let instrument_orders = self .index .instrument_orders - .get(&instrument_id) + .get(instrument_id) .map_or(HashSet::new(), |o| o.iter().copied().collect()); if let Some(existing_query) = &mut query { @@ -724,7 +724,7 @@ impl Cache { let strategy_orders = self .index .strategy_orders - .get(&strategy_id) + .get(strategy_id) .map_or(HashSet::new(), |o| o.iter().copied().collect()); if let Some(existing_query) = &mut query { @@ -742,9 +742,9 @@ impl Cache { fn build_position_query_filter_set( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> Option> { let mut query: Option> = None; @@ -752,7 +752,7 @@ impl Cache { query = Some( self.index .venue_positions - .get(&venue) + .get(venue) .map_or(HashSet::new(), |p| p.iter().copied().collect()), ); }; @@ -761,7 +761,7 @@ impl Cache { let instrument_positions = self .index .instrument_positions - .get(&instrument_id) + .get(instrument_id) .map_or(HashSet::new(), |p| p.iter().copied().collect()); if let Some(existing_query) = query { @@ -780,7 +780,7 @@ impl Cache { let strategy_positions = self .index .strategy_positions - .get(&strategy_id) + .get(strategy_id) .map_or(HashSet::new(), |p| p.iter().copied().collect()); if let Some(existing_query) = query { @@ -800,7 +800,7 @@ impl Cache { fn get_orders_for_ids( &self, - client_order_ids: HashSet, + client_order_ids: &HashSet, side: Option, ) -> Vec<&OrderAny> { let side = side.unwrap_or(OrderSide::NoOrderSide); @@ -809,7 +809,7 @@ impl Cache { for client_order_id in client_order_ids { let order = self .orders - .get(&client_order_id) + .get(client_order_id) .unwrap_or_else(|| panic!("Order {client_order_id} not found")); if side == OrderSide::NoOrderSide || side == order.order_side() { orders.push(order); @@ -821,7 +821,7 @@ impl Cache { fn get_positions_for_ids( &self, - position_ids: HashSet<&PositionId>, + position_ids: &HashSet, side: Option, ) -> Vec<&Position> { let side = side.unwrap_or(PositionSide::NoPositionSide); @@ -843,9 +843,9 @@ impl Cache { #[must_use] pub fn client_order_ids( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> HashSet { let query = self.build_order_query_filter_set(venue, instrument_id, strategy_id); match query { @@ -857,9 +857,9 @@ impl Cache { #[must_use] pub fn client_order_ids_open( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> HashSet { let query = self.build_order_query_filter_set(venue, instrument_id, strategy_id); match query { @@ -876,9 +876,9 @@ impl Cache { #[must_use] pub fn client_order_ids_closed( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> HashSet { let query = self.build_order_query_filter_set(venue, instrument_id, strategy_id); match query { @@ -895,9 +895,9 @@ impl Cache { #[must_use] pub fn client_order_ids_emulated( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> HashSet { let query = self.build_order_query_filter_set(venue, instrument_id, strategy_id); match query { @@ -914,9 +914,9 @@ impl Cache { #[must_use] pub fn client_order_ids_inflight( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> HashSet { let query = self.build_order_query_filter_set(venue, instrument_id, strategy_id); match query { @@ -933,9 +933,9 @@ impl Cache { #[must_use] pub fn position_ids( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> HashSet { let query = self.build_position_query_filter_set(venue, instrument_id, strategy_id); match query { @@ -947,9 +947,9 @@ impl Cache { #[must_use] pub fn position_open_ids( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> HashSet { let query = self.build_position_query_filter_set(venue, instrument_id, strategy_id); match query { @@ -966,9 +966,9 @@ impl Cache { #[must_use] pub fn position_closed_ids( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, ) -> HashSet { let query = self.build_position_query_filter_set(venue, instrument_id, strategy_id); match query { @@ -997,88 +997,88 @@ impl Cache { self.index.exec_algorithms.clone() } - // -- ORDER QUERIES ------------------------------------------------------- + // -- ORDER QUERIES --------------------------------------------------------------------------- #[must_use] - pub fn order(&self, client_order_id: ClientOrderId) -> Option<&OrderAny> { - self.orders.get(&client_order_id) + pub fn order(&self, client_order_id: &ClientOrderId) -> Option<&OrderAny> { + self.orders.get(client_order_id) } #[must_use] - pub fn client_order_id(&self, venue_order_id: VenueOrderId) -> Option<&ClientOrderId> { - self.index.order_ids.get(&venue_order_id) + pub fn client_order_id(&self, venue_order_id: &VenueOrderId) -> Option<&ClientOrderId> { + self.index.order_ids.get(venue_order_id) } #[must_use] - pub fn venue_order_id(&self, client_order_id: ClientOrderId) -> Option { + pub fn venue_order_id(&self, client_order_id: &ClientOrderId) -> Option { self.orders - .get(&client_order_id) + .get(client_order_id) .and_then(nautilus_model::polymorphism::GetVenueOrderId::venue_order_id) } #[must_use] - pub fn client_id(&self, client_order_id: ClientOrderId) -> Option<&ClientId> { - self.index.order_client.get(&client_order_id) + pub fn client_id(&self, client_order_id: &ClientOrderId) -> Option<&ClientId> { + self.index.order_client.get(client_order_id) } #[must_use] pub fn orders( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> Vec<&OrderAny> { let client_order_ids = self.client_order_ids(venue, instrument_id, strategy_id); - self.get_orders_for_ids(client_order_ids, side) + self.get_orders_for_ids(&client_order_ids, side) } #[must_use] pub fn orders_open( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> Vec<&OrderAny> { let client_order_ids = self.client_order_ids_open(venue, instrument_id, strategy_id); - self.get_orders_for_ids(client_order_ids, side) + self.get_orders_for_ids(&client_order_ids, side) } #[must_use] pub fn orders_closed( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> Vec<&OrderAny> { let client_order_ids = self.client_order_ids_closed(venue, instrument_id, strategy_id); - self.get_orders_for_ids(client_order_ids, side) + self.get_orders_for_ids(&client_order_ids, side) } #[must_use] pub fn orders_emulated( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> Vec<&OrderAny> { let client_order_ids = self.client_order_ids_emulated(venue, instrument_id, strategy_id); - self.get_orders_for_ids(client_order_ids, side) + self.get_orders_for_ids(&client_order_ids, side) } #[must_use] pub fn orders_inflight( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> Vec<&OrderAny> { let client_order_ids = self.client_order_ids_inflight(venue, instrument_id, strategy_id); - self.get_orders_for_ids(client_order_ids, side) + self.get_orders_for_ids(&client_order_ids, side) } #[must_use] @@ -1086,48 +1086,48 @@ impl Cache { let client_order_ids = self.index.position_orders.get(&position_id); match client_order_ids { Some(client_order_ids) => { - self.get_orders_for_ids(client_order_ids.iter().copied().collect(), None) + self.get_orders_for_ids(&client_order_ids.iter().cloned().collect(), None) } None => Vec::new(), } } #[must_use] - pub fn order_exists(&self, client_order_id: ClientOrderId) -> bool { - self.index.orders.contains(&client_order_id) + pub fn order_exists(&self, client_order_id: &ClientOrderId) -> bool { + self.index.orders.contains(client_order_id) } #[must_use] - pub fn is_order_open(&self, client_order_id: ClientOrderId) -> bool { - self.index.orders_open.contains(&client_order_id) + pub fn is_order_open(&self, client_order_id: &ClientOrderId) -> bool { + self.index.orders_open.contains(client_order_id) } #[must_use] - pub fn is_order_closed(&self, client_order_id: ClientOrderId) -> bool { - self.index.orders_closed.contains(&client_order_id) + pub fn is_order_closed(&self, client_order_id: &ClientOrderId) -> bool { + self.index.orders_closed.contains(client_order_id) } #[must_use] - pub fn is_order_emulated(&self, client_order_id: ClientOrderId) -> bool { - self.index.orders_emulated.contains(&client_order_id) + pub fn is_order_emulated(&self, client_order_id: &ClientOrderId) -> bool { + self.index.orders_emulated.contains(client_order_id) } #[must_use] - pub fn is_order_inflight(&self, client_order_id: ClientOrderId) -> bool { - self.index.orders_inflight.contains(&client_order_id) + pub fn is_order_inflight(&self, client_order_id: &ClientOrderId) -> bool { + self.index.orders_inflight.contains(client_order_id) } #[must_use] - pub fn is_order_pending_cancel_local(&self, client_order_id: ClientOrderId) -> bool { - self.index.orders_pending_cancel.contains(&client_order_id) + pub fn is_order_pending_cancel_local(&self, client_order_id: &ClientOrderId) -> bool { + self.index.orders_pending_cancel.contains(client_order_id) } #[must_use] pub fn orders_open_count( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> usize { self.orders_open(venue, instrument_id, strategy_id, side) @@ -1137,9 +1137,9 @@ impl Cache { #[must_use] pub fn orders_closed_count( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> usize { self.orders_closed(venue, instrument_id, strategy_id, side) @@ -1149,9 +1149,9 @@ impl Cache { #[must_use] pub fn orders_emulated_count( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> usize { self.orders_emulated(venue, instrument_id, strategy_id, side) @@ -1161,9 +1161,9 @@ impl Cache { #[must_use] pub fn orders_inflight_count( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> usize { self.orders_inflight(venue, instrument_id, strategy_id, side) @@ -1173,15 +1173,275 @@ impl Cache { #[must_use] pub fn orders_total_count( &self, - venue: Option, - instrument_id: Option, - strategy_id: Option, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, side: Option, ) -> usize { self.orders(venue, instrument_id, strategy_id, side).len() } - // -- GENERAL ------------------------------------------------------------- + #[must_use] + pub fn order_list(&self, order_list_id: &OrderListId) -> Option<&OrderList> { + self.order_lists.get(order_list_id) + } + + #[must_use] + pub fn order_lists( + &self, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, + ) -> Vec<&OrderList> { + let mut order_lists = self.order_lists.values().collect::>(); + + if let Some(venue) = venue { + order_lists.retain(|ol| ol.instrument_id.venue == *venue); + } + + if let Some(instrument_id) = instrument_id { + order_lists.retain(|ol| &ol.instrument_id == instrument_id); + } + + if let Some(strategy_id) = strategy_id { + order_lists.retain(|ol| &ol.strategy_id == strategy_id); + } + + order_lists + } + + #[must_use] + pub fn order_list_exists(&self, order_list_id: &OrderListId) -> bool { + self.order_lists.contains_key(order_list_id) + } + + // -- EXEC ALGORITHM QUERIES ------------------------------------------------------------------ + + #[must_use] + pub fn orders_for_exec_algorithm( + &self, + exec_algorithm_id: &ExecAlgorithmId, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, + side: Option, + ) -> Vec<&OrderAny> { + let query = self.build_order_query_filter_set(venue, instrument_id, strategy_id); + let exec_algorithm_order_ids = self.index.exec_algorithm_orders.get(exec_algorithm_id); + + if let Some(query) = query { + if let Some(exec_algorithm_order_ids) = exec_algorithm_order_ids { + let exec_algorithm_order_ids = exec_algorithm_order_ids.intersection(&query); + } + } + + if let Some(exec_algorithm_order_ids) = exec_algorithm_order_ids { + self.get_orders_for_ids(exec_algorithm_order_ids, side) + } else { + Vec::new() + } + } + + #[must_use] + pub fn orders_for_exec_spawn(&self, exec_spawn_id: &ClientOrderId) -> Vec<&OrderAny> { + self.get_orders_for_ids( + self.index + .exec_spawn_orders + .get(exec_spawn_id) + .unwrap_or(&HashSet::new()), + None, + ) + } + + #[must_use] + pub fn exec_spawn_total_quantity( + &self, + exec_spawn_id: &ClientOrderId, + active_only: bool, + ) -> Option { + let exec_spawn_orders = self.orders_for_exec_spawn(exec_spawn_id); + + let mut total_quantity: Option = None; + + for spawn_order in exec_spawn_orders { + if !active_only || !spawn_order.is_closed() { + if let Some(mut total_quantity) = total_quantity { + total_quantity += spawn_order.quantity() + } + } else { + total_quantity = Some(spawn_order.quantity()) + } + } + + total_quantity + } + + #[must_use] + pub fn exec_spawn_total_filled_qty( + &self, + exec_spawn_id: &ClientOrderId, + active_only: bool, + ) -> Option { + let exec_spawn_orders = self.orders_for_exec_spawn(exec_spawn_id); + + let mut total_quantity: Option = None; + + for spawn_order in exec_spawn_orders { + if !active_only || !spawn_order.is_closed() { + if let Some(mut total_quantity) = total_quantity { + total_quantity += spawn_order.filled_qty() + } + } else { + total_quantity = Some(spawn_order.filled_qty()) + } + } + + total_quantity + } + + #[must_use] + pub fn exec_spawn_total_leaves_qty( + &self, + exec_spawn_id: &ClientOrderId, + active_only: bool, + ) -> Option { + let exec_spawn_orders = self.orders_for_exec_spawn(exec_spawn_id); + + let mut total_quantity: Option = None; + + for spawn_order in exec_spawn_orders { + if !active_only || !spawn_order.is_closed() { + if let Some(mut total_quantity) = total_quantity { + total_quantity += spawn_order.leaves_qty() + } + } else { + total_quantity = Some(spawn_order.leaves_qty()) + } + } + + total_quantity + } + + // -- POSITION QUERIES ------------------------------------------------------------------------ + + #[must_use] + pub fn position(&self, position_id: &PositionId) -> Option<&Position> { + self.positions.get(position_id) + } + + #[must_use] + pub fn position_for_order(&self, client_order_id: &ClientOrderId) -> Option<&Position> { + self.index + .order_position + .get(client_order_id) + .and_then(|position_id| self.positions.get(position_id)) + } + + #[must_use] + pub fn position_id(&self, client_order_id: &ClientOrderId) -> Option<&PositionId> { + self.index.order_position.get(client_order_id) + } + + #[must_use] + pub fn positions( + &self, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, + side: Option, + ) -> Vec<&Position> { + let position_ids = self.position_ids(venue, instrument_id, strategy_id); + self.get_positions_for_ids(&position_ids, side) + } + + #[must_use] + pub fn positions_open( + &self, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, + side: Option, + ) -> Vec<&Position> { + let position_ids = self.position_open_ids(venue, instrument_id, strategy_id); + self.get_positions_for_ids(&position_ids, side) + } + + #[must_use] + pub fn positions_closed( + &self, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, + side: Option, + ) -> Vec<&Position> { + let position_ids = self.position_closed_ids(venue, instrument_id, strategy_id); + self.get_positions_for_ids(&position_ids, side) + } + + #[must_use] + pub fn position_exists(&self, position_id: &PositionId) -> bool { + self.index.positions.contains(position_id) + } + + #[must_use] + pub fn is_position_open(&self, position_id: &PositionId) -> bool { + self.index.positions_open.contains(position_id) + } + + #[must_use] + pub fn is_position_closed(&self, position_id: &PositionId) -> bool { + self.index.positions_closed.contains(position_id) + } + + #[must_use] + pub fn positions_open_count( + &self, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, + side: Option, + ) -> u64 { + self.positions_open(venue, instrument_id, strategy_id, side) + .len() as u64 + } + + #[must_use] + pub fn positions_closed_count( + &self, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, + side: Option, + ) -> u64 { + self.positions_closed(venue, instrument_id, strategy_id, side) + .len() as u64 + } + + #[must_use] + pub fn positions_total_count( + &self, + venue: Option<&Venue>, + instrument_id: Option<&InstrumentId>, + strategy_id: Option<&StrategyId>, + side: Option, + ) -> u64 { + self.positions(venue, instrument_id, strategy_id, side) + .len() as u64 + } + + // -- STRATEGY QUERIES ------------------------------------------------------------------------ + + #[must_use] + pub fn strategy_id_for_order(&self, client_order_id: &ClientOrderId) -> Option<&StrategyId> { + self.index.order_strategy.get(client_order_id) + } + + #[must_use] + pub fn strategy_id_for_position(&self, position_id: &PositionId) -> Option<&StrategyId> { + self.index.position_strategy.get(position_id) + } + + // -- GENERAL --------------------------------------------------------------------------------- pub fn get(&self, key: &str) -> anyhow::Result> { check_valid_string(key, stringify!(key))?; @@ -1189,7 +1449,7 @@ impl Cache { Ok(self.general.get(key).map(std::vec::Vec::as_slice)) } - // -- DATA QUERIES -------------------------------------------------------- + // -- DATA QUERIES ---------------------------------------------------------------------------- #[must_use] pub fn price(&self, instrument_id: &InstrumentId, price_type: PriceType) -> Option { @@ -1262,6 +1522,150 @@ impl Cache { pub fn bar(&self, bar_type: &BarType) -> Option<&Bar> { self.bars.get(bar_type).and_then(|bars| bars.front()) } + + #[must_use] + pub fn book_update_count(&self, instrument_id: &InstrumentId) -> u64 { + self.books + .get(instrument_id) + .map(|book| book.count) + .unwrap_or(0) + } + + #[must_use] + pub fn quote_tick_count(&self, instrument_id: &InstrumentId) -> u64 { + self.quotes + .get(instrument_id) + .map(|quotes| quotes.len()) + .unwrap_or(0) as u64 + } + + #[must_use] + pub fn trade_tick_count(&self, instrument_id: &InstrumentId) -> u64 { + self.trades + .get(instrument_id) + .map(|trades| trades.len()) + .unwrap_or(0) as u64 + } + + #[must_use] + pub fn bar_count(&self, bar_type: &BarType) -> u64 { + self.bars.get(bar_type).map(|bars| bars.len()).unwrap_or(0) as u64 + } + + #[must_use] + pub fn has_order_book(&self, instrument_id: &InstrumentId) -> bool { + self.books.contains_key(instrument_id) + } + + #[must_use] + pub fn has_quote_ticks(&self, instrument_id: &InstrumentId) -> bool { + self.quote_tick_count(instrument_id) > 0 + } + + #[must_use] + pub fn has_trade_ticks(&self, instrument_id: &InstrumentId) -> bool { + self.trade_tick_count(instrument_id) > 0 + } + + #[must_use] + pub fn has_bars(&self, bar_type: &BarType) -> bool { + self.bar_count(bar_type) > 0 + } + + // -- INSTRUMENT QUERIES ---------------------------------------------------------------------- + + #[must_use] + pub fn instrument(&self, instrument_id: &InstrumentId) -> Option<&InstrumentAny> { + self.instruments.get(instrument_id) + } + + #[must_use] + pub fn instrument_ids(&self, venue: &Venue) -> Vec<&InstrumentId> { + self.instruments + .keys() + .filter(|i| &i.venue == venue) + .collect() + } + + #[must_use] + pub fn instruments(&self, venue: &Venue) -> Vec<&InstrumentAny> { + self.instruments + .values() + .filter(|i| &i.id().venue == venue) + .collect() + } + + #[must_use] + pub fn bar_types( + &self, + instrument_id: Option<&InstrumentId>, + price_type: Option<&PriceType>, + aggregation_source: AggregationSource, + ) -> Vec<&BarType> { + let mut bar_types = self + .bars + .keys() + .filter(|bar_type| bar_type.aggregation_source == aggregation_source) + .collect::>(); + + if let Some(instrument_id) = instrument_id { + bar_types.retain(|bar_type| &bar_type.instrument_id == instrument_id); + } + + if let Some(price_type) = price_type { + bar_types.retain(|bar_type| &bar_type.spec.price_type == price_type); + } + + bar_types + } + + // -- SYNTHETIC QUERIES ----------------------------------------------------------------------- + + #[must_use] + pub fn synthetic(&self, instrument_id: &InstrumentId) -> Option<&SyntheticInstrument> { + self.synthetics.get(instrument_id) + } + + #[must_use] + pub fn synthetic_ids(&self) -> Vec<&InstrumentId> { + self.synthetics.keys().collect() + } + + #[must_use] + pub fn synthetics(&self) -> Vec<&SyntheticInstrument> { + self.synthetics.values().collect() + } + + // -- ACCOUNT QUERIES ----------------------------------------------------------------------- + + #[must_use] + pub fn account(&self, account_id: &AccountId) -> Option<&dyn Account> { + self.accounts + .get(account_id) + .map(|box_account| box_account.as_ref()) + } + + #[must_use] + pub fn account_for_venue(&self, venue: &Venue) -> Option<&dyn Account> { + self.index + .venue_account + .get(venue) + .and_then(|account_id| self.accounts.get(account_id)) + .map(|box_account| box_account.as_ref()) + } + + #[must_use] + pub fn account_id(&self, venue: &Venue) -> Option<&AccountId> { + self.index.venue_account.get(venue) + } + + #[must_use] + pub fn accounts(&self, account_id: &AccountId) -> Vec<&dyn Account> { + self.accounts + .values() + .map(|box_account| box_account.as_ref()) + .collect() + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/nautilus_core/model/src/orders/base.rs b/nautilus_core/model/src/orders/base.rs index 846aa2c462c9..a6097e0620a0 100644 --- a/nautilus_core/model/src/orders/base.rs +++ b/nautilus_core/model/src/orders/base.rs @@ -47,8 +47,8 @@ use crate::{ }, polymorphism::{ GetClientOrderId, GetEmulationTrigger, GetExecAlgorithmId, GetExecSpawnId, GetInstrumentId, - GetLimitPrice, GetOrderSide, GetOrderSideSpecified, GetStopPrice, GetStrategyId, - GetVenueOrderId, + GetLimitPrice, GetOrderFilledQty, GetOrderLeavesQty, GetOrderQuantity, GetOrderSide, + GetOrderSideSpecified, GetStopPrice, GetStrategyId, GetVenueOrderId, IsClosed, IsOpen, }, types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; @@ -261,6 +261,54 @@ impl GetOrderSide for OrderAny { } } +impl GetOrderQuantity for OrderAny { + fn quantity(&self) -> Quantity { + match self { + Self::Limit(order) => order.quantity, + Self::LimitIfTouched(order) => order.quantity, + Self::Market(order) => order.quantity, + Self::MarketIfTouched(order) => order.quantity, + Self::MarketToLimit(order) => order.quantity, + Self::StopLimit(order) => order.quantity, + Self::StopMarket(order) => order.quantity, + Self::TrailingStopLimit(order) => order.quantity, + Self::TrailingStopMarket(order) => order.quantity, + } + } +} + +impl GetOrderFilledQty for OrderAny { + fn filled_qty(&self) -> Quantity { + match self { + Self::Limit(order) => order.filled_qty(), + Self::LimitIfTouched(order) => order.filled_qty(), + Self::Market(order) => order.filled_qty(), + Self::MarketIfTouched(order) => order.filled_qty(), + Self::MarketToLimit(order) => order.filled_qty(), + Self::StopLimit(order) => order.filled_qty(), + Self::StopMarket(order) => order.filled_qty(), + Self::TrailingStopLimit(order) => order.filled_qty(), + Self::TrailingStopMarket(order) => order.filled_qty(), + } + } +} + +impl GetOrderLeavesQty for OrderAny { + fn leaves_qty(&self) -> Quantity { + match self { + Self::Limit(order) => order.leaves_qty(), + Self::LimitIfTouched(order) => order.leaves_qty(), + Self::Market(order) => order.leaves_qty(), + Self::MarketIfTouched(order) => order.leaves_qty(), + Self::MarketToLimit(order) => order.leaves_qty(), + Self::StopLimit(order) => order.leaves_qty(), + Self::StopMarket(order) => order.leaves_qty(), + Self::TrailingStopLimit(order) => order.leaves_qty(), + Self::TrailingStopMarket(order) => order.leaves_qty(), + } + } +} + impl GetOrderSideSpecified for OrderAny { fn order_side_specified(&self) -> OrderSideSpecified { match self { @@ -293,6 +341,38 @@ impl GetEmulationTrigger for OrderAny { } } +impl IsOpen for OrderAny { + fn is_open(&self) -> bool { + match self { + Self::Limit(order) => order.is_open(), + Self::LimitIfTouched(order) => order.is_open(), + Self::Market(order) => order.is_open(), + Self::MarketIfTouched(order) => order.is_open(), + Self::MarketToLimit(order) => order.is_open(), + Self::StopLimit(order) => order.is_open(), + Self::StopMarket(order) => order.is_open(), + Self::TrailingStopLimit(order) => order.is_open(), + Self::TrailingStopMarket(order) => order.is_open(), + } + } +} + +impl IsClosed for OrderAny { + fn is_closed(&self) -> bool { + match self { + Self::Limit(order) => order.is_closed(), + Self::LimitIfTouched(order) => order.is_closed(), + Self::Market(order) => order.is_closed(), + Self::MarketIfTouched(order) => order.is_closed(), + Self::MarketToLimit(order) => order.is_closed(), + Self::StopLimit(order) => order.is_closed(), + Self::StopMarket(order) => order.is_closed(), + Self::TrailingStopLimit(order) => order.is_closed(), + Self::TrailingStopMarket(order) => order.is_closed(), + } + } +} + #[derive(Clone, Debug)] pub enum PassiveOrderAny { Limit(LimitOrderAny), diff --git a/nautilus_core/model/src/polymorphism.rs b/nautilus_core/model/src/polymorphism.rs index a1e703c69f64..77e340acb4db 100644 --- a/nautilus_core/model/src/polymorphism.rs +++ b/nautilus_core/model/src/polymorphism.rs @@ -23,7 +23,7 @@ use crate::{ client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, instrument_id::InstrumentId, strategy_id::StrategyId, venue_order_id::VenueOrderId, }, - types::price::Price, + types::{price::Price, quantity::Quantity}, }; pub trait GetTsInit { @@ -58,6 +58,18 @@ pub trait GetOrderSide { fn order_side(&self) -> OrderSide; } +pub trait GetOrderQuantity { + fn quantity(&self) -> Quantity; +} + +pub trait GetOrderFilledQty { + fn filled_qty(&self) -> Quantity; +} + +pub trait GetOrderLeavesQty { + fn leaves_qty(&self) -> Quantity; +} + pub trait GetOrderSideSpecified { fn order_side_specified(&self) -> OrderSideSpecified; } @@ -73,3 +85,11 @@ pub trait GetLimitPrice { pub trait GetStopPrice { fn stop_px(&self) -> Price; } + +pub trait IsOpen { + fn is_open(&self) -> bool; +} + +pub trait IsClosed { + fn is_closed(&self) -> bool; +}