1use super::{
17 Bar, Cache, CustomData, DataEngine, Display, FundingRateUpdate, IndexPriceUpdate, Instrument,
18 InstrumentAny, InstrumentClose, InstrumentStatus, MarkPriceUpdate, MarketStatusAction,
19 OptionGreeks, OrderBookDelta, OrderBookDeltas, OrderBookDepth, QuoteTick, Rc, RecordFlag,
20 RefCell, TradeTick, book, mem, msgbus, switchboard,
21};
22
23impl DataEngine {
24 #[inline]
25 fn pipeline_cache_writes_allowed(&self) -> bool {
26 !self.config.disable_historical_cache
27 }
28
29 pub(crate) fn handle_instrument(&mut self, instrument: &InstrumentAny) {
30 log::debug!("Handling instrument: {}", instrument.id());
31
32 if let Err(e) = self
33 .cache
34 .as_ref()
35 .borrow_mut()
36 .add_instrument(instrument.clone())
37 {
38 log_error_on_cache_insert(&e);
39 }
40
41 let topic = switchboard::get_instrument_topic(instrument.id());
42 log::debug!("Publishing instrument to topic: {topic}");
43 msgbus::publish_instrument(topic, instrument);
44
45 self.update_option_chains(instrument);
46 }
47
48 pub(super) fn handle_delta(&mut self, delta: OrderBookDelta) {
49 let mut deltas = if self.config.buffer_deltas {
50 self.buffer_delta(delta);
51
52 if !RecordFlag::F_LAST.matches(delta.flags) {
53 return; }
55
56 self.buffered_deltas_map
57 .remove(&delta.instrument_id)
58 .expect("buffered deltas exist")
59 } else {
60 self.single_delta_batch(delta)
61 };
62
63 let topic = switchboard::get_book_deltas_topic(deltas.instrument_id);
64 msgbus::publish_deltas(topic, &deltas);
65 self.reclaim_deltas_frame(mem::take(&mut deltas.deltas));
66 }
67
68 pub(super) fn handle_deltas(&mut self, deltas: &OrderBookDeltas) {
69 if self.config.buffer_deltas {
70 let instrument_id = deltas.instrument_id;
71
72 for delta in &deltas.deltas {
73 let is_last = RecordFlag::F_LAST.matches(delta.flags);
74 self.buffer_delta(*delta);
75
76 if is_last {
77 let mut deltas_to_publish = self
78 .buffered_deltas_map
79 .remove(&instrument_id)
80 .expect("buffered deltas exist");
81 let topic = switchboard::get_book_deltas_topic(instrument_id);
82 msgbus::publish_deltas(topic, &deltas_to_publish);
83 self.reclaim_deltas_frame(mem::take(&mut deltas_to_publish.deltas));
84 }
85 }
86 } else {
87 let topic = switchboard::get_book_deltas_topic(deltas.instrument_id);
88 msgbus::publish_deltas(topic, deltas);
89 }
90 }
91
92 pub(super) fn handle_depth(&self, depth: &OrderBookDepth) {
93 let topic = switchboard::get_book_depth_topic(depth.instrument_id);
94 msgbus::publish_depth(topic, depth);
95
96 if self.config.emit_quotes_from_book_depths
97 && let Some(quote) = derive_quote_from_depth(depth)
98 {
99 book::publish_quote_if_changed(&self.cache, quote);
100 }
101 }
102
103 pub(super) fn handle_quote(&self, quote: QuoteTick) {
104 if let Err(e) = self.cache.as_ref().borrow_mut().add_quote(quote) {
105 log_error_on_cache_insert(&e);
106 }
107
108 for synthetic_quote in self.synthetic_quotes_from_quote(quote) {
109 let topic = switchboard::get_quotes_topic(synthetic_quote.instrument_id);
110 msgbus::publish_quote(topic, &synthetic_quote);
111 }
112
113 let topic = switchboard::get_quotes_topic(quote.instrument_id);
114 msgbus::publish_quote(topic, "e);
115 }
116
117 pub(super) fn handle_trade(&self, trade: TradeTick) {
118 if let Err(e) = self.cache.as_ref().borrow_mut().add_trade(trade) {
119 log_error_on_cache_insert(&e);
120 }
121
122 for synthetic_trade in self.synthetic_trades_from_trade(trade) {
123 let topic = switchboard::get_trades_topic(synthetic_trade.instrument_id);
124 msgbus::publish_trade(topic, &synthetic_trade);
125 }
126
127 let topic = switchboard::get_trades_topic(trade.instrument_id);
128 msgbus::publish_trade(topic, &trade);
129 }
130
131 pub(super) fn handle_bar(&self, bar: Bar) {
132 process_engine_bar(&self.cache, self.config.validate_data_sequence, true, bar);
133 }
134
135 pub(super) fn handle_mark_price(&self, mark_price: MarkPriceUpdate) {
136 if let Err(e) = self.cache.as_ref().borrow_mut().add_mark_price(mark_price) {
137 log_error_on_cache_insert(&e);
138 }
139
140 let topic = switchboard::get_mark_price_topic(mark_price.instrument_id);
141 msgbus::publish_mark_price(topic, &mark_price);
142 }
143
144 pub(super) fn handle_index_price(&self, index_price: IndexPriceUpdate) {
145 if let Err(e) = self
146 .cache
147 .as_ref()
148 .borrow_mut()
149 .add_index_price(index_price)
150 {
151 log_error_on_cache_insert(&e);
152 }
153
154 let topic = switchboard::get_index_price_topic(index_price.instrument_id);
155 msgbus::publish_index_price(topic, &index_price);
156 }
157
158 pub fn handle_funding_rate(&mut self, funding_rate: FundingRateUpdate) {
160 if let Err(e) = self
161 .cache
162 .as_ref()
163 .borrow_mut()
164 .add_funding_rate(funding_rate)
165 {
166 log_error_on_cache_insert(&e);
167 }
168
169 let topic = switchboard::get_funding_rate_topic(funding_rate.instrument_id);
170 msgbus::publish_funding_rate(topic, &funding_rate);
171 }
172
173 pub(super) fn handle_instrument_status(&mut self, status: InstrumentStatus) {
174 if let Err(e) = self
175 .cache
176 .as_ref()
177 .borrow_mut()
178 .add_instrument_status(status)
179 {
180 log_error_on_cache_insert(&e);
181 }
182
183 let topic = switchboard::get_instrument_status_topic(status.instrument_id);
184 msgbus::publish_any(topic, &status);
185
186 if self
187 .option_chain_instrument_index
188 .contains_key(&status.instrument_id)
189 && matches!(
190 status.action,
191 MarketStatusAction::Close | MarketStatusAction::NotAvailableForTrading
192 )
193 {
194 self.expire_option_chain_instrument(status.instrument_id);
195 }
196 }
197
198 pub(super) fn handle_instrument_close(&self, close: InstrumentClose) {
199 let topic = switchboard::get_instrument_close_topic(close.instrument_id);
200 msgbus::publish_any(topic, &close);
201 }
202
203 pub(super) fn handle_custom_data(&self, custom: &CustomData) {
204 log::debug!("Processing custom data: {}", custom.data.type_name());
205 let topic = switchboard::get_custom_topic(&custom.data_type);
206 msgbus::publish_any(topic, custom);
207 }
208
209 pub(super) fn handle_delta_pipeline(&mut self, delta: OrderBookDelta) {
210 let mut deltas = self.single_delta_batch(delta);
212 let topic = switchboard::get_pipeline_book_deltas_topic(deltas.instrument_id);
213 msgbus::publish_deltas(topic, &deltas);
214 self.reclaim_deltas_frame(mem::take(&mut deltas.deltas));
215 }
216
217 fn buffer_delta(&mut self, delta: OrderBookDelta) {
218 if let Some(buffered_deltas) = self.buffered_deltas_map.get_mut(&delta.instrument_id) {
219 buffered_deltas.deltas.push(delta);
220 buffered_deltas.flags = delta.flags;
221 buffered_deltas.sequence = delta.sequence;
222 buffered_deltas.ts_event = delta.ts_event;
223 buffered_deltas.ts_init = delta.ts_init;
224 return;
225 }
226
227 let instrument_id = delta.instrument_id;
228 let buffered_deltas = self.single_delta_batch(delta);
229 self.buffered_deltas_map
230 .insert(instrument_id, buffered_deltas);
231 }
232
233 fn single_delta_batch(&mut self, delta: OrderBookDelta) -> OrderBookDeltas {
234 let instrument_id = delta.instrument_id;
235 let mut frame = mem::take(&mut self.deltas_frame);
236 frame.clear();
237 frame.push(delta);
238 OrderBookDeltas::new(instrument_id, frame)
239 }
240
241 fn reclaim_deltas_frame(&mut self, mut frame: Vec<OrderBookDelta>) {
242 frame.clear();
243 self.deltas_frame = frame;
244 }
245
246 pub(super) fn handle_deltas_pipeline(&self, deltas: &OrderBookDeltas) {
247 let topic = switchboard::get_pipeline_book_deltas_topic(deltas.instrument_id);
248 msgbus::publish_deltas(topic, deltas);
249 }
250
251 pub(super) fn handle_depth_pipeline(&self, depth: &OrderBookDepth) {
252 let topic = switchboard::get_pipeline_book_depth_topic(depth.instrument_id);
253 msgbus::publish_depth(topic, depth);
254 }
255
256 pub(super) fn handle_quote_pipeline(&self, quote: QuoteTick) {
257 if self.pipeline_cache_writes_allowed()
258 && let Err(e) = self.cache.as_ref().borrow_mut().add_quote(quote)
259 {
260 log_error_on_cache_insert(&e);
261 }
262
263 let topic = switchboard::get_pipeline_quotes_topic(quote.instrument_id);
264 msgbus::publish_quote(topic, "e);
265 }
266
267 pub(super) fn handle_trade_pipeline(&self, trade: TradeTick) {
268 if self.pipeline_cache_writes_allowed()
269 && let Err(e) = self.cache.as_ref().borrow_mut().add_trade(trade)
270 {
271 log_error_on_cache_insert(&e);
272 }
273
274 let topic = switchboard::get_pipeline_trades_topic(trade.instrument_id);
275 msgbus::publish_trade(topic, &trade);
276 }
277
278 pub(super) fn handle_bar_pipeline(&self, bar: Bar) {
279 if !validate_bar_sequence(&self.cache, self.config.validate_data_sequence, &bar) {
280 return;
281 }
282
283 if self.pipeline_cache_writes_allowed()
284 && let Err(e) = self.cache.as_ref().borrow_mut().add_bar(bar)
285 {
286 log_error_on_cache_insert(&e);
287 }
288
289 let topic = switchboard::get_pipeline_bars_topic(bar.bar_type);
290 msgbus::publish_bar(topic, &bar);
291 }
292
293 pub(super) fn handle_mark_price_pipeline(&self, mark_price: MarkPriceUpdate) {
294 if self.pipeline_cache_writes_allowed()
295 && let Err(e) = self.cache.as_ref().borrow_mut().add_mark_price(mark_price)
296 {
297 log_error_on_cache_insert(&e);
298 }
299
300 let topic = switchboard::get_pipeline_mark_price_topic(mark_price.instrument_id);
301 msgbus::publish_mark_price(topic, &mark_price);
302 }
303
304 pub(super) fn handle_index_price_pipeline(&self, index_price: IndexPriceUpdate) {
305 if self.pipeline_cache_writes_allowed()
306 && let Err(e) = self
307 .cache
308 .as_ref()
309 .borrow_mut()
310 .add_index_price(index_price)
311 {
312 log_error_on_cache_insert(&e);
313 }
314
315 let topic = switchboard::get_pipeline_index_price_topic(index_price.instrument_id);
316 msgbus::publish_index_price(topic, &index_price);
317 }
318
319 pub(super) fn handle_funding_rate_pipeline(&self, funding_rate: FundingRateUpdate) {
320 if self.pipeline_cache_writes_allowed()
321 && let Err(e) = self
322 .cache
323 .as_ref()
324 .borrow_mut()
325 .add_funding_rate(funding_rate)
326 {
327 log_error_on_cache_insert(&e);
328 }
329
330 let topic = switchboard::get_pipeline_funding_rate_topic(funding_rate.instrument_id);
331 msgbus::publish_funding_rate(topic, &funding_rate);
332 }
333
334 pub(super) fn handle_instrument_status_pipeline(&self, status: InstrumentStatus) {
335 if self.pipeline_cache_writes_allowed()
336 && let Err(e) = self
337 .cache
338 .as_ref()
339 .borrow_mut()
340 .add_instrument_status(status)
341 {
342 log_error_on_cache_insert(&e);
343 }
344
345 let topic = switchboard::get_pipeline_instrument_status_topic(status.instrument_id);
346 msgbus::publish_any(topic, &status);
347 }
348
349 pub(super) fn handle_option_greeks_pipeline(&self, greeks: OptionGreeks) {
350 if self.pipeline_cache_writes_allowed() {
351 self.cache.borrow_mut().add_option_greeks(greeks);
352 }
353
354 let topic = switchboard::get_pipeline_option_greeks_topic(greeks.instrument_id);
355 msgbus::publish_option_greeks(topic, &greeks);
356 }
357
358 pub(super) fn handle_instrument_close_pipeline(&self, close: InstrumentClose) {
359 let topic = switchboard::get_pipeline_instrument_close_topic(close.instrument_id);
360 msgbus::publish_any(topic, &close);
361 }
362
363 pub(super) fn handle_custom_data_pipeline(&self, custom: &CustomData) {
364 log::debug!("Pipeline custom data: {}", custom.data.type_name());
365 let topic = switchboard::get_pipeline_custom_topic(&custom.data_type);
366 msgbus::publish_any(topic, custom);
367 }
368
369 pub(super) fn handle_instrument_response(&self, instrument: InstrumentAny) {
370 let mut cache = self.cache.as_ref().borrow_mut();
371 if let Err(e) = cache.add_instrument(instrument) {
372 log_error_on_cache_insert(&e);
373 }
374 }
375
376 pub(super) fn handle_instruments(&self, instruments: &[InstrumentAny]) {
377 let mut cache = self.cache.as_ref().borrow_mut();
379
380 for instrument in instruments {
381 if let Err(e) = cache.add_instrument(instrument.clone()) {
382 log_error_on_cache_insert(&e);
383 }
384 }
385 }
386
387 pub(super) fn handle_quotes(&self, quotes: &[QuoteTick]) {
388 if let Err(e) = self.cache.as_ref().borrow_mut().add_quotes(quotes) {
389 log_error_on_cache_insert(&e);
390 }
391 }
392
393 pub(super) fn handle_trades(&self, trades: &[TradeTick]) {
394 if let Err(e) = self.cache.as_ref().borrow_mut().add_trades(trades) {
395 log_error_on_cache_insert(&e);
396 }
397 }
398
399 pub(super) fn handle_funding_rates(&self, funding_rates: &[FundingRateUpdate]) {
400 if let Err(e) = self
401 .cache
402 .as_ref()
403 .borrow_mut()
404 .add_funding_rates(funding_rates)
405 {
406 log_error_on_cache_insert(&e);
407 }
408 }
409
410 pub(super) fn handle_bars(&self, bars: &[Bar]) {
411 if let Err(e) = self.cache.as_ref().borrow_mut().add_bars(bars) {
412 log_error_on_cache_insert(&e);
413 }
414 }
415}
416
417#[inline(always)]
418pub(super) fn log_error_on_cache_insert<T: Display>(e: &T) {
419 log::error!("Error on cache insert: {e}");
420}
421
422fn derive_quote_from_depth(depth: &OrderBookDepth) -> Option<QuoteTick> {
425 let bid = depth.bids.first()?;
426 let ask = depth.asks.first()?;
427
428 if bid.side.is_none() || ask.side.is_none() || bid.size.is_zero() || ask.size.is_zero() {
429 return None;
430 }
431
432 Some(QuoteTick::new(
433 depth.instrument_id,
434 bid.price,
435 ask.price,
436 bid.size,
437 ask.size,
438 depth.ts_event,
439 depth.ts_init,
440 ))
441}
442
443pub(super) fn process_engine_bar(
447 cache: &Rc<RefCell<Cache>>,
448 validate_sequence: bool,
449 publish: bool,
450 bar: Bar,
451) {
452 debug_assert!(
453 bar.bar_type.is_standard(),
454 "bars must be published and cached under the standard bar type"
455 );
456
457 if !validate_bar_sequence(cache, validate_sequence, &bar) {
458 return;
459 }
460
461 if let Err(e) = cache.as_ref().borrow_mut().add_bar(bar) {
462 log_error_on_cache_insert(&e);
463 }
464
465 if publish {
466 let topic = switchboard::get_bars_topic(bar.bar_type);
467 msgbus::publish_bar(topic, &bar);
468 }
469}
470
471fn validate_bar_sequence(cache: &Rc<RefCell<Cache>>, validate_sequence: bool, bar: &Bar) -> bool {
472 if !validate_sequence {
473 return true;
474 }
475
476 let Some(last_bar) = cache.as_ref().borrow().bar(&bar.bar_type).copied() else {
477 return true;
478 };
479
480 if bar.ts_event < last_bar.ts_event {
481 log::warn!(
482 "Bar {bar} was prior to last bar `ts_event` {}",
483 last_bar.ts_event,
484 );
485 return false;
486 }
487
488 if bar.ts_init < last_bar.ts_init {
489 log::warn!(
490 "Bar {bar} was prior to last bar `ts_init` {}",
491 last_bar.ts_init,
492 );
493 return false;
494 }
495
496 true
497}