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