nautilus_execution/matching_engine/
ids_generator.rs1use std::{cell::RefCell, rc::Rc};
17
18use nautilus_common::cache::Cache;
19use nautilus_model::{
20 enums::OmsType,
21 identifiers::{PositionId, TradeId, Venue, VenueOrderId},
22 orders::OrderAny,
23};
24use uuid::Uuid;
25
26pub struct IdsGenerator {
27 venue: Venue,
28 raw_id: u32,
29 oms_type: OmsType,
30 use_random_ids: bool,
31 use_position_ids: bool,
32 cache: Rc<RefCell<Cache>>,
33 position_count: usize,
34 order_count: usize,
35 execution_count: usize,
36}
37
38impl IdsGenerator {
39 pub const fn new(
40 venue: Venue,
41 oms_type: OmsType,
42 raw_id: u32,
43 use_random_ids: bool,
44 use_position_ids: bool,
45 cache: Rc<RefCell<Cache>>,
46 ) -> Self {
47 Self {
48 venue,
49 raw_id,
50 oms_type,
51 cache,
52 use_random_ids,
53 use_position_ids,
54 position_count: 0,
55 order_count: 0,
56 execution_count: 0,
57 }
58 }
59
60 pub const fn reset(&mut self) {
61 self.position_count = 0;
62 self.order_count = 0;
63 self.execution_count = 0;
64 }
65
66 pub fn get_venue_order_id(&mut self, order: &OrderAny) -> anyhow::Result<VenueOrderId> {
67 if let Some(venue_order_id) = order.venue_order_id() {
69 return Ok(venue_order_id);
70 }
71
72 if let Some(venue_order_id) = self.cache.borrow().venue_order_id(&order.client_order_id()) {
74 return Ok(venue_order_id.to_owned());
75 }
76
77 let venue_order_id = self.generate_venue_order_id();
78 self.cache.borrow_mut().add_venue_order_id(
79 &order.client_order_id(),
80 &venue_order_id,
81 false,
82 )?;
83 Ok(venue_order_id)
84 }
85
86 pub fn get_position_id(
87 &mut self,
88 order: &OrderAny,
89 generate: Option<bool>,
90 ) -> Option<PositionId> {
91 let generate = generate.unwrap_or(true);
92 if self.oms_type == OmsType::Hedging {
93 {
94 let cache = self.cache.as_ref().borrow();
95 let position_id_result = cache.position_id(&order.client_order_id());
96 if let Some(position_id) = position_id_result {
97 return Some(position_id.to_owned());
98 }
99 }
100 if generate {
101 self.generate_venue_position_id()
102 } else {
103 panic!(
104 "Position id should be generated. Hedging Oms type order matching engine doesnt exists in cache."
105 )
106 }
107 } else {
108 let cache = self.cache.as_ref().borrow();
110 let positions_open =
111 cache.positions_open(None, Some(&order.instrument_id()), None, None);
112 if positions_open.is_empty() {
113 None
114 } else {
115 Some(positions_open[0].id)
116 }
117 }
118 }
119
120 pub fn generate_trade_id(&mut self) -> TradeId {
121 self.execution_count += 1;
122 let trade_id = if self.use_random_ids {
123 Uuid::new_v4().to_string()
124 } else {
125 format!("{}-{}-{}", self.venue, self.raw_id, self.execution_count)
126 };
127 TradeId::from(trade_id.as_str())
128 }
129
130 pub fn generate_venue_position_id(&mut self) -> Option<PositionId> {
131 if !self.use_position_ids {
132 return None;
133 }
134
135 self.position_count += 1;
136 if self.use_random_ids {
137 Some(PositionId::new(Uuid::new_v4().to_string()))
138 } else {
139 Some(PositionId::new(
140 format!("{}-{}-{}", self.venue, self.raw_id, self.position_count).as_str(),
141 ))
142 }
143 }
144
145 pub fn generate_venue_order_id(&mut self) -> VenueOrderId {
146 self.order_count += 1;
147 if self.use_random_ids {
148 VenueOrderId::new(Uuid::new_v4().to_string())
149 } else {
150 VenueOrderId::new(
151 format!("{}-{}-{}", self.venue, self.raw_id, self.order_count).as_str(),
152 )
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use std::{cell::RefCell, rc::Rc};
160
161 use nautilus_common::cache::Cache;
162 use nautilus_model::{
163 enums::OmsType,
164 events::OrderFilled,
165 identifiers::{PositionId, Venue, VenueOrderId},
166 instruments::InstrumentAny,
167 orders::OrderAny,
168 position::Position,
169 };
170 use rstest::rstest;
171
172 use crate::matching_engine::{
173 ids_generator::IdsGenerator,
174 tests::{instrument_eth_usdt, market_order_buy, market_order_fill, market_order_sell},
175 };
176
177 fn get_ids_generator(
178 cache: Rc<RefCell<Cache>>,
179 use_position_ids: bool,
180 oms_type: OmsType,
181 ) -> IdsGenerator {
182 IdsGenerator::new(
183 Venue::from("BINANCE"),
184 oms_type,
185 1,
186 false,
187 use_position_ids,
188 cache,
189 )
190 }
191
192 #[rstest]
193 fn test_get_position_id_hedging_with_existing_position(
194 instrument_eth_usdt: InstrumentAny,
195 market_order_buy: OrderAny,
196 market_order_fill: OrderFilled,
197 ) {
198 let cache = Rc::new(RefCell::new(Cache::default()));
199 let mut ids_generator = get_ids_generator(cache.clone(), false, OmsType::Hedging);
200
201 let position = Position::new(&instrument_eth_usdt, market_order_fill);
202
203 cache
205 .borrow_mut()
206 .add_position(position.clone(), OmsType::Hedging)
207 .unwrap();
208
209 let position_id = ids_generator.get_position_id(&market_order_buy, None);
210 assert_eq!(position_id, Some(position.id));
211 }
212
213 #[rstest]
214 fn test_get_position_id_hedging_with_generated_position(market_order_buy: OrderAny) {
215 let cache = Rc::new(RefCell::new(Cache::default()));
216 let mut ids_generator = get_ids_generator(cache, true, OmsType::Hedging);
217
218 let position_id = ids_generator.get_position_id(&market_order_buy, None);
219 assert_eq!(position_id, Some(PositionId::new("BINANCE-1-1")));
220 }
221
222 #[rstest]
223 fn test_get_position_id_netting(
224 instrument_eth_usdt: InstrumentAny,
225 market_order_buy: OrderAny,
226 market_order_fill: OrderFilled,
227 ) {
228 let cache = Rc::new(RefCell::new(Cache::default()));
229 let mut ids_generator = get_ids_generator(cache.clone(), false, OmsType::Netting);
230
231 let position_id = ids_generator.get_position_id(&market_order_buy, None);
233 assert_eq!(position_id, None);
234
235 let position = Position::new(&instrument_eth_usdt, market_order_fill);
237 cache
238 .as_ref()
239 .borrow_mut()
240 .add_position(position.clone(), OmsType::Netting)
241 .unwrap();
242
243 let position_id = ids_generator.get_position_id(&market_order_buy, None);
245 assert_eq!(position_id, Some(position.id));
246 }
247
248 #[rstest]
249 fn test_generate_venue_position_id() {
250 let cache = Rc::new(RefCell::new(Cache::default()));
251 let mut ids_generator_with_position_ids =
252 get_ids_generator(cache.clone(), true, OmsType::Netting);
253 let mut ids_generator_no_position_ids = get_ids_generator(cache, false, OmsType::Netting);
254
255 assert_eq!(
256 ids_generator_no_position_ids.generate_venue_position_id(),
257 None
258 );
259
260 let position_id_1 = ids_generator_with_position_ids.generate_venue_position_id();
261 let position_id_2 = ids_generator_with_position_ids.generate_venue_position_id();
262 assert_eq!(position_id_1, Some(PositionId::new("BINANCE-1-1")));
263 assert_eq!(position_id_2, Some(PositionId::new("BINANCE-1-2")));
264 }
265
266 #[rstest]
267 fn get_venue_position_id(market_order_buy: OrderAny, market_order_sell: OrderAny) {
268 let cache = Rc::new(RefCell::new(Cache::default()));
269 let mut ids_generator = get_ids_generator(cache, true, OmsType::Netting);
270
271 let venue_order_id1 = ids_generator.get_venue_order_id(&market_order_buy).unwrap();
272 let venue_order_id2 = ids_generator
273 .get_venue_order_id(&market_order_sell)
274 .unwrap();
275 assert_eq!(venue_order_id1, VenueOrderId::from("BINANCE-1-1"));
276 assert_eq!(venue_order_id2, VenueOrderId::from("BINANCE-1-2"));
277
278 let venue_order_id3 = ids_generator.get_venue_order_id(&market_order_buy).unwrap();
280 assert_eq!(venue_order_id3, VenueOrderId::from("BINANCE-1-1"));
281 }
282}