mod account_tx;
mod fill_cancel_tx;
mod misc_tx;
mod order_tx;
pub use account_tx::*;
pub use fill_cancel_tx::*;
pub use misc_tx::*;
pub use order_tx::*;
use serde::{Deserialize, Serialize};
use crate::models::{AccountId, DateTime, RequestId, TransactionId};
macro_rules! for_each_transaction {
($tx:expr, $inner:ident => $body:expr, $unknown:expr) => {
match $tx {
Transaction::Create($inner) => $body,
Transaction::Close($inner) => $body,
Transaction::Reopen($inner) => $body,
Transaction::ClientConfigure($inner) => $body,
Transaction::ClientConfigureReject($inner) => $body,
Transaction::TransferFunds($inner) => $body,
Transaction::TransferFundsReject($inner) => $body,
Transaction::MarketOrder($inner) => $body,
Transaction::MarketOrderReject($inner) => $body,
Transaction::FixedPriceOrder($inner) => $body,
Transaction::LimitOrder($inner) => $body,
Transaction::LimitOrderReject($inner) => $body,
Transaction::StopOrder($inner) => $body,
Transaction::StopOrderReject($inner) => $body,
Transaction::MarketIfTouchedOrder($inner) => $body,
Transaction::MarketIfTouchedOrderReject($inner) => $body,
Transaction::TakeProfitOrder($inner) => $body,
Transaction::TakeProfitOrderReject($inner) => $body,
Transaction::StopLossOrder($inner) => $body,
Transaction::StopLossOrderReject($inner) => $body,
Transaction::TrailingStopLossOrder($inner) => $body,
Transaction::TrailingStopLossOrderReject($inner) => $body,
Transaction::OrderFill($inner) => $body,
Transaction::OrderCancel($inner) => $body,
Transaction::OrderCancelReject($inner) => $body,
Transaction::OrderClientExtensionsModify($inner) => $body,
Transaction::OrderClientExtensionsModifyReject($inner) => $body,
Transaction::TradeClientExtensionsModify($inner) => $body,
Transaction::TradeClientExtensionsModifyReject($inner) => $body,
Transaction::MarginCallEnter($inner) => $body,
Transaction::MarginCallExtend($inner) => $body,
Transaction::MarginCallExit($inner) => $body,
Transaction::DelayedTradeClosure($inner) => $body,
Transaction::DailyFinancing($inner) => $body,
Transaction::ResetResettablePL($inner) => $body,
Transaction::DividendAdjustment($inner) => $body,
Transaction::Unknown(_) => $unknown,
}
};
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)] pub enum Transaction {
#[serde(rename = "CREATE")]
Create(CreateTransaction),
#[serde(rename = "CLOSE")]
Close(CloseTransaction),
#[serde(rename = "REOPEN")]
Reopen(ReopenTransaction),
#[serde(rename = "CLIENT_CONFIGURE")]
ClientConfigure(ClientConfigureTransaction),
#[serde(rename = "CLIENT_CONFIGURE_REJECT")]
ClientConfigureReject(ClientConfigureRejectTransaction),
#[serde(rename = "TRANSFER_FUNDS")]
TransferFunds(TransferFundsTransaction),
#[serde(rename = "TRANSFER_FUNDS_REJECT")]
TransferFundsReject(TransferFundsRejectTransaction),
#[serde(rename = "MARKET_ORDER")]
MarketOrder(MarketOrderTransaction),
#[serde(rename = "MARKET_ORDER_REJECT")]
MarketOrderReject(MarketOrderRejectTransaction),
#[serde(rename = "FIXED_PRICE_ORDER")]
FixedPriceOrder(FixedPriceOrderTransaction),
#[serde(rename = "LIMIT_ORDER")]
LimitOrder(LimitOrderTransaction),
#[serde(rename = "LIMIT_ORDER_REJECT")]
LimitOrderReject(LimitOrderRejectTransaction),
#[serde(rename = "STOP_ORDER")]
StopOrder(StopOrderTransaction),
#[serde(rename = "STOP_ORDER_REJECT")]
StopOrderReject(StopOrderRejectTransaction),
#[serde(rename = "MARKET_IF_TOUCHED_ORDER")]
MarketIfTouchedOrder(MarketIfTouchedOrderTransaction),
#[serde(rename = "MARKET_IF_TOUCHED_ORDER_REJECT")]
MarketIfTouchedOrderReject(MarketIfTouchedOrderRejectTransaction),
#[serde(rename = "TAKE_PROFIT_ORDER")]
TakeProfitOrder(TakeProfitOrderTransaction),
#[serde(rename = "TAKE_PROFIT_ORDER_REJECT")]
TakeProfitOrderReject(TakeProfitOrderRejectTransaction),
#[serde(rename = "STOP_LOSS_ORDER")]
StopLossOrder(StopLossOrderTransaction),
#[serde(rename = "STOP_LOSS_ORDER_REJECT")]
StopLossOrderReject(StopLossOrderRejectTransaction),
#[serde(rename = "TRAILING_STOP_LOSS_ORDER")]
TrailingStopLossOrder(TrailingStopLossOrderTransaction),
#[serde(rename = "TRAILING_STOP_LOSS_ORDER_REJECT")]
TrailingStopLossOrderReject(TrailingStopLossOrderRejectTransaction),
#[serde(rename = "ORDER_FILL")]
OrderFill(OrderFillTransaction),
#[serde(rename = "ORDER_CANCEL")]
OrderCancel(OrderCancelTransaction),
#[serde(rename = "ORDER_CANCEL_REJECT")]
OrderCancelReject(OrderCancelRejectTransaction),
#[serde(rename = "ORDER_CLIENT_EXTENSIONS_MODIFY")]
OrderClientExtensionsModify(OrderClientExtensionsModifyTransaction),
#[serde(rename = "ORDER_CLIENT_EXTENSIONS_MODIFY_REJECT")]
OrderClientExtensionsModifyReject(OrderClientExtensionsModifyRejectTransaction),
#[serde(rename = "TRADE_CLIENT_EXTENSIONS_MODIFY")]
TradeClientExtensionsModify(TradeClientExtensionsModifyTransaction),
#[serde(rename = "TRADE_CLIENT_EXTENSIONS_MODIFY_REJECT")]
TradeClientExtensionsModifyReject(TradeClientExtensionsModifyRejectTransaction),
#[serde(rename = "MARGIN_CALL_ENTER")]
MarginCallEnter(MarginCallEnterTransaction),
#[serde(rename = "MARGIN_CALL_EXTEND")]
MarginCallExtend(MarginCallExtendTransaction),
#[serde(rename = "MARGIN_CALL_EXIT")]
MarginCallExit(MarginCallExitTransaction),
#[serde(rename = "DELAYED_TRADE_CLOSURE")]
DelayedTradeClosure(DelayedTradeClosureTransaction),
#[serde(rename = "DAILY_FINANCING")]
DailyFinancing(DailyFinancingTransaction),
#[serde(rename = "RESET_RESETTABLE_PL")]
ResetResettablePL(ResetResettablePLTransaction),
#[serde(rename = "DIVIDEND_ADJUSTMENT")]
DividendAdjustment(DividendAdjustmentTransaction),
#[serde(untagged)]
Unknown(serde_json::Value),
}
impl Transaction {
pub fn id(&self) -> Option<&TransactionId> {
for_each_transaction!(self, t => t.id.as_ref(), None)
}
pub fn time(&self) -> Option<&DateTime> {
for_each_transaction!(self, t => t.time.as_ref(), None)
}
pub fn account_id(&self) -> Option<&AccountId> {
for_each_transaction!(self, t => t.account_id.as_ref(), None)
}
pub fn batch_id(&self) -> Option<&TransactionId> {
for_each_transaction!(self, t => t.batch_id.as_ref(), None)
}
pub fn request_id(&self) -> Option<&RequestId> {
for_each_transaction!(self, t => t.request_id.as_ref(), None)
}
pub fn user_id(&self) -> Option<i64> {
for_each_transaction!(self, t => t.user_id, None)
}
pub fn type_name(&self) -> Option<&str> {
match self {
Transaction::Create(_) => Some("CREATE"),
Transaction::Close(_) => Some("CLOSE"),
Transaction::Reopen(_) => Some("REOPEN"),
Transaction::ClientConfigure(_) => Some("CLIENT_CONFIGURE"),
Transaction::ClientConfigureReject(_) => Some("CLIENT_CONFIGURE_REJECT"),
Transaction::TransferFunds(_) => Some("TRANSFER_FUNDS"),
Transaction::TransferFundsReject(_) => Some("TRANSFER_FUNDS_REJECT"),
Transaction::MarketOrder(_) => Some("MARKET_ORDER"),
Transaction::MarketOrderReject(_) => Some("MARKET_ORDER_REJECT"),
Transaction::FixedPriceOrder(_) => Some("FIXED_PRICE_ORDER"),
Transaction::LimitOrder(_) => Some("LIMIT_ORDER"),
Transaction::LimitOrderReject(_) => Some("LIMIT_ORDER_REJECT"),
Transaction::StopOrder(_) => Some("STOP_ORDER"),
Transaction::StopOrderReject(_) => Some("STOP_ORDER_REJECT"),
Transaction::MarketIfTouchedOrder(_) => Some("MARKET_IF_TOUCHED_ORDER"),
Transaction::MarketIfTouchedOrderReject(_) => Some("MARKET_IF_TOUCHED_ORDER_REJECT"),
Transaction::TakeProfitOrder(_) => Some("TAKE_PROFIT_ORDER"),
Transaction::TakeProfitOrderReject(_) => Some("TAKE_PROFIT_ORDER_REJECT"),
Transaction::StopLossOrder(_) => Some("STOP_LOSS_ORDER"),
Transaction::StopLossOrderReject(_) => Some("STOP_LOSS_ORDER_REJECT"),
Transaction::TrailingStopLossOrder(_) => Some("TRAILING_STOP_LOSS_ORDER"),
Transaction::TrailingStopLossOrderReject(_) => Some("TRAILING_STOP_LOSS_ORDER_REJECT"),
Transaction::OrderFill(_) => Some("ORDER_FILL"),
Transaction::OrderCancel(_) => Some("ORDER_CANCEL"),
Transaction::OrderCancelReject(_) => Some("ORDER_CANCEL_REJECT"),
Transaction::OrderClientExtensionsModify(_) => Some("ORDER_CLIENT_EXTENSIONS_MODIFY"),
Transaction::OrderClientExtensionsModifyReject(_) => {
Some("ORDER_CLIENT_EXTENSIONS_MODIFY_REJECT")
}
Transaction::TradeClientExtensionsModify(_) => Some("TRADE_CLIENT_EXTENSIONS_MODIFY"),
Transaction::TradeClientExtensionsModifyReject(_) => {
Some("TRADE_CLIENT_EXTENSIONS_MODIFY_REJECT")
}
Transaction::MarginCallEnter(_) => Some("MARGIN_CALL_ENTER"),
Transaction::MarginCallExtend(_) => Some("MARGIN_CALL_EXTEND"),
Transaction::MarginCallExit(_) => Some("MARGIN_CALL_EXIT"),
Transaction::DelayedTradeClosure(_) => Some("DELAYED_TRADE_CLOSURE"),
Transaction::DailyFinancing(_) => Some("DAILY_FINANCING"),
Transaction::ResetResettablePL(_) => Some("RESET_RESETTABLE_PL"),
Transaction::DividendAdjustment(_) => Some("DIVIDEND_ADJUSTMENT"),
Transaction::Unknown(value) => value.get("type").and_then(serde_json::Value::as_str),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)] pub enum TransactionStreamItem {
Transaction(Transaction),
Heartbeat(TransactionHeartbeat),
}
impl serde::Serialize for TransactionStreamItem {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
TransactionStreamItem::Transaction(tx) => tx.serialize(serializer),
TransactionStreamItem::Heartbeat(heartbeat) => heartbeat.serialize(serializer),
}
}
}
impl<'de> serde::Deserialize<'de> for TransactionStreamItem {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = serde_json::Value::deserialize(deserializer)?;
let is_heartbeat =
value.get("type").and_then(serde_json::Value::as_str) == Some("HEARTBEAT");
if is_heartbeat {
serde_json::from_value(value)
.map(TransactionStreamItem::Heartbeat)
.map_err(serde::de::Error::custom)
} else {
serde_json::from_value(value)
.map(TransactionStreamItem::Transaction)
.map_err(serde::de::Error::custom)
}
}
}