use std::fmt::{self, Debug, Display};
use std::num::ParseIntError;
use std::str::FromStr;
use thiserror::Error;
use serde::{Deserialize, Serialize};
use time::macros::format_description;
use time::{Date, OffsetDateTime};
use crate::messages::{IncomingMessages, ResponseMessage};
use crate::subscriptions::{DecoderContext, StreamDecoder};
use crate::{Error, ToField};
pub(crate) mod common;
mod builder;
pub use builder::{HistoricalDataBuilder, HistoricalScheduleBuilder, HistoricalTicksBuilder};
#[doc(hidden)]
#[cfg(feature = "sync")]
pub mod sync;
#[doc(hidden)]
#[cfg(feature = "async")]
pub mod r#async;
#[derive(Clone, Debug, Error, PartialEq)]
pub enum HistoricalParseError {
#[error("Invalid BarSize input '{0}'")]
BarSize(String),
#[error("Invalid Duration input '{0}' {1}")]
Duration(String, String),
#[error("Invalid WhatToShow input '{0}'")]
WhatToShow(String),
#[error("ParseIntError '{0}' {1}")]
ParseIntError(String, ParseIntError),
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, PartialEq, Eq, Copy, Serialize, Deserialize)]
pub enum BarTimestamp {
Date(Date),
DateTime(OffsetDateTime),
}
impl PartialOrd for BarTimestamp {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for BarTimestamp {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(Self::Date(a), Self::Date(b)) => a.cmp(b),
(Self::DateTime(a), Self::DateTime(b)) => a.cmp(b),
(Self::Date(d), Self::DateTime(dt)) => d.midnight().assume_utc().cmp(dt),
(Self::DateTime(dt), Self::Date(d)) => dt.cmp(&d.midnight().assume_utc()),
}
}
}
impl Display for BarTimestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Date(d) => {
let fmt = format_description!("[year][month][day]");
write!(f, "{}", d.format(&fmt).unwrap_or_default())
}
Self::DateTime(dt) => write!(f, "{}", dt.unix_timestamp()),
}
}
}
impl FromStr for BarTimestamp {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s.len() == 8 && s.bytes().all(|b| b.is_ascii_digit()) {
let fmt = format_description!("[year][month][day]");
let d = Date::parse(s, fmt).map_err(|e| Error::parse_field(s, e.to_string()))?;
return Ok(Self::Date(d));
}
let secs: i64 = s.parse().map_err(|e: std::num::ParseIntError| Error::parse_field(s, e.to_string()))?;
OffsetDateTime::from_unix_timestamp(secs)
.map(Self::DateTime)
.map_err(|e| Error::parse_field(s, e.to_string()))
}
}
impl BarTimestamp {
pub fn is_date(&self) -> bool {
matches!(self, Self::Date(_))
}
pub fn is_date_time(&self) -> bool {
matches!(self, Self::DateTime(_))
}
}
impl From<Date> for BarTimestamp {
fn from(d: Date) -> Self {
Self::Date(d)
}
}
impl From<OffsetDateTime> for BarTimestamp {
fn from(dt: OffsetDateTime) -> Self {
Self::DateTime(dt)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, PartialEq, Copy, Serialize, Deserialize)]
pub struct Bar {
pub date: BarTimestamp,
pub open: f64,
pub high: f64,
pub low: f64,
pub close: f64,
pub volume: f64,
pub wap: f64,
pub count: i32,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, Copy, PartialEq, Serialize, Deserialize)]
pub enum BarSize {
Sec,
Sec5,
Sec10,
Sec15,
Sec30,
Min,
Min2,
Min3,
Min5,
Min10,
Min15,
Min20,
Min30,
Hour,
Hour2,
Hour3,
Hour4,
Hour8,
Day,
Week,
Month,
}
impl Display for BarSize {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Sec => write!(f, "1 secs"),
Self::Sec5 => write!(f, "5 secs"),
Self::Sec10 => write!(f, "10 secs"),
Self::Sec15 => write!(f, "15 secs"),
Self::Sec30 => write!(f, "30 secs"),
Self::Min => write!(f, "1 min"),
Self::Min2 => write!(f, "2 mins"),
Self::Min3 => write!(f, "3 mins"),
Self::Min5 => write!(f, "5 mins"),
Self::Min10 => write!(f, "10 mins"),
Self::Min15 => write!(f, "15 mins"),
Self::Min20 => write!(f, "20 mins"),
Self::Min30 => write!(f, "30 mins"),
Self::Hour => write!(f, "1 hour"),
Self::Hour2 => write!(f, "2 hours"),
Self::Hour3 => write!(f, "3 hours"),
Self::Hour4 => write!(f, "4 hours"),
Self::Hour8 => write!(f, "8 hours"),
Self::Day => write!(f, "1 day"),
Self::Week => write!(f, "1 week"),
Self::Month => write!(f, "1 month"),
}
}
}
impl FromStr for BarSize {
type Err = HistoricalParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"SEC" => Ok(Self::Sec),
"SEC5" => Ok(Self::Sec5),
"SEC10" => Ok(Self::Sec10),
"SEC15" => Ok(Self::Sec15),
"SEC30" => Ok(Self::Sec30),
"MIN" => Ok(Self::Min),
"MIN2" => Ok(Self::Min2),
"MIN3" => Ok(Self::Min3),
"MIN5" => Ok(Self::Min5),
"MIN10" => Ok(Self::Min10),
"MIN15" => Ok(Self::Min15),
"MIN20" => Ok(Self::Min20),
"MIN30" => Ok(Self::Min30),
"HOUR" => Ok(Self::Hour),
"HOUR2" => Ok(Self::Hour2),
"HOUR3" => Ok(Self::Hour3),
"HOUR4" => Ok(Self::Hour4),
"HOUR8" => Ok(Self::Hour8),
"DAY" => Ok(Self::Day),
"WEEK" => Ok(Self::Week),
"MONTH" => Ok(Self::Month),
_ => Err(HistoricalParseError::BarSize(s.to_string())),
}
}
}
impl From<&str> for BarSize {
fn from(val: &str) -> Self {
Self::from_str(val).unwrap()
}
}
impl From<String> for BarSize {
fn from(val: String) -> Self {
Self::from(val.as_str())
}
}
impl ToField for BarSize {
fn to_field(&self) -> String {
self.to_string()
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, Copy, PartialEq, Serialize, Deserialize)]
pub struct Duration {
value: i32,
unit: char,
}
impl Duration {
pub const SECOND: Self = Self::seconds(1);
pub const DAY: Self = Self::days(1);
pub const WEEK: Self = Self::weeks(1);
pub const MONTH: Self = Self::months(1);
pub const YEAR: Self = Self::years(1);
pub const fn seconds(seconds: i32) -> Self {
Self { value: seconds, unit: 'S' }
}
pub const fn days(days: i32) -> Self {
Self { value: days, unit: 'D' }
}
pub const fn weeks(weeks: i32) -> Self {
Self { value: weeks, unit: 'W' }
}
pub const fn months(months: i32) -> Self {
Self { value: months, unit: 'M' }
}
pub const fn years(years: i32) -> Self {
Self { value: years, unit: 'Y' }
}
}
impl Display for Duration {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{} {}", self.value, self.unit)
}
}
impl FromStr for Duration {
type Err = HistoricalParseError;
fn from_str(val: &str) -> Result<Self, HistoricalParseError> {
if val.is_empty() {
return Err(HistoricalParseError::Duration(val.to_string(), "Empty string".to_string()));
}
match val.to_uppercase().rsplit_once(' ') {
Some((value_part, unit_part)) => {
let value = value_part
.parse::<i32>()
.map_err(|e| HistoricalParseError::ParseIntError(value_part.to_string(), e))?;
match unit_part {
"S" => Ok(Self::seconds(value)),
"D" => Ok(Self::days(value)),
"W" => Ok(Self::weeks(value)),
"M" => Ok(Self::months(value)),
"Y" => Ok(Self::years(value)),
_ => Err(HistoricalParseError::Duration(val.to_string(), "Unsupported unit".to_string())),
}
}
None => Err(HistoricalParseError::Duration(val.to_string(), "Missing delimiter".to_string())),
}
}
}
impl From<&str> for Duration {
fn from(val: &str) -> Self {
Self::from_str(val).unwrap()
}
}
impl From<String> for Duration {
fn from(val: String) -> Self {
Self::from(val.as_str())
}
}
impl ToField for Duration {
fn to_field(&self) -> String {
self.to_string()
}
}
pub trait ToDuration {
fn seconds(&self) -> Duration;
fn days(&self) -> Duration;
fn weeks(&self) -> Duration;
fn months(&self) -> Duration;
fn years(&self) -> Duration;
}
impl ToDuration for i32 {
fn seconds(&self) -> Duration {
Duration::seconds(*self)
}
fn days(&self) -> Duration {
Duration::days(*self)
}
fn weeks(&self) -> Duration {
Duration::weeks(*self)
}
fn months(&self) -> Duration {
Duration::months(*self)
}
fn years(&self) -> Duration {
Duration::years(*self)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub struct HistogramEntry {
pub price: f64,
pub size: i32,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct HistoricalData {
pub start: OffsetDateTime,
pub end: OffsetDateTime,
pub bars: Vec<Bar>,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum HistoricalBarUpdate {
Historical(HistoricalData),
Update(Bar),
End {
start: OffsetDateTime,
end: OffsetDateTime,
},
}
impl StreamDecoder<HistoricalBarUpdate> for HistoricalBarUpdate {
const RESPONSE_MESSAGE_IDS: &[IncomingMessages] = &[
IncomingMessages::HistoricalData,
IncomingMessages::HistoricalDataUpdate,
IncomingMessages::HistoricalDataEnd,
IncomingMessages::Error,
];
fn decode(_context: &DecoderContext, message: &mut ResponseMessage) -> Result<Self, Error> {
match message.message_type() {
IncomingMessages::HistoricalData => Ok(Self::Historical(common::decoders::decode_historical_data(message)?)),
IncomingMessages::HistoricalDataUpdate => Ok(Self::Update(common::decoders::decode_historical_data_update(message)?)),
IncomingMessages::HistoricalDataEnd => {
let (start, end) = common::decoders::decode_historical_data_end(message)?;
Ok(Self::End { start, end })
}
IncomingMessages::Error => Err(Error::from(message.clone())),
_ => Err(Error::unexpected_response(message)),
}
}
fn cancel_message(_server_version: i32, request_id: Option<i32>, _context: Option<&DecoderContext>) -> Result<Vec<u8>, Error> {
let request_id = request_id.expect("Request ID required to encode cancel historical data");
common::encoders::encode_cancel_historical_data(request_id)
}
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Schedule {
pub start: OffsetDateTime,
pub end: OffsetDateTime,
pub time_zone: String,
pub sessions: Vec<Session>,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub struct Session {
pub reference: Date,
pub start: OffsetDateTime,
pub end: OffsetDateTime,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub struct TickMidpoint {
pub timestamp: OffsetDateTime,
pub price: f64,
pub size: i32,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub struct TickBidAsk {
pub timestamp: OffsetDateTime,
pub tick_attribute_bid_ask: TickAttributeBidAsk,
pub price_bid: f64,
pub price_ask: f64,
pub size_bid: i32,
pub size_ask: i32,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub struct TickAttributeBidAsk {
pub bid_past_low: bool,
pub ask_past_high: bool,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct TickLast {
pub timestamp: OffsetDateTime,
pub tick_attribute_last: TickAttributeLast,
pub price: f64,
pub size: i32,
pub exchange: String,
pub special_conditions: String,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy)]
pub struct TickAttributeLast {
pub past_limit: bool,
pub unreported: bool,
}
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Clone, Debug, Copy, PartialEq, Serialize, Deserialize)]
pub enum WhatToShow {
Trades,
MidPoint,
Bid,
Ask,
BidAsk,
HistoricalVolatility,
OptionImpliedVolatility,
FeeRate,
Schedule,
AdjustedLast,
}
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"),
Self::BidAsk => write!(f, "BID_ASK"),
Self::HistoricalVolatility => write!(f, "HISTORICAL_VOLATILITY"),
Self::OptionImpliedVolatility => write!(f, "OPTION_IMPLIED_VOLATILITY"),
Self::FeeRate => write!(f, "FEE_RATE"),
Self::Schedule => write!(f, "SCHEDULE"),
Self::AdjustedLast => write!(f, "ADJUSTED_LAST"),
}
}
}
#[derive(Debug)]
pub struct WhatToShowParseError;
impl fmt::Display for WhatToShowParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Invalid WhatToShow string")
}
}
impl FromStr for WhatToShow {
type Err = HistoricalParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"TRADES" => Ok(Self::Trades),
"MIDPOINT" => Ok(Self::MidPoint),
"BID" => Ok(Self::Bid),
"ASK" => Ok(Self::Ask),
"BID_ASK" => Ok(Self::BidAsk),
"HISTORICAL_VOLATILITY" => Ok(Self::HistoricalVolatility),
"OPTION_IMPLIED_VOLATILITY" => Ok(Self::OptionImpliedVolatility),
"FEE_RATE" => Ok(Self::FeeRate),
"SCHEDULE" => Ok(Self::Schedule),
"ADJUSTED_LAST" => Ok(Self::AdjustedLast),
_ => Err(HistoricalParseError::WhatToShow(s.to_string())),
}
}
}
impl From<&str> for WhatToShow {
fn from(val: &str) -> Self {
Self::from_str(val).unwrap()
}
}
impl From<String> for WhatToShow {
fn from(val: String) -> Self {
Self::from(val.as_str())
}
}
impl ToField for WhatToShow {
fn to_field(&self) -> String {
self.to_string()
}
}
impl ToField for Option<WhatToShow> {
fn to_field(&self) -> String {
match self {
Some(what_to_show) => what_to_show.to_string(),
None => "".into(),
}
}
}
#[cfg(all(feature = "sync", not(feature = "async")))]
#[allow(unused_imports)]
pub use sync::*;
#[cfg(feature = "async")]
pub use r#async::TickSubscription;
#[allow(private_interfaces)]
pub trait TickDecoder<T> {
const MESSAGE_TYPE: IncomingMessages;
fn decode(message: &ResponseMessage) -> Result<(Vec<T>, bool), Error>;
}
#[allow(private_interfaces)]
impl TickDecoder<TickBidAsk> for TickBidAsk {
const MESSAGE_TYPE: IncomingMessages = IncomingMessages::HistoricalTickBidAsk;
fn decode(message: &ResponseMessage) -> Result<(Vec<TickBidAsk>, bool), Error> {
common::decoders::decode_historical_ticks_bid_ask(message)
}
}
#[allow(private_interfaces)]
impl TickDecoder<TickLast> for TickLast {
const MESSAGE_TYPE: IncomingMessages = IncomingMessages::HistoricalTickLast;
fn decode(message: &ResponseMessage) -> Result<(Vec<TickLast>, bool), Error> {
common::decoders::decode_historical_ticks_last(message)
}
}
#[allow(private_interfaces)]
impl TickDecoder<TickMidpoint> for TickMidpoint {
const MESSAGE_TYPE: IncomingMessages = IncomingMessages::HistoricalTick;
fn decode(message: &ResponseMessage) -> Result<(Vec<TickMidpoint>, bool), Error> {
common::decoders::decode_historical_ticks_mid_point(message)
}
}
#[cfg(all(feature = "sync", not(feature = "async")))]
pub use sync::{TickSubscription, TickSubscriptionIter, TickSubscriptionOwnedIter, TickSubscriptionTimeoutIter, TickSubscriptionTryIter};
#[cfg(test)]
#[path = "mod_tests.rs"]
mod tests;