nautilus_backtest/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!("Position id should be generated. Hedging Oms type order matching engine doesnt exists in cache.")
104 }
105 } else {
106 let cache = self.cache.as_ref().borrow();
108 let positions_open =
109 cache.positions_open(None, Some(&order.instrument_id()), None, None);
110 if positions_open.is_empty() {
111 None
112 } else {
113 Some(positions_open[0].id)
114 }
115 }
116 }
117
118 pub fn generate_trade_id(&mut self) -> TradeId {
119 self.execution_count += 1;
120 let trade_id = if self.use_random_ids {
121 Uuid::new_v4().to_string()
122 } else {
123 format!("{}-{}-{}", self.venue, self.raw_id, self.execution_count)
124 };
125 TradeId::from(trade_id.as_str())
126 }
127
128 pub fn generate_venue_position_id(&mut self) -> Option<PositionId> {
129 if !self.use_position_ids {
130 return None;
131 }
132
133 self.position_count += 1;
134 if self.use_random_ids {
135 Some(PositionId::new(Uuid::new_v4().to_string()))
136 } else {
137 Some(PositionId::new(
138 format!("{}-{}-{}", self.venue, self.raw_id, self.position_count).as_str(),
139 ))
140 }
141 }
142
143 pub fn generate_venue_order_id(&mut self) -> VenueOrderId {
144 self.order_count += 1;
145 if self.use_random_ids {
146 VenueOrderId::new(Uuid::new_v4().to_string())
147 } else {
148 VenueOrderId::new(
149 format!("{}-{}-{}", self.venue, self.raw_id, self.order_count).as_str(),
150 )
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use std::{cell::RefCell, rc::Rc};
158
159 use nautilus_common::cache::Cache;
160 use nautilus_core::time::AtomicTime;
161 use nautilus_model::{
162 enums::OmsType,
163 events::OrderFilled,
164 identifiers::{stubs::account_id, AccountId, PositionId, Venue, VenueOrderId},
165 instruments::InstrumentAny,
166 orders::OrderAny,
167 position::Position,
168 };
169 use rstest::rstest;
170
171 use crate::matching_engine::{
172 ids_generator::IdsGenerator,
173 tests::{
174 instrument_eth_usdt, market_order_buy, market_order_fill, market_order_sell, time,
175 },
176 };
177
178 fn get_ids_generator(
179 cache: Rc<RefCell<Cache>>,
180 use_position_ids: bool,
181 oms_type: OmsType,
182 ) -> IdsGenerator {
183 IdsGenerator::new(
184 Venue::from("BINANCE"),
185 oms_type,
186 1,
187 false,
188 use_position_ids,
189 cache,
190 )
191 }
192
193 #[rstest]
194 fn test_get_position_id_hedging_with_existing_position(
195 account_id: AccountId,
196 time: AtomicTime,
197 instrument_eth_usdt: InstrumentAny,
198 market_order_buy: OrderAny,
199 market_order_fill: OrderFilled,
200 ) {
201 let cache = Rc::new(RefCell::new(Cache::default()));
202 let mut ids_generator = get_ids_generator(cache.clone(), false, OmsType::Hedging);
203
204 let position = Position::new(&instrument_eth_usdt, market_order_fill);
205
206 cache
208 .borrow_mut()
209 .add_position(position.clone(), OmsType::Hedging)
210 .unwrap();
211
212 let position_id = ids_generator.get_position_id(&market_order_buy, None);
213 assert_eq!(position_id, Some(position.id));
214 }
215
216 #[rstest]
217 fn test_get_position_id_hedging_with_generated_position(
218 instrument_eth_usdt: InstrumentAny,
219 account_id: AccountId,
220 market_order_buy: OrderAny,
221 ) {
222 let cache = Rc::new(RefCell::new(Cache::default()));
223 let mut ids_generator = get_ids_generator(cache, true, OmsType::Hedging);
224
225 let position_id = ids_generator.get_position_id(&market_order_buy, None);
226 assert_eq!(position_id, Some(PositionId::new("BINANCE-1-1")));
227 }
228
229 #[rstest]
230 fn test_get_position_id_netting(
231 instrument_eth_usdt: InstrumentAny,
232 account_id: AccountId,
233 market_order_buy: OrderAny,
234 market_order_fill: OrderFilled,
235 ) {
236 let cache = Rc::new(RefCell::new(Cache::default()));
237 let mut ids_generator = get_ids_generator(cache.clone(), false, OmsType::Netting);
238
239 let position_id = ids_generator.get_position_id(&market_order_buy, None);
241 assert_eq!(position_id, None);
242
243 let position = Position::new(&instrument_eth_usdt, market_order_fill);
245 cache
246 .as_ref()
247 .borrow_mut()
248 .add_position(position.clone(), OmsType::Netting)
249 .unwrap();
250
251 let position_id = ids_generator.get_position_id(&market_order_buy, None);
253 assert_eq!(position_id, Some(position.id));
254 }
255
256 #[rstest]
257 fn test_generate_venue_position_id(
258 account_id: AccountId,
259 time: AtomicTime,
260 instrument_eth_usdt: InstrumentAny,
261 ) {
262 let cache = Rc::new(RefCell::new(Cache::default()));
263 let mut ids_generator_with_position_ids =
264 get_ids_generator(cache.clone(), true, OmsType::Netting);
265 let mut ids_generator_no_position_ids = get_ids_generator(cache, false, OmsType::Netting);
266
267 assert_eq!(
268 ids_generator_no_position_ids.generate_venue_position_id(),
269 None
270 );
271
272 let position_id_1 = ids_generator_with_position_ids.generate_venue_position_id();
273 let position_id_2 = ids_generator_with_position_ids.generate_venue_position_id();
274 assert_eq!(position_id_1, Some(PositionId::new("BINANCE-1-1")));
275 assert_eq!(position_id_2, Some(PositionId::new("BINANCE-1-2")));
276 }
277
278 #[rstest]
279 fn get_venue_position_id(
280 instrument_eth_usdt: InstrumentAny,
281 account_id: AccountId,
282 market_order_buy: OrderAny,
283 market_order_sell: OrderAny,
284 market_order_fill: OrderFilled,
285 ) {
286 let cache = Rc::new(RefCell::new(Cache::default()));
287 let mut ids_generator = get_ids_generator(cache, true, OmsType::Netting);
288
289 let venue_order_id1 = ids_generator.get_venue_order_id(&market_order_buy).unwrap();
290 let venue_order_id2 = ids_generator
291 .get_venue_order_id(&market_order_sell)
292 .unwrap();
293 assert_eq!(venue_order_id1, VenueOrderId::from("BINANCE-1-1"));
294 assert_eq!(venue_order_id2, VenueOrderId::from("BINANCE-1-2"));
295
296 let venue_order_id3 = ids_generator.get_venue_order_id(&market_order_buy).unwrap();
298 assert_eq!(venue_order_id3, VenueOrderId::from("BINANCE-1-1"));
299 }
300}