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, OrderBookDepth, 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::Buy,
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() -> OrderBookDepth {
211 stub_depth(DEPTH10_LEN)
212}
213
214#[must_use]
216pub fn stub_depth(levels: usize) -> OrderBookDepth {
217 let instrument_id = InstrumentId::from("AAPL.XNAS");
218 let flags = 0;
219 let sequence = 0;
220 let ts_event = 1;
221 let ts_init = 2;
222
223 let mut bids = Vec::with_capacity(levels);
224 let mut asks = Vec::with_capacity(levels);
225
226 let mut price = 99.00;
227 let mut quantity = 100.0;
228
229 for i in 0..levels {
230 bids.push(BookOrder::new(
231 OrderSide::Buy,
232 Price::new(price, 2),
233 Quantity::new(quantity, 0),
234 (i + 1) as u64,
235 ));
236
237 price -= 1.0;
238 quantity += 100.0;
239 }
240
241 let mut price = 100.00;
242 let mut quantity = 100.0;
243
244 for i in 0..levels {
245 asks.push(BookOrder::new(
246 OrderSide::Sell,
247 Price::new(price, 2),
248 Quantity::new(quantity, 0),
249 (i + levels + 1) as u64,
250 ));
251
252 price += 1.0;
253 quantity += 100.0;
254 }
255
256 let bid_counts = vec![1; levels];
257 let ask_counts = vec![1; levels];
258
259 OrderBookDepth::new(
260 instrument_id,
261 bids,
262 asks,
263 bid_counts,
264 ask_counts,
265 flags,
266 sequence,
267 ts_event.into(),
268 ts_init.into(),
269 )
270}
271
272#[fixture]
273pub fn stub_book_order() -> BookOrder {
274 let price = Price::from("100.00");
275 let size = Quantity::from("10");
276 let side = OrderSide::Buy;
277 let order_id = 123_456;
278
279 BookOrder::new(side, price, size, order_id)
280}
281
282#[fixture]
283pub fn quote_ethusdt_binance() -> QuoteTick {
284 QuoteTick {
285 instrument_id: InstrumentId::from("ETHUSDT-PERP.BINANCE"),
286 bid_price: Price::from("10000.0000"),
287 ask_price: Price::from("10001.0000"),
288 bid_size: Quantity::from("1.00000000"),
289 ask_size: Quantity::from("1.00000000"),
290 ts_event: UnixNanos::default(),
291 ts_init: UnixNanos::from(1),
292 }
293}
294
295#[fixture]
296pub fn quote_audusd() -> QuoteTick {
297 QuoteTick {
298 instrument_id: InstrumentId::from("AUD/USD.SIM"),
299 bid_price: Price::from("100.0000"),
300 ask_price: Price::from("101.0000"),
301 bid_size: Quantity::from("1.00000000"),
302 ask_size: Quantity::from("1.00000000"),
303 ts_event: UnixNanos::default(),
304 ts_init: UnixNanos::from(1),
305 }
306}
307
308#[fixture]
309pub fn stub_trade_ethusdt_buy() -> TradeTick {
310 TradeTick {
311 instrument_id: InstrumentId::from("ETHUSDT-PERP.BINANCE"),
312 price: Price::from("10000.0000"),
313 size: Quantity::from("1.00000000"),
314 aggressor_side: AggressorSide::Buy,
315 trade_id: TradeId::new("123456789"),
316 ts_event: UnixNanos::default(),
317 ts_init: UnixNanos::from(1),
318 }
319}
320
321#[fixture]
322pub fn stub_bar() -> Bar {
323 let instrument_id = InstrumentId {
324 symbol: Symbol::new("AUD/USD"),
325 venue: Venue::new("SIM"),
326 };
327 let bar_spec = BarSpecification::new(1, BarAggregation::Minute, PriceType::Bid);
328 let bar_type = BarType::Standard {
329 instrument_id,
330 spec: bar_spec,
331 aggregation_source: AggregationSource::External,
332 };
333 Bar {
334 bar_type,
335 open: Price::from("1.00002"),
336 high: Price::from("1.00004"),
337 low: Price::from("1.00001"),
338 close: Price::from("1.00003"),
339 volume: Quantity::from("100000"),
340 ts_event: UnixNanos::default(),
341 ts_init: UnixNanos::from(1),
342 }
343}
344
345#[fixture]
346pub fn stub_instrument_status() -> InstrumentStatus {
347 let instrument_id = InstrumentId::from("MSFT.XNAS");
348 InstrumentStatus::new(
349 instrument_id,
350 MarketStatusAction::Trading,
351 UnixNanos::from(1),
352 UnixNanos::from(2),
353 None,
354 None,
355 None,
356 None,
357 None,
358 )
359}
360
361#[fixture]
362pub fn stub_instrument_close() -> InstrumentClose {
363 let instrument_id = InstrumentId::from("MSFT.XNAS");
364 InstrumentClose::new(
365 instrument_id,
366 Price::from("100.50"),
367 InstrumentCloseType::EndOfSession,
368 UnixNanos::from(1),
369 UnixNanos::from(2),
370 )
371}
372
373#[derive(Debug)]
374pub struct OrderBookDeltaTestBuilder {
375 instrument_id: InstrumentId,
376 action: Option<BookAction>,
377 book_order: Option<BookOrder>,
378 flags: Option<u8>,
379 sequence: Option<u64>,
380 ts_event: Option<UnixNanos>,
381 ts_init: Option<UnixNanos>,
382}
383
384impl OrderBookDeltaTestBuilder {
385 #[must_use]
386 pub fn new(instrument_id: InstrumentId) -> Self {
387 Self {
388 instrument_id,
389 action: None,
390 book_order: None,
391 flags: None,
392 sequence: None,
393 ts_event: None,
394 ts_init: None,
395 }
396 }
397
398 pub fn book_action(&mut self, action: BookAction) -> &mut Self {
399 self.action = Some(action);
400 self
401 }
402
403 fn get_book_action(&self) -> BookAction {
404 self.action.unwrap_or(BookAction::Add)
405 }
406
407 pub fn book_order(&mut self, book_order: BookOrder) -> &mut Self {
408 self.book_order = Some(book_order);
409 self
410 }
411
412 fn get_book_order(&self) -> BookOrder {
413 self.book_order.unwrap_or(BookOrder::new(
414 OrderSide::Sell,
415 Price::from("1500.00"),
416 Quantity::from("1"),
417 1,
418 ))
419 }
420
421 pub fn flags(&mut self, flags: u8) -> &mut Self {
422 self.flags = Some(flags);
423 self
424 }
425
426 fn get_flags(&self) -> u8 {
427 self.flags.unwrap_or(0)
428 }
429
430 pub fn sequence(&mut self, sequence: u64) -> &mut Self {
431 self.sequence = Some(sequence);
432 self
433 }
434
435 fn get_sequence(&self) -> u64 {
436 self.sequence.unwrap_or(1)
437 }
438
439 pub fn ts_event(&mut self, ts_event: UnixNanos) -> &mut Self {
440 self.ts_event = Some(ts_event);
441 self
442 }
443
444 pub fn ts_init(&mut self, ts_init: UnixNanos) -> &mut Self {
445 self.ts_init = Some(ts_init);
446 self
447 }
448
449 #[must_use]
450 pub fn build(&self) -> OrderBookDelta {
451 OrderBookDelta::new(
452 self.instrument_id,
453 self.get_book_action(),
454 self.get_book_order(),
455 self.get_flags(),
456 self.get_sequence(),
457 self.ts_event.unwrap_or(UnixNanos::from(1)),
458 self.ts_init.unwrap_or(UnixNanos::from(2)),
459 )
460 }
461}
462
463#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
465pub struct StubCustomData {
466 pub ts_init: UnixNanos,
467 pub value: i64,
468}
469
470impl HasTsInit for StubCustomData {
471 fn ts_init(&self) -> UnixNanos {
472 self.ts_init
473 }
474}
475
476impl CustomDataTrait for StubCustomData {
477 fn type_name(&self) -> &'static str {
478 "StubCustomData"
479 }
480 fn as_any(&self) -> &dyn std::any::Any {
481 self
482 }
483 fn ts_event(&self) -> UnixNanos {
484 self.ts_init
485 }
486 fn to_json(&self) -> anyhow::Result<String> {
487 Ok(serde_json::to_string(self)?)
488 }
489 fn clone_arc(&self) -> Arc<dyn CustomDataTrait> {
490 Arc::new(self.clone())
491 }
492 fn eq_arc(&self, other: &dyn CustomDataTrait) -> bool {
493 if let Some(o) = other.as_any().downcast_ref::<Self>() {
494 self == o
495 } else {
496 false
497 }
498 }
499
500 fn type_name_static() -> &'static str {
501 "StubCustomData"
502 }
503 fn from_json(value: serde_json::Value) -> anyhow::Result<Arc<dyn CustomDataTrait>> {
504 let parsed: Self = serde_json::from_value(value)?;
505 Ok(Arc::new(parsed))
506 }
507}
508
509pub fn ensure_stub_custom_data_registered() {
511 let _ = register_custom_data_json::<StubCustomData>();
512}
513
514#[must_use]
516pub fn stub_custom_data(
517 ts_init: u64,
518 value: i64,
519 metadata: Option<Params>,
520 identifier: Option<String>,
521) -> CustomData {
522 ensure_stub_custom_data_registered();
523 let inner = StubCustomData {
524 ts_init: UnixNanos::from(ts_init),
525 value,
526 };
527 let data_type = DataType::new("StubCustomData", metadata, identifier);
528 CustomData::new(Arc::new(inner), data_type)
529}