use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::fmt;
use std::os::raw::{c_int, c_uint};
use strum_macros::EnumString;
use thiserror::Error;
use widestring::WideCString;
pub use libdxfeed_sys::*;
pub const DXF_ET_TRADE: c_int = 1 << dx_event_id_dx_eid_trade;
pub const DXF_ET_QUOTE: c_int = 1 << dx_event_id_dx_eid_quote;
pub const DXF_ET_SUMMARY: c_int = 1 << dx_event_id_dx_eid_summary;
pub const DXF_ET_PROFILE: c_int = 1 << dx_event_id_dx_eid_profile;
pub const DXF_ET_ORDER: c_int = 1 << dx_event_id_dx_eid_order;
pub const DXF_ET_TIME_AND_SALE: c_int = 1 << dx_event_id_dx_eid_time_and_sale;
pub const DXF_ET_CANDLE: c_int = 1 << dx_event_id_dx_eid_candle;
pub const DXF_ET_TRADE_ETH: c_int = 1 << dx_event_id_dx_eid_trade_eth;
pub const DXF_ET_SPREAD_ORDER: c_int = 1 << dx_event_id_dx_eid_spread_order;
pub const DXF_ET_GREEKS: c_int = 1 << dx_event_id_dx_eid_greeks;
pub const DXF_ET_THEO_PRICE: c_int = 1 << dx_event_id_dx_eid_theo_price;
pub const DXF_ET_UNDERLYING: c_int = 1 << dx_event_id_dx_eid_underlying;
pub const DXF_ET_SERIES: c_int = 1 << dx_event_id_dx_eid_series;
pub const DXF_ET_CONFIGURATION: c_int = 1 << dx_event_id_dx_eid_configuration;
pub const DXF_ET_UNUSED: c_uint = !((1 << dx_event_id_dx_eid_count) - 1);
#[derive(
Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Debug, Hash, EnumString,
)]
pub enum EventType {
Trade = DXF_ET_TRADE as isize,
Quote = DXF_ET_QUOTE as isize,
Summary = DXF_ET_SUMMARY as isize,
Profile = DXF_ET_PROFILE as isize,
Order = DXF_ET_ORDER as isize,
TimeAndSale = DXF_ET_TIME_AND_SALE as isize,
Candle = DXF_ET_CANDLE as isize,
TradeETH = DXF_ET_TRADE_ETH as isize,
SpreadOrder = DXF_ET_SPREAD_ORDER as isize,
Greeks = DXF_ET_GREEKS as isize,
TheoPrice = DXF_ET_THEO_PRICE as isize,
Underlying = DXF_ET_UNDERLYING as isize,
Series = DXF_ET_SERIES as isize,
Configuration = DXF_ET_CONFIGURATION as isize,
}
impl TryFrom<c_int> for EventType {
type Error = Error;
fn try_from(value: c_int) -> Result<Self, Self::Error> {
match value {
DXF_ET_TRADE => Ok(EventType::Trade),
DXF_ET_QUOTE => Ok(EventType::Quote),
DXF_ET_SUMMARY => Ok(EventType::Summary),
DXF_ET_PROFILE => Ok(EventType::Profile),
DXF_ET_ORDER => Ok(EventType::Order),
DXF_ET_TIME_AND_SALE => Ok(EventType::TimeAndSale),
DXF_ET_CANDLE => Ok(EventType::Candle),
DXF_ET_TRADE_ETH => Ok(EventType::TradeETH),
DXF_ET_SPREAD_ORDER => Ok(EventType::SpreadOrder),
DXF_ET_GREEKS => Ok(EventType::Greeks),
DXF_ET_THEO_PRICE => Ok(EventType::TheoPrice),
DXF_ET_UNDERLYING => Ok(EventType::Underlying),
DXF_ET_SERIES => Ok(EventType::Series),
DXF_ET_CONFIGURATION => Ok(EventType::Configuration),
_ => Err(Error::Invalid(value)),
}
}
}
impl<T: AsRef<EventData>> From<T> for EventType {
fn from(event: T) -> Self {
match event.as_ref() {
EventData::Trade(_) => EventType::Trade,
EventData::Quote(_) => EventType::Quote,
EventData::Summary(_) => EventType::Summary,
EventData::Profile(_) => EventType::Profile,
EventData::Order(_) => EventType::Order,
EventData::TimeAndSale(_) => EventType::TimeAndSale,
EventData::Candle(_) => EventType::Candle,
EventData::TradeETH(_) => EventType::TradeETH,
EventData::SpreadOrder(_) => EventType::SpreadOrder,
EventData::Greeks(_) => EventType::Greeks,
EventData::TheoPrice(_) => EventType::TheoPrice,
EventData::Underlying(_) => EventType::Underlying,
EventData::Series(_) => EventType::Series,
EventData::Configuration(_) => EventType::Configuration,
}
}
}
impl fmt::Display for EventType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl EventType {
pub fn to_string(value: c_int) -> String {
Self::try_from(value).map_or_else(
|_err| format!("<Unknown>({})", value),
|event_type| format!("{}", event_type),
)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ProfileEventData {
pub beta: f64,
pub eps: f64,
pub div_freq: f64,
pub exd_div_amount: f64,
pub exd_div_date: i32,
pub high_52_week_price: f64,
pub low_52_week_price: f64,
pub shares: f64,
pub free_float: f64,
pub high_limit_price: f64,
pub low_limit_price: f64,
pub halt_start_time: i64,
pub halt_end_time: i64,
pub raw_flags: i32,
pub description: String,
pub status_reason: String,
pub trading_status: u32,
pub ssr: u32,
}
impl From<&dxf_profile_t> for ProfileEventData {
fn from(c_profile: &dxf_profile_t) -> Self {
let description = unsafe {
WideCString::from_ptr_str(c_profile.description as *const _).to_string_lossy()
};
let status_reason = unsafe {
WideCString::from_ptr_str(c_profile.status_reason as *const _).to_string_lossy()
};
Self {
beta: c_profile.beta as f64,
eps: c_profile.eps as f64,
div_freq: c_profile.div_freq as f64,
exd_div_amount: c_profile.exd_div_amount as f64,
exd_div_date: c_profile.exd_div_date as i32,
high_52_week_price: c_profile.high_52_week_price as f64,
low_52_week_price: c_profile.low_52_week_price as f64,
shares: c_profile.shares as f64,
free_float: c_profile.free_float as f64,
high_limit_price: c_profile.high_limit_price as f64,
low_limit_price: c_profile.low_limit_price as f64,
halt_start_time: c_profile.halt_start_time as i64,
halt_end_time: c_profile.halt_end_time as i64,
raw_flags: c_profile.raw_flags as i32,
description,
status_reason,
trading_status: c_profile.trading_status as u32,
ssr: c_profile.ssr as u32,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OrderEventData {
pub source: [dxf_char_t; 17usize],
pub event_flags: dxf_event_flags_t,
pub index: dxf_long_t,
pub time: dxf_long_t,
pub sequence: dxf_int_t,
pub time_nanos: dxf_int_t,
pub action: dxf_order_action_t,
pub action_time: dxf_long_t,
pub order_id: dxf_long_t,
pub aux_order_id: dxf_long_t,
pub price: dxf_double_t,
pub size: dxf_double_t,
pub executed_size: dxf_double_t,
pub count: dxf_double_t,
pub trade_id: dxf_long_t,
pub trade_price: dxf_double_t,
pub trade_size: dxf_double_t,
pub exchange_code: dxf_char_t,
pub side: dxf_order_side_t,
pub scope: dxf_order_scope_t,
pub mm_or_spread: String,
}
impl From<&dxf_order_t> for OrderEventData {
fn from(c_order: &dxf_order_t) -> Self {
let mm_or_spread = unsafe {
WideCString::from_ptr_str(c_order.__bindgen_anon_1.market_maker as *const _)
.to_string_lossy()
};
Self {
source: c_order.source,
event_flags: c_order.event_flags,
index: c_order.index,
time: c_order.time,
sequence: c_order.sequence,
time_nanos: c_order.time_nanos,
action: c_order.action,
action_time: c_order.action_time,
order_id: c_order.order_id,
aux_order_id: c_order.aux_order_id,
price: c_order.price,
size: c_order.size,
executed_size: c_order.executed_size,
count: c_order.count,
trade_id: c_order.trade_id,
trade_price: c_order.trade_price,
trade_size: c_order.trade_size,
exchange_code: c_order.exchange_code,
side: c_order.side,
scope: c_order.scope,
mm_or_spread,
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct TimeAndSaleData {
pub event_flags: dxf_event_flags_t,
pub index: dxf_long_t,
pub time: dxf_long_t,
pub exchange_code: dxf_char_t,
pub price: dxf_double_t,
pub size: dxf_double_t,
pub bid_price: dxf_double_t,
pub ask_price: dxf_double_t,
pub exchange_sale_conditions: String,
pub raw_flags: dxf_int_t,
pub buyer: String,
pub seller: String,
pub side: dxf_order_side_t,
pub kind: dxf_tns_type_t,
pub is_valid_tick: bool,
pub is_eth_trade: bool,
pub trade_through_exempt: dxf_char_t,
pub is_spread_leg: bool,
pub scope: dxf_order_scope_t,
}
impl From<&dxf_time_and_sale_t> for TimeAndSaleData {
fn from(c_time_and_sale: &dxf_time_and_sale_t) -> Self {
let exchange_sale_conditions = unsafe {
WideCString::from_ptr_str(c_time_and_sale.exchange_sale_conditions as *const _)
.to_string_lossy()
};
let buyer = unsafe {
WideCString::from_ptr_str(c_time_and_sale.buyer as *const _).to_string_lossy()
};
let seller = unsafe {
WideCString::from_ptr_str(c_time_and_sale.seller as *const _).to_string_lossy()
};
Self {
event_flags: c_time_and_sale.event_flags,
index: c_time_and_sale.index,
time: c_time_and_sale.time,
exchange_code: c_time_and_sale.exchange_code,
price: c_time_and_sale.price,
size: c_time_and_sale.size,
bid_price: c_time_and_sale.bid_price,
ask_price: c_time_and_sale.ask_price,
exchange_sale_conditions,
raw_flags: c_time_and_sale.raw_flags,
buyer,
seller,
side: c_time_and_sale.side,
kind: c_time_and_sale.type_,
is_valid_tick: c_time_and_sale.is_valid_tick > 0,
is_eth_trade: c_time_and_sale.is_eth_trade > 0,
trade_through_exempt: c_time_and_sale.trade_through_exempt,
is_spread_leg: c_time_and_sale.is_spread_leg > 0,
scope: c_time_and_sale.scope,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SpreadOrderData {
pub index: dxf_int_t,
pub time: dxf_int_t,
pub time_nanos: dxf_int_t,
pub sequence: dxf_int_t,
pub action_time: dxf_long_t,
pub order_id: dxf_long_t,
pub aux_order_id: dxf_long_t,
pub price: dxf_double_t,
pub size: dxf_double_t,
pub executed_size: dxf_double_t,
pub count: dxf_double_t,
pub flags: dxf_int_t,
pub trade_id: dxf_long_t,
pub trade_price: dxf_double_t,
pub trade_size: dxf_double_t,
pub spread_symbol: String,
}
impl From<&dx_spread_order_t> for SpreadOrderData {
fn from(c_spread_order: &dx_spread_order_t) -> Self {
let spread_symbol = unsafe {
WideCString::from_ptr_str(c_spread_order.spread_symbol as *const _).to_string_lossy()
};
Self {
index: c_spread_order.index,
time: c_spread_order.time,
time_nanos: c_spread_order.time_nanos,
sequence: c_spread_order.sequence,
action_time: c_spread_order.action_time,
order_id: c_spread_order.order_id,
aux_order_id: c_spread_order.aux_order_id,
price: c_spread_order.price,
size: c_spread_order.size,
executed_size: c_spread_order.executed_size,
count: c_spread_order.count,
flags: c_spread_order.flags,
trade_id: c_spread_order.trade_id,
trade_price: c_spread_order.trade_price,
trade_size: c_spread_order.trade_size,
spread_symbol,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigurationData {
pub version: dxf_int_t,
pub object: String,
}
impl From<&dxf_configuration_t> for ConfigurationData {
fn from(c_config: &dxf_configuration_t) -> Self {
let object =
unsafe { WideCString::from_ptr_str(c_config.object as *const _).to_string_lossy() };
Self {
version: c_config.version,
object,
}
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("Invalid event_type: `{0}`")]
Invalid(c_int),
#[cfg(unix)]
#[error("Converting from WideCString")]
UtfError(#[from] widestring::error::Utf32Error),
#[cfg(windows)]
#[error("Converting from WideCString")]
UtfError(#[from] widestring::error::Utf16Error),
#[error("Unknown error")]
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EventData {
Trade(dxf_trade_t),
Quote(dxf_quote_t),
Summary(dxf_summary_t),
Profile(ProfileEventData),
Order(OrderEventData),
TimeAndSale(TimeAndSaleData),
Candle(dxf_candle_t),
TradeETH(dxf_trade_eth_t),
SpreadOrder(SpreadOrderData),
Greeks(dxf_greeks_t),
TheoPrice(dxf_theo_price_t),
Underlying(dxf_underlying_t),
Series(dxf_series_t),
Configuration(ConfigurationData),
}
impl EventData {
pub fn get_event_type(&self) -> c_int {
match self {
Self::Trade(_) => DXF_ET_TRADE,
Self::Quote(_) => DXF_ET_QUOTE,
Self::Summary(_) => DXF_ET_SUMMARY,
Self::Profile(_) => DXF_ET_PROFILE,
Self::Order(_) => DXF_ET_ORDER,
Self::TimeAndSale(_) => DXF_ET_TIME_AND_SALE,
Self::Candle(_) => DXF_ET_CANDLE,
Self::TradeETH(_) => DXF_ET_TRADE_ETH,
Self::SpreadOrder(_) => DXF_ET_SPREAD_ORDER,
Self::Greeks(_) => DXF_ET_GREEKS,
Self::TheoPrice(_) => DXF_ET_THEO_PRICE,
Self::Underlying(_) => DXF_ET_UNDERLYING,
Self::Series(_) => DXF_ET_SERIES,
Self::Configuration(_) => DXF_ET_CONFIGURATION,
}
}
}
impl EventData {
pub fn try_get_event_data(
event_type: c_int,
data: *const dxf_event_data_t,
) -> Result<EventData, Error> {
match event_type {
DXF_ET_TRADE => {
let c_trade: &dxf_trade_t = unsafe { &*(data as *mut dxf_trade_t) };
Ok(EventData::Trade(c_trade.clone()))
}
DXF_ET_QUOTE => {
let c_quote: &dxf_quote_t = unsafe { &*(data as *mut dxf_quote_t) };
Ok(EventData::Quote(c_quote.clone()))
}
DXF_ET_SUMMARY => {
let c_summary: &dxf_summary_t = unsafe { &*(data as *mut dxf_summary_t) };
Ok(EventData::Summary(c_summary.clone()))
}
DXF_ET_PROFILE => {
let c_profile: &dxf_profile_t = unsafe { &*(data as *mut dxf_profile_t) };
Ok(EventData::Profile(ProfileEventData::from(c_profile)))
}
DXF_ET_ORDER => {
let c_order: &dxf_order_t = unsafe { &*(data as *mut dxf_order_t) };
Ok(EventData::Order(OrderEventData::from(c_order)))
}
DXF_ET_TIME_AND_SALE => {
let c_time_and_sale: &dxf_time_and_sale_t =
unsafe { &*(data as *mut dxf_time_and_sale_t) };
Ok(EventData::TimeAndSale(TimeAndSaleData::from(
c_time_and_sale,
)))
}
DXF_ET_CANDLE => {
let c_candle: &dxf_candle_t = unsafe { &*(data as *mut dxf_candle_t) };
Ok(EventData::Candle(c_candle.clone()))
}
DXF_ET_TRADE_ETH => {
let c_trade_eth: &dxf_trade_eth_t = unsafe { &*(data as *mut dxf_trade_eth_t) };
Ok(EventData::TradeETH(c_trade_eth.clone()))
}
DXF_ET_SPREAD_ORDER => {
let c_spread_order: &dx_spread_order = unsafe { &*(data as *mut dx_spread_order) };
Ok(EventData::SpreadOrder(SpreadOrderData::from(
c_spread_order,
)))
}
DXF_ET_GREEKS => {
let c_greeks: &dxf_greeks_t = unsafe { &*(data as *mut dxf_greeks_t) };
Ok(EventData::Greeks(c_greeks.clone()))
}
DXF_ET_THEO_PRICE => {
let c_theo: &dxf_theo_price_t = unsafe { &*(data as *mut dxf_theo_price_t) };
Ok(EventData::TheoPrice(c_theo.clone()))
}
DXF_ET_UNDERLYING => {
let c_underlying: &dxf_underlying_t = unsafe { &*(data as *mut dxf_underlying_t) };
Ok(EventData::Underlying(c_underlying.clone()))
}
DXF_ET_SERIES => {
let c_series: &dxf_series_t = unsafe { &*(data as *mut dxf_series_t) };
Ok(EventData::Series(c_series.clone()))
}
DXF_ET_CONFIGURATION => {
let c_configuration: &dxf_configuration_t =
unsafe { &*(data as *mut dxf_configuration_t) };
Ok(EventData::Configuration(ConfigurationData::from(
c_configuration,
)))
}
_ => Err(Error::Invalid(event_type)),
}
}
}
unsafe impl Send for EventData {}
unsafe impl Sync for EventData {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Event {
pub sym: String,
pub data: EventData,
}
unsafe impl Send for Event {}
unsafe impl Sync for Event {}
impl Event {
pub fn new(sym: String, data: EventData) -> Self {
Event { sym, data }
}
pub fn try_from_c(
event_type: c_int,
raw_sym: dxf_const_string_t,
data: *const dxf_event_data_t,
) -> Result<Self, Error> {
let c_sym = unsafe { WideCString::from_ptr_str(raw_sym as *const _) };
let sym = c_sym.to_string()?;
let event_data = EventData::try_get_event_data(event_type, data)?;
Ok(Event::new(sym, event_data))
}
}
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn event_type_from_str() {
let name2expected: [(&str, EventType); 14] = [
("Trade", EventType::Trade),
("Quote", EventType::Quote),
("Summary", EventType::Summary),
("Profile", EventType::Profile),
("Order", EventType::Order),
("TimeAndSale", EventType::TimeAndSale),
("Candle", EventType::Candle),
("TradeETH", EventType::TradeETH),
("SpreadOrder", EventType::SpreadOrder),
("Greeks", EventType::Greeks),
("TheoPrice", EventType::TheoPrice),
("Underlying", EventType::Underlying),
("Series", EventType::Series),
("Configuration", EventType::Configuration),
];
for (name, expected) in name2expected {
let result = EventType::from_str(name);
assert_eq!(result, Ok(expected));
}
}
}