use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use crate::ToField;
use crate::contracts::OptionComputation;
use crate::messages::Notice;
use crate::messages::{IncomingMessages, RequestMessage, ResponseMessage};
use crate::subscriptions::{DecoderContext, StreamDecoder};
use crate::Error;
pub(crate) mod common;
#[cfg(feature = "sync")]
pub mod sync;
#[cfg(feature = "async")]
pub mod r#async;
pub use crate::contracts::tick_types::TickType;
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, Copy, Serialize, Deserialize, PartialEq)]
pub enum BarSize {
Sec5,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct BidAsk {
pub time: OffsetDateTime,
pub bid_price: f64,
pub ask_price: f64,
pub bid_size: f64,
pub ask_size: f64,
pub bid_ask_attribute: BidAskAttribute,
}
impl StreamDecoder<BidAsk> for BidAsk {
const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::TickByTick];
fn decode(context: &DecoderContext, message: &mut ResponseMessage) -> Result<Self, Error> {
match message.message_type() {
IncomingMessages::TickByTick => common::decoders::decode_bid_ask_tick(context, message),
IncomingMessages::Error => Err(Error::from(message.clone())),
_ => Err(Error::UnexpectedResponse(message.clone())),
}
}
fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<RequestMessage, Error> {
let request_id = request_id.expect("Request ID required to encode cancel realtime bars");
common::encoders::encode_cancel_tick_by_tick(request_id)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct BidAskAttribute {
pub bid_past_low: bool,
pub ask_past_high: bool,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct MidPoint {
pub time: OffsetDateTime,
pub mid_point: f64,
}
impl StreamDecoder<MidPoint> for MidPoint {
const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::TickByTick];
fn decode(context: &DecoderContext, message: &mut ResponseMessage) -> Result<Self, Error> {
match message.message_type() {
IncomingMessages::TickByTick => common::decoders::decode_mid_point_tick(context, message),
IncomingMessages::Error => Err(Error::from(message.clone())),
_ => Err(Error::UnexpectedResponse(message.clone())),
}
}
fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<RequestMessage, Error> {
let request_id = request_id.expect("Request ID required to encode cancel mid point ticks");
common::encoders::encode_cancel_tick_by_tick(request_id)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Bar {
pub date: OffsetDateTime,
pub open: f64,
pub high: f64,
pub low: f64,
pub close: f64,
pub volume: f64,
pub wap: f64,
pub count: i32,
}
impl StreamDecoder<Bar> for Bar {
const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::RealTimeBars, IncomingMessages::Error];
fn decode(context: &DecoderContext, message: &mut ResponseMessage) -> Result<Self, Error> {
match message.message_type() {
IncomingMessages::RealTimeBars => common::decoders::decode_realtime_bar(context, message),
IncomingMessages::Error => Err(Error::from(message.clone())),
_ => Err(Error::UnexpectedResponse(message.clone())),
}
}
fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<RequestMessage, Error> {
let request_id = request_id.expect("Request ID required to encode cancel realtime bars");
common::encoders::encode_cancel_realtime_bars(request_id)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Trade {
pub tick_type: String,
pub time: OffsetDateTime,
pub price: f64,
pub size: f64,
pub trade_attribute: TradeAttribute,
pub exchange: String,
pub special_conditions: String,
}
impl StreamDecoder<Trade> for Trade {
const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::TickByTick];
fn decode(context: &DecoderContext, message: &mut ResponseMessage) -> Result<Self, Error> {
match message.message_type() {
IncomingMessages::TickByTick => common::decoders::decode_trade_tick(context, message),
IncomingMessages::Error => Err(Error::from(message.clone())),
_ => Err(Error::UnexpectedResponse(message.clone())),
}
}
fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<RequestMessage, Error> {
let request_id = request_id.expect("Request ID required to encode cancel realtime bars");
common::encoders::encode_cancel_tick_by_tick(request_id)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct TradeAttribute {
pub past_limit: bool,
pub unreported: bool,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, Copy)]
pub enum WhatToShow {
Trades,
MidPoint,
Bid,
Ask,
}
impl std::fmt::Display for WhatToShow {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Trades => write!(f, "TRADES"),
Self::MidPoint => write!(f, "MIDPOINT"),
Self::Bid => write!(f, "BID"),
Self::Ask => write!(f, "ASK"),
}
}
}
impl ToField for WhatToShow {
fn to_field(&self) -> String {
self.to_string()
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum MarketDepths {
MarketDepth(MarketDepth),
MarketDepthL2(MarketDepthL2),
Notice(Notice),
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct MarketDepth {
pub position: i32,
pub operation: i32,
pub side: i32,
pub price: f64,
pub size: f64,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct MarketDepthL2 {
pub position: i32,
pub market_maker: String,
pub operation: i32,
pub side: i32,
pub price: f64,
pub size: f64,
pub smart_depth: bool,
}
impl StreamDecoder<MarketDepths> for MarketDepths {
const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[IncomingMessages::MarketDepth, IncomingMessages::MarketDepthL2, IncomingMessages::Error];
fn decode(context: &DecoderContext, message: &mut ResponseMessage) -> Result<Self, Error> {
match message.message_type() {
IncomingMessages::MarketDepth => Ok(MarketDepths::MarketDepth(common::decoders::decode_market_depth(message)?)),
IncomingMessages::MarketDepthL2 => Ok(MarketDepths::MarketDepthL2(common::decoders::decode_market_depth_l2(
context.server_version,
message,
)?)),
IncomingMessages::Error => {
let code = message.error_code();
if (2100..2200).contains(&code) {
Ok(MarketDepths::Notice(Notice::from(message)))
} else {
Err(Error::from(message.clone()))
}
}
_ => Err(Error::UnexpectedResponse(message.clone())),
}
}
fn cancel_message(server_version: i32, request_id: Option<i32>, context: Option<&DecoderContext>) -> Result<RequestMessage, Error> {
let request_id = request_id.expect("Request ID required to encode cancel realtime bars");
common::encoders::encode_cancel_market_depth(server_version, request_id, context.map(|c| c.is_smart_depth).unwrap_or(false))
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default)]
pub struct DepthMarketDataDescription {
pub exchange_name: String,
pub security_type: String,
pub listing_exchange: String,
pub service_data_type: String,
pub aggregated_group: Option<String>,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug)]
pub enum TickTypes {
Price(TickPrice),
Size(TickSize),
String(TickString),
EFP(TickEFP),
Generic(TickGeneric),
OptionComputation(OptionComputation),
SnapshotEnd,
Notice(Notice),
RequestParameters(TickRequestParameters),
PriceSize(TickPriceSize),
}
impl StreamDecoder<TickTypes> for TickTypes {
const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[
IncomingMessages::TickPrice,
IncomingMessages::TickSize,
IncomingMessages::TickString,
IncomingMessages::TickEFP,
IncomingMessages::TickGeneric,
IncomingMessages::TickOptionComputation,
IncomingMessages::TickSnapshotEnd,
IncomingMessages::Error,
IncomingMessages::TickReqParams,
];
fn decode(context: &DecoderContext, message: &mut ResponseMessage) -> Result<Self, Error> {
match message.message_type() {
IncomingMessages::TickPrice => Ok(common::decoders::decode_tick_price(context.server_version, message)?),
IncomingMessages::TickSize => Ok(TickTypes::Size(common::decoders::decode_tick_size(message)?)),
IncomingMessages::TickString => Ok(TickTypes::String(common::decoders::decode_tick_string(message)?)),
IncomingMessages::TickEFP => Ok(TickTypes::EFP(common::decoders::decode_tick_efp(message)?)),
IncomingMessages::TickGeneric => Ok(TickTypes::Generic(common::decoders::decode_tick_generic(message)?)),
IncomingMessages::TickOptionComputation => Ok(TickTypes::OptionComputation(common::decoders::decode_tick_option_computation(
context.server_version,
message,
)?)),
IncomingMessages::TickReqParams => Ok(TickTypes::RequestParameters(common::decoders::decode_tick_request_parameters(message)?)),
IncomingMessages::TickSnapshotEnd => Ok(TickTypes::SnapshotEnd),
IncomingMessages::Error => Ok(TickTypes::Notice(Notice::from(message))),
_ => Err(Error::NotImplemented),
}
}
fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<RequestMessage, Error> {
let request_id = request_id.expect("Request ID required to encode cancel realtime bars");
common::encoders::encode_cancel_market_data(request_id)
}
fn is_snapshot_end(&self) -> bool {
matches!(self, TickTypes::SnapshotEnd)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default)]
pub struct TickPrice {
pub tick_type: TickType,
pub price: f64,
pub attributes: TickAttribute,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq, Default)]
pub struct TickAttribute {
pub can_auto_execute: bool,
pub past_limit: bool,
pub pre_open: bool,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default)]
pub struct TickSize {
pub tick_type: TickType,
pub size: f64,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default)]
pub struct TickPriceSize {
pub price_tick_type: TickType,
pub price: f64,
pub attributes: TickAttribute,
pub size_tick_type: TickType,
pub size: f64,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default)]
pub struct TickString {
pub tick_type: TickType,
pub value: String,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default)]
pub struct TickEFP {
pub tick_type: TickType,
pub basis_points: f64,
pub formatted_basis_points: String,
pub implied_futures_price: f64,
pub hold_days: i32,
pub future_last_trade_date: String,
pub dividend_impact: f64,
pub dividends_to_last_trade_date: f64,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default)]
pub struct TickGeneric {
pub tick_type: TickType,
pub value: f64,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Default)]
pub struct TickRequestParameters {
pub min_tick: f64,
pub bbo_exchange: String,
pub snapshot_permissions: i32,
}
#[cfg(feature = "sync")]
pub mod blocking {
pub(crate) use super::sync::*;
}
#[cfg(all(feature = "sync", not(feature = "async")))]
pub use sync::*;
#[cfg(feature = "async")]
pub use r#async::*;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_what_to_show_display() {
assert_eq!(WhatToShow::Trades.to_string(), "TRADES");
assert_eq!(WhatToShow::MidPoint.to_string(), "MIDPOINT");
assert_eq!(WhatToShow::Bid.to_string(), "BID");
assert_eq!(WhatToShow::Ask.to_string(), "ASK");
}
}