1use std::sync::Arc;
19
20use nautilus_core::{Params, UnixNanos};
21use rstest::fixture;
22use serde::{Deserialize, Serialize};
23
24use super::{
25 Bar, BarSpecification, BarType, CustomData, CustomDataTrait, DEPTH10_LEN, DataType, HasTsInit,
26 InstrumentStatus, OrderBookDelta, OrderBookDeltas, OrderBookDepth10, QuoteTick, TradeTick,
27 close::InstrumentClose, register_custom_data_json,
28};
29use crate::{
30 data::order::BookOrder,
31 enums::{
32 AggregationSource, AggressorSide, BarAggregation, BookAction, InstrumentCloseType,
33 MarketStatusAction, OrderSide, PriceType,
34 },
35 identifiers::{InstrumentId, Symbol, TradeId, Venue},
36 types::{Price, Quantity},
37};
38
39impl Default for QuoteTick {
40 fn default() -> Self {
42 Self {
43 instrument_id: InstrumentId::from("AUDUSD.SIM"),
44 bid_price: Price::from("1.00000"),
45 ask_price: Price::from("1.00000"),
46 bid_size: Quantity::from(100_000),
47 ask_size: Quantity::from(100_000),
48 ts_event: UnixNanos::default(),
49 ts_init: UnixNanos::default(),
50 }
51 }
52}
53
54impl Default for TradeTick {
55 fn default() -> Self {
57 Self {
58 instrument_id: InstrumentId::from("AUDUSD.SIM"),
59 price: Price::from("1.00000"),
60 size: Quantity::from(100_000),
61 aggressor_side: AggressorSide::Buyer,
62 trade_id: TradeId::new("123456789"),
63 ts_event: UnixNanos::default(),
64 ts_init: UnixNanos::default(),
65 }
66 }
67}
68
69impl Default for Bar {
70 fn default() -> Self {
72 Self {
73 bar_type: BarType::from("AUDUSD.SIM-1-MINUTE-LAST-INTERNAL"),
74 open: Price::from("1.00010"),
75 high: Price::from("1.00020"),
76 low: Price::from("1.00000"),
77 close: Price::from("1.00010"),
78 volume: Quantity::from(100_000),
79 ts_event: UnixNanos::default(),
80 ts_init: UnixNanos::default(),
81 }
82 }
83}
84
85#[fixture]
86pub fn stub_delta() -> OrderBookDelta {
87 let instrument_id = InstrumentId::from("AAPL.XNAS");
88 let action = BookAction::Add;
89 let price = Price::from("100.00");
90 let size = Quantity::from("10");
91 let side = OrderSide::Buy;
92 let order_id = 123_456;
93 let flags = 0;
94 let sequence = 1;
95 let ts_event = 1;
96 let ts_init = 2;
97
98 let order = BookOrder::new(side, price, size, order_id);
99 OrderBookDelta::new(
100 instrument_id,
101 action,
102 order,
103 flags,
104 sequence,
105 ts_event.into(),
106 ts_init.into(),
107 )
108}
109
110#[fixture]
111pub fn stub_deltas() -> OrderBookDeltas {
112 let instrument_id = InstrumentId::from("AAPL.XNAS");
113 let flags = 32; let sequence = 0;
115 let ts_event = 1;
116 let ts_init = 2;
117
118 let delta0 = OrderBookDelta::clear(instrument_id, sequence, ts_event.into(), ts_init.into());
119 let delta1 = OrderBookDelta::new(
120 instrument_id,
121 BookAction::Add,
122 BookOrder::new(
123 OrderSide::Sell,
124 Price::from("102.00"),
125 Quantity::from("300"),
126 1,
127 ),
128 flags,
129 sequence,
130 ts_event.into(),
131 ts_init.into(),
132 );
133 let delta2 = OrderBookDelta::new(
134 instrument_id,
135 BookAction::Add,
136 BookOrder::new(
137 OrderSide::Sell,
138 Price::from("101.00"),
139 Quantity::from("200"),
140 2,
141 ),
142 flags,
143 sequence,
144 ts_event.into(),
145 ts_init.into(),
146 );
147 let delta3 = OrderBookDelta::new(
148 instrument_id,
149 BookAction::Add,
150 BookOrder::new(
151 OrderSide::Sell,
152 Price::from("100.00"),
153 Quantity::from("100"),
154 3,
155 ),
156 flags,
157 sequence,
158 ts_event.into(),
159 ts_init.into(),
160 );
161 let delta4 = OrderBookDelta::new(
162 instrument_id,
163 BookAction::Add,
164 BookOrder::new(
165 OrderSide::Buy,
166 Price::from("99.00"),
167 Quantity::from("100"),
168 4,
169 ),
170 flags,
171 sequence,
172 ts_event.into(),
173 ts_init.into(),
174 );
175 let delta5 = OrderBookDelta::new(
176 instrument_id,
177 BookAction::Add,
178 BookOrder::new(
179 OrderSide::Buy,
180 Price::from("98.00"),
181 Quantity::from("200"),
182 5,
183 ),
184 flags,
185 sequence,
186 ts_event.into(),
187 ts_init.into(),
188 );
189 let delta6 = OrderBookDelta::new(
190 instrument_id,
191 BookAction::Add,
192 BookOrder::new(
193 OrderSide::Buy,
194 Price::from("97.00"),
195 Quantity::from("300"),
196 6,
197 ),
198 flags,
199 sequence,
200 ts_event.into(),
201 ts_init.into(),
202 );
203
204 let deltas = vec![delta0, delta1, delta2, delta3, delta4, delta5, delta6];
205
206 OrderBookDeltas::new(instrument_id, deltas)
207}
208
209#[fixture]
210pub fn stub_depth10() -> OrderBookDepth10 {
211 let instrument_id = InstrumentId::from("AAPL.XNAS");
212 let flags = 0;
213 let sequence = 0;
214 let ts_event = 1;
215 let ts_init = 2;
216
217 let mut bids: [BookOrder; DEPTH10_LEN] = [BookOrder::default(); DEPTH10_LEN];
218 let mut asks: [BookOrder; DEPTH10_LEN] = [BookOrder::default(); DEPTH10_LEN];
219
220 let mut price = 99.00;
222 let mut quantity = 100.0;
223
224 for (i, bid) in bids.iter_mut().enumerate() {
225 *bid = BookOrder::new(
226 OrderSide::Buy,
227 Price::new(price, 2),
228 Quantity::new(quantity, 0),
229 (i + 1) as u64,
230 );
231
232 price -= 1.0;
233 quantity += 100.0;
234 }
235
236 let mut price = 100.00;
238 let mut quantity = 100.0;
239
240 for (i, ask) in asks.iter_mut().enumerate() {
241 *ask = BookOrder::new(
242 OrderSide::Sell,
243 Price::new(price, 2),
244 Quantity::new(quantity, 0),
245 (i + 11) as u64,
246 );
247
248 price += 1.0;
249 quantity += 100.0;
250 }
251
252 let bid_counts: [u32; DEPTH10_LEN] = [1; DEPTH10_LEN];
253 let ask_counts: [u32; DEPTH10_LEN] = [1; DEPTH10_LEN];
254
255 OrderBookDepth10::new(
256 instrument_id,
257 bids,
258 asks,
259 bid_counts,
260 ask_counts,
261 flags,
262 sequence,
263 ts_event.into(),
264 ts_init.into(),
265 )
266}
267
268#[fixture]
269pub fn stub_book_order() -> BookOrder {
270 let price = Price::from("100.00");
271 let size = Quantity::from("10");
272 let side = OrderSide::Buy;
273 let order_id = 123_456;
274
275 BookOrder::new(side, price, size, order_id)
276}
277
278#[fixture]
279pub fn quote_ethusdt_binance() -> QuoteTick {
280 QuoteTick {
281 instrument_id: InstrumentId::from("ETHUSDT-PERP.BINANCE"),
282 bid_price: Price::from("10000.0000"),
283 ask_price: Price::from("10001.0000"),
284 bid_size: Quantity::from("1.00000000"),
285 ask_size: Quantity::from("1.00000000"),
286 ts_event: UnixNanos::default(),
287 ts_init: UnixNanos::from(1),
288 }
289}
290
291#[fixture]
292pub fn quote_audusd() -> QuoteTick {
293 QuoteTick {
294 instrument_id: InstrumentId::from("AUD/USD.SIM"),
295 bid_price: Price::from("100.0000"),
296 ask_price: Price::from("101.0000"),
297 bid_size: Quantity::from("1.00000000"),
298 ask_size: Quantity::from("1.00000000"),
299 ts_event: UnixNanos::default(),
300 ts_init: UnixNanos::from(1),
301 }
302}
303
304#[fixture]
305pub fn stub_trade_ethusdt_buyer() -> TradeTick {
306 TradeTick {
307 instrument_id: InstrumentId::from("ETHUSDT-PERP.BINANCE"),
308 price: Price::from("10000.0000"),
309 size: Quantity::from("1.00000000"),
310 aggressor_side: AggressorSide::Buyer,
311 trade_id: TradeId::new("123456789"),
312 ts_event: UnixNanos::default(),
313 ts_init: UnixNanos::from(1),
314 }
315}
316
317#[fixture]
318pub fn stub_bar() -> Bar {
319 let instrument_id = InstrumentId {
320 symbol: Symbol::new("AUD/USD"),
321 venue: Venue::new("SIM"),
322 };
323 let bar_spec = BarSpecification::new(1, BarAggregation::Minute, PriceType::Bid);
324 let bar_type = BarType::Standard {
325 instrument_id,
326 spec: bar_spec,
327 aggregation_source: AggregationSource::External,
328 };
329 Bar {
330 bar_type,
331 open: Price::from("1.00002"),
332 high: Price::from("1.00004"),
333 low: Price::from("1.00001"),
334 close: Price::from("1.00003"),
335 volume: Quantity::from("100000"),
336 ts_event: UnixNanos::default(),
337 ts_init: UnixNanos::from(1),
338 }
339}
340
341#[fixture]
342pub fn stub_instrument_status() -> InstrumentStatus {
343 let instrument_id = InstrumentId::from("MSFT.XNAS");
344 InstrumentStatus::new(
345 instrument_id,
346 MarketStatusAction::Trading,
347 UnixNanos::from(1),
348 UnixNanos::from(2),
349 None,
350 None,
351 None,
352 None,
353 None,
354 )
355}
356
357#[fixture]
358pub fn stub_instrument_close() -> InstrumentClose {
359 let instrument_id = InstrumentId::from("MSFT.XNAS");
360 InstrumentClose::new(
361 instrument_id,
362 Price::from("100.50"),
363 InstrumentCloseType::EndOfSession,
364 UnixNanos::from(1),
365 UnixNanos::from(2),
366 )
367}
368
369#[derive(Debug)]
370pub struct OrderBookDeltaTestBuilder {
371 instrument_id: InstrumentId,
372 action: Option<BookAction>,
373 book_order: Option<BookOrder>,
374 flags: Option<u8>,
375 sequence: Option<u64>,
376 ts_event: Option<UnixNanos>,
377}
378
379impl OrderBookDeltaTestBuilder {
380 #[must_use]
381 pub fn new(instrument_id: InstrumentId) -> Self {
382 Self {
383 instrument_id,
384 action: None,
385 book_order: None,
386 flags: None,
387 sequence: None,
388 ts_event: None,
389 }
390 }
391
392 pub fn book_action(&mut self, action: BookAction) -> &mut Self {
393 self.action = Some(action);
394 self
395 }
396
397 fn get_book_action(&self) -> BookAction {
398 self.action.unwrap_or(BookAction::Add)
399 }
400
401 pub fn book_order(&mut self, book_order: BookOrder) -> &mut Self {
402 self.book_order = Some(book_order);
403 self
404 }
405
406 fn get_book_order(&self) -> BookOrder {
407 self.book_order.unwrap_or(BookOrder::new(
408 OrderSide::Sell,
409 Price::from("1500.00"),
410 Quantity::from("1"),
411 1,
412 ))
413 }
414
415 pub fn flags(&mut self, flags: u8) -> &mut Self {
416 self.flags = Some(flags);
417 self
418 }
419
420 fn get_flags(&self) -> u8 {
421 self.flags.unwrap_or(0)
422 }
423
424 pub fn sequence(&mut self, sequence: u64) -> &mut Self {
425 self.sequence = Some(sequence);
426 self
427 }
428
429 fn get_sequence(&self) -> u64 {
430 self.sequence.unwrap_or(1)
431 }
432
433 pub fn ts_event(&mut self, ts_event: UnixNanos) -> &mut Self {
434 self.ts_event = Some(ts_event);
435 self
436 }
437
438 #[must_use]
439 pub fn build(&self) -> OrderBookDelta {
440 OrderBookDelta::new(
441 self.instrument_id,
442 self.get_book_action(),
443 self.get_book_order(),
444 self.get_flags(),
445 self.get_sequence(),
446 self.ts_event.unwrap_or(UnixNanos::from(1)),
447 UnixNanos::from(2),
448 )
449 }
450}
451
452#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
454pub struct StubCustomData {
455 pub ts_init: UnixNanos,
456 pub value: i64,
457}
458
459impl HasTsInit for StubCustomData {
460 fn ts_init(&self) -> UnixNanos {
461 self.ts_init
462 }
463}
464
465impl CustomDataTrait for StubCustomData {
466 fn type_name(&self) -> &'static str {
467 "StubCustomData"
468 }
469 fn as_any(&self) -> &dyn std::any::Any {
470 self
471 }
472 fn ts_event(&self) -> UnixNanos {
473 self.ts_init
474 }
475 fn to_json(&self) -> anyhow::Result<String> {
476 Ok(serde_json::to_string(self)?)
477 }
478 fn clone_arc(&self) -> Arc<dyn CustomDataTrait> {
479 Arc::new(self.clone())
480 }
481 fn eq_arc(&self, other: &dyn CustomDataTrait) -> bool {
482 if let Some(o) = other.as_any().downcast_ref::<Self>() {
483 self == o
484 } else {
485 false
486 }
487 }
488
489 fn type_name_static() -> &'static str {
490 "StubCustomData"
491 }
492 fn from_json(value: serde_json::Value) -> anyhow::Result<Arc<dyn CustomDataTrait>> {
493 let parsed: Self = serde_json::from_value(value)?;
494 Ok(Arc::new(parsed))
495 }
496}
497
498pub fn ensure_stub_custom_data_registered() {
500 let _ = register_custom_data_json::<StubCustomData>();
501}
502
503#[must_use]
505pub fn stub_custom_data(
506 ts_init: u64,
507 value: i64,
508 metadata: Option<Params>,
509 identifier: Option<String>,
510) -> CustomData {
511 ensure_stub_custom_data_registered();
512 let inner = StubCustomData {
513 ts_init: UnixNanos::from(ts_init),
514 value,
515 };
516 let data_type = DataType::new("StubCustomData", metadata, identifier);
517 CustomData::new(Arc::new(inner), data_type)
518}