1use serde::{Deserialize, Serialize};
17use time::OffsetDateTime;
18
19use crate::common::error_helpers;
20use crate::contracts::OptionComputation;
21use crate::messages::{IncomingMessages, Notice, ResponseMessage, MARKET_DEPTH_RESET_CODE, NO_SECURITY_DEFINITION_CODE};
22use crate::subscriptions::{DecoderContext, StreamDecoder};
23use crate::Error;
24
25pub(crate) mod common;
27
28mod builder;
29pub use builder::{MarketDataBuilder, MarketDepthBuilder, RealtimeBarsBuilder, TickByTickBuilder};
30
31pub mod generic_tick;
32
33mod quote;
34pub use quote::Quote;
35
36#[doc(hidden)]
38#[cfg(feature = "sync")]
39pub mod sync;
40
41#[doc(hidden)]
42#[cfg(feature = "async")]
43pub mod r#async;
44
45use crate::contracts::tick_types::TickType;
46
47#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
51#[derive(Debug, Serialize, Deserialize, PartialEq)]
52pub struct BidAsk {
53 pub time: OffsetDateTime,
55 pub bid_price: f64,
57 pub ask_price: f64,
59 pub bid_size: f64,
61 pub ask_size: f64,
63 pub bid_ask_attribute: BidAskAttribute,
65}
66
67impl StreamDecoder<BidAsk> for BidAsk {
68 const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::TickByTick];
69
70 fn decode(_context: &DecoderContext, message: &ResponseMessage) -> Result<Self, Error> {
71 match message.message_type() {
72 IncomingMessages::TickByTick => common::decoders::decode_bid_ask_tick(message),
73 _ => Err(Error::unexpected_response(message)),
74 }
75 }
76
77 fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<Vec<u8>, Error> {
78 let request_id = error_helpers::require_request_id_for(request_id, "encode cancel bid/ask ticks")?;
79 common::encoders::encode_cancel_tick_by_tick(request_id)
80 }
81
82 fn rejection_ends_request(notice: &Notice) -> bool {
84 notice.code == NO_SECURITY_DEFINITION_CODE
85 }
86}
87
88#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
90#[derive(Debug, Serialize, Deserialize, PartialEq)]
91pub struct BidAskAttribute {
92 pub bid_past_low: bool,
94 pub ask_past_high: bool,
96}
97
98#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
100#[derive(Debug, Serialize, Deserialize, PartialEq)]
101pub struct MidPoint {
102 pub time: OffsetDateTime,
104 pub mid_point: f64,
106}
107
108impl StreamDecoder<MidPoint> for MidPoint {
109 const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::TickByTick];
110
111 fn decode(_context: &DecoderContext, message: &ResponseMessage) -> Result<Self, Error> {
112 match message.message_type() {
113 IncomingMessages::TickByTick => common::decoders::decode_mid_point_tick(message),
114 _ => Err(Error::unexpected_response(message)),
115 }
116 }
117
118 fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<Vec<u8>, Error> {
119 let request_id = error_helpers::require_request_id_for(request_id, "encode cancel mid point ticks")?;
120 common::encoders::encode_cancel_tick_by_tick(request_id)
121 }
122
123 fn rejection_ends_request(notice: &Notice) -> bool {
125 notice.code == NO_SECURITY_DEFINITION_CODE
126 }
127}
128
129#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
131#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
132pub struct Bar {
133 pub date: OffsetDateTime,
135 pub open: f64,
137 pub high: f64,
139 pub low: f64,
141 pub close: f64,
143 pub volume: f64,
145 pub wap: f64,
147 pub count: i32,
149}
150
151impl StreamDecoder<Bar> for Bar {
152 const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::RealTimeBars];
153
154 fn decode(_context: &DecoderContext, message: &ResponseMessage) -> Result<Self, Error> {
155 match message.message_type() {
156 IncomingMessages::RealTimeBars => common::decoders::decode_realtime_bar(message),
157 _ => Err(Error::unexpected_response(message)),
158 }
159 }
160
161 fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<Vec<u8>, Error> {
162 let request_id = error_helpers::require_request_id_for(request_id, "encode cancel realtime bars")?;
163 common::encoders::encode_cancel_realtime_bars(request_id)
164 }
165
166 fn rejection_ends_request(notice: &Notice) -> bool {
168 notice.code == NO_SECURITY_DEFINITION_CODE
169 }
170}
171
172#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
174#[derive(Debug, Serialize, Deserialize, PartialEq)]
175pub struct Trade {
176 pub time: OffsetDateTime,
178 pub price: f64,
180 pub size: f64,
182 pub trade_attribute: TradeAttribute,
184 pub exchange: String,
186 pub special_conditions: String,
188}
189
190impl StreamDecoder<Trade> for Trade {
191 const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::TickByTick];
192
193 fn decode(_context: &DecoderContext, message: &ResponseMessage) -> Result<Self, Error> {
194 match message.message_type() {
195 IncomingMessages::TickByTick => common::decoders::decode_trade_tick(message),
196 _ => Err(Error::unexpected_response(message)),
197 }
198 }
199
200 fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<Vec<u8>, Error> {
201 let request_id = error_helpers::require_request_id_for(request_id, "encode cancel trade ticks")?;
202 common::encoders::encode_cancel_tick_by_tick(request_id)
203 }
204
205 fn rejection_ends_request(notice: &Notice) -> bool {
207 notice.code == NO_SECURITY_DEFINITION_CODE
208 }
209}
210
211#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
213#[derive(Debug, Serialize, Deserialize, PartialEq)]
214pub struct TradeAttribute {
215 pub past_limit: bool,
217 pub unreported: bool,
219}
220
221#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
223#[derive(Clone, Debug, Copy)]
224pub enum WhatToShow {
225 Trades,
227 MidPoint,
229 Bid,
231 Ask,
233 AggTrades,
239}
240
241impl std::fmt::Display for WhatToShow {
242 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
243 match self {
244 Self::Trades => write!(f, "TRADES"),
245 Self::MidPoint => write!(f, "MIDPOINT"),
246 Self::Bid => write!(f, "BID"),
247 Self::Ask => write!(f, "ASK"),
248 Self::AggTrades => write!(f, "AGGTRADES"),
249 }
250 }
251}
252
253#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
260#[derive(Debug, Serialize, Deserialize, PartialEq)]
261pub enum MarketDepths {
262 MarketDepth(MarketDepth),
264 MarketDepthL2(MarketDepthL2),
266 Reset,
270}
271
272#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
273#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
274pub struct MarketDepth {
276 pub position: i32,
278 pub operation: i32,
280 pub side: i32,
282 pub price: f64,
284 pub size: f64,
286}
287
288#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
290#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
291pub struct MarketDepthL2 {
292 pub position: i32,
294 pub market_maker: String,
296 pub operation: i32,
298 pub side: i32,
300 pub price: f64,
302 pub size: f64,
304 pub smart_depth: bool,
306}
307
308impl StreamDecoder<MarketDepths> for MarketDepths {
309 const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::MarketDepth, IncomingMessages::MarketDepthL2];
310
311 fn decode(_context: &DecoderContext, message: &ResponseMessage) -> Result<Self, Error> {
312 match message.message_type() {
313 IncomingMessages::MarketDepth => Ok(MarketDepths::MarketDepth(common::decoders::decode_market_depth(message)?)),
314 IncomingMessages::MarketDepthL2 => Ok(MarketDepths::MarketDepthL2(common::decoders::decode_market_depth_l2(message)?)),
315 _ => Err(Error::unexpected_response(message)),
316 }
317 }
318
319 fn data_from_notice(notice: &Notice) -> Option<Self> {
320 (notice.code == MARKET_DEPTH_RESET_CODE).then_some(MarketDepths::Reset)
321 }
322
323 fn cancel_message(_server_version: i32, request_id: Option<i32>, context: Option<&DecoderContext>) -> Result<Vec<u8>, Error> {
324 let request_id = error_helpers::require_request_id_for(request_id, "encode cancel market depth")?;
325 common::encoders::encode_cancel_market_depth(request_id, context.map(|c| c.is_smart_depth).unwrap_or(false))
326 }
327
328 fn rejection_ends_request(notice: &Notice) -> bool {
330 notice.code == NO_SECURITY_DEFINITION_CODE
331 }
332}
333
334#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
336#[derive(Debug, Default)]
337pub struct DepthMarketDataDescription {
338 pub exchange_name: String,
340 pub security_type: String,
342 pub listing_exchange: String,
344 pub service_data_type: String,
346 pub aggregated_group: Option<String>,
348}
349
350#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
352#[derive(Debug)]
353pub enum TickTypes {
354 Price(TickPrice),
356 Size(TickSize),
358 String(TickString),
360 Generic(TickGeneric),
362 OptionComputation(OptionComputation),
364 SnapshotEnd,
366 RequestParameters(TickRequestParameters),
368 PriceSize(TickPriceSize),
370 MarketDataType(crate::market_data::MarketDataType),
372}
373
374impl StreamDecoder<TickTypes> for TickTypes {
375 const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[
376 IncomingMessages::TickPrice,
377 IncomingMessages::TickSize,
378 IncomingMessages::TickString,
379 IncomingMessages::TickGeneric,
380 IncomingMessages::TickOptionComputation,
381 IncomingMessages::TickSnapshotEnd,
382 IncomingMessages::TickReqParams,
383 IncomingMessages::MarketDataType,
384 ];
385
386 fn decode(_context: &DecoderContext, message: &ResponseMessage) -> Result<Self, Error> {
387 match message.message_type() {
388 IncomingMessages::TickPrice => common::decoders::decode_tick_price(message),
389 IncomingMessages::TickSize => common::decoders::decode_tick_size(message).map(TickTypes::Size),
390 IncomingMessages::TickString => common::decoders::decode_tick_string(message).map(TickTypes::String),
391 IncomingMessages::TickGeneric => common::decoders::decode_tick_generic(message).map(TickTypes::Generic),
392 IncomingMessages::TickOptionComputation => common::decoders::decode_tick_option_computation(message).map(TickTypes::OptionComputation),
393 IncomingMessages::TickReqParams => common::decoders::decode_tick_request_parameters(message).map(TickTypes::RequestParameters),
394 IncomingMessages::MarketDataType => common::decoders::decode_market_data_type(message).map(TickTypes::MarketDataType),
395 IncomingMessages::TickSnapshotEnd => Ok(TickTypes::SnapshotEnd),
396 _ => Err(Error::unexpected_response(message)),
397 }
398 }
399
400 fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<Vec<u8>, Error> {
401 let request_id = error_helpers::require_request_id_for(request_id, "encode cancel market data")?;
402 common::encoders::encode_cancel_market_data(request_id)
403 }
404
405 fn rejection_ends_request(notice: &Notice) -> bool {
407 notice.code == NO_SECURITY_DEFINITION_CODE
408 }
409
410 fn is_snapshot_end(&self) -> bool {
411 matches!(self, TickTypes::SnapshotEnd)
412 }
413}
414
415#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
417#[derive(Debug, Default)]
418pub struct TickPrice {
419 pub tick_type: TickType,
421 pub price: f64,
423 pub attributes: TickAttribute,
425}
426
427#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
429#[derive(Debug, PartialEq, Default)]
430pub struct TickAttribute {
431 pub can_auto_execute: bool,
433 pub past_limit: bool,
435 pub pre_open: bool,
437}
438
439#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
441#[derive(Debug, Default)]
442pub struct TickSize {
443 pub tick_type: TickType,
445 pub size: f64,
447}
448
449#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
451#[derive(Debug, Default)]
452pub struct TickPriceSize {
453 pub price_tick_type: TickType,
455 pub price: f64,
457 pub attributes: TickAttribute,
459 pub size_tick_type: TickType,
461 pub size: f64,
463}
464
465#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
467#[derive(Debug, Default)]
468pub struct TickString {
469 pub tick_type: TickType,
471 pub value: String,
473}
474
475#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
477#[derive(Debug, Default)]
478pub struct TickGeneric {
479 pub tick_type: TickType,
481 pub value: f64,
483}
484
485#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
487#[derive(Debug, Default)]
488pub struct TickRequestParameters {
489 pub min_tick: f64,
491 pub bbo_exchange: String,
493 pub snapshot_permissions: i32,
495}
496
497#[cfg(test)]
498#[path = "mod_tests.rs"]
499mod tests;