use nautilus_model::identifiers::{
ClientOrderId, InstrumentId, StrategyId, TraderId, VenueOrderId,
};
use nautilus_network::websocket::WebSocketClient;
use serde::{Deserialize, Serialize};
use ustr::Ustr;
use crate::{
common::enums::{
BinanceAlgoStatus, BinanceAlgoType, BinanceFuturesOrderType, BinanceKlineInterval,
BinanceMarginType, BinanceOrderStatus, BinancePositionSide, BinancePriceMatch,
BinanceSelfTradePreventionMode, BinanceSide, BinanceTimeInForce, BinanceWorkingType,
BinanceWsMethod,
},
futures::http::BinanceFuturesInstrument,
};
#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum BinanceFuturesWsStreamsMessage {
AggTrade(BinanceFuturesAggTradeMsg),
Trade(BinanceFuturesTradeMsg),
BookTicker(BinanceFuturesBookTickerMsg),
DepthUpdate(BinanceFuturesDepthUpdateMsg),
MarkPrice(BinanceFuturesMarkPriceMsg),
Kline(BinanceFuturesKlineMsg),
ForceOrder(BinanceFuturesLiquidationMsg),
Ticker(BinanceFuturesTickerMsg),
AccountUpdate(BinanceFuturesAccountUpdateMsg),
OrderUpdate(Box<BinanceFuturesOrderUpdateMsg>),
AlgoUpdate(Box<BinanceFuturesAlgoUpdateMsg>),
MarginCall(BinanceFuturesMarginCallMsg),
AccountConfigUpdate(BinanceFuturesAccountConfigMsg),
ListenKeyExpired,
Error(BinanceFuturesWsErrorMsg),
Reconnected,
}
#[derive(Debug, Clone)]
pub struct BinanceFuturesWsErrorMsg {
pub code: i64,
pub msg: String,
}
#[derive(Debug)]
#[allow(
clippy::large_enum_variant,
reason = "Commands are ephemeral and immediately consumed"
)]
pub enum BinanceFuturesWsStreamsCommand {
SetClient(WebSocketClient),
Disconnect,
Subscribe { streams: Vec<String> },
Unsubscribe { streams: Vec<String> },
}
#[derive(Debug)]
#[allow(
clippy::large_enum_variant,
reason = "Commands are ephemeral and immediately consumed"
)]
pub enum ExecHandlerCommand {
SetClient(WebSocketClient),
Disconnect,
InitializeInstruments(Vec<BinanceFuturesInstrument>),
UpdateInstrument(BinanceFuturesInstrument),
Subscribe { streams: Vec<String> },
RegisterOrder {
client_order_id: ClientOrderId,
trader_id: TraderId,
strategy_id: StrategyId,
instrument_id: InstrumentId,
},
RegisterCancel {
client_order_id: ClientOrderId,
trader_id: TraderId,
strategy_id: StrategyId,
instrument_id: InstrumentId,
venue_order_id: Option<VenueOrderId>,
},
RegisterModify {
client_order_id: ClientOrderId,
trader_id: TraderId,
strategy_id: StrategyId,
instrument_id: InstrumentId,
venue_order_id: Option<VenueOrderId>,
},
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesAggTradeMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "a")]
pub agg_trade_id: u64,
#[serde(rename = "p")]
pub price: String,
#[serde(rename = "q")]
pub quantity: String,
#[serde(rename = "f")]
pub first_trade_id: u64,
#[serde(rename = "l")]
pub last_trade_id: u64,
#[serde(rename = "T")]
pub trade_time: i64,
#[serde(rename = "m")]
pub is_buyer_maker: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesTradeMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "t")]
pub trade_id: u64,
#[serde(rename = "p")]
pub price: String,
#[serde(rename = "q")]
pub quantity: String,
#[serde(rename = "T")]
pub trade_time: i64,
#[serde(rename = "m")]
pub is_buyer_maker: bool,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesDepthUpdateMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "T")]
pub transaction_time: i64,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "U")]
pub first_update_id: u64,
#[serde(rename = "u")]
pub final_update_id: u64,
#[serde(rename = "pu")]
pub prev_final_update_id: u64,
#[serde(rename = "b")]
pub bids: Vec<[String; 2]>,
#[serde(rename = "a")]
pub asks: Vec<[String; 2]>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesMarkPriceMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "p")]
pub mark_price: String,
#[serde(rename = "i")]
pub index_price: String,
#[serde(rename = "P")]
pub estimated_settle_price: String,
#[serde(rename = "r")]
pub funding_rate: String,
#[serde(rename = "T")]
pub next_funding_time: i64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesBookTickerMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "u")]
pub update_id: u64,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "T")]
pub transaction_time: i64,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "b")]
pub best_bid_price: String,
#[serde(rename = "B")]
pub best_bid_qty: String,
#[serde(rename = "a")]
pub best_ask_price: String,
#[serde(rename = "A")]
pub best_ask_qty: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesKlineMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "k")]
pub kline: BinanceFuturesKlineData,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesKlineData {
#[serde(rename = "t")]
pub start_time: i64,
#[serde(rename = "T")]
pub close_time: i64,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "i")]
pub interval: BinanceKlineInterval,
#[serde(rename = "f")]
pub first_trade_id: i64,
#[serde(rename = "L")]
pub last_trade_id: i64,
#[serde(rename = "o")]
pub open: String,
#[serde(rename = "c")]
pub close: String,
#[serde(rename = "h")]
pub high: String,
#[serde(rename = "l")]
pub low: String,
#[serde(rename = "v")]
pub volume: String,
#[serde(rename = "n")]
pub num_trades: i64,
#[serde(rename = "x")]
pub is_closed: bool,
#[serde(rename = "q")]
pub quote_volume: String,
#[serde(rename = "V")]
pub taker_buy_volume: String,
#[serde(rename = "Q")]
pub taker_buy_quote_volume: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesLiquidationMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "o")]
pub order: BinanceFuturesLiquidationOrder,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesLiquidationOrder {
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "S")]
pub side: BinanceSide,
#[serde(rename = "o")]
pub order_type: BinanceFuturesOrderType,
#[serde(rename = "f")]
pub time_in_force: BinanceTimeInForce,
#[serde(rename = "q")]
pub original_qty: String,
#[serde(rename = "p")]
pub price: String,
#[serde(rename = "ap")]
pub average_price: String,
#[serde(rename = "X")]
pub status: BinanceOrderStatus,
#[serde(rename = "l")]
pub last_filled_qty: String,
#[serde(rename = "z")]
pub accumulated_qty: String,
#[serde(rename = "T")]
pub trade_time: i64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesTickerMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "p")]
pub price_change: String,
#[serde(rename = "P")]
pub price_change_percent: String,
#[serde(rename = "w")]
pub weighted_avg_price: String,
#[serde(rename = "c")]
pub last_price: String,
#[serde(rename = "Q")]
pub last_qty: String,
#[serde(rename = "o")]
pub open_price: String,
#[serde(rename = "h")]
pub high_price: String,
#[serde(rename = "l")]
pub low_price: String,
#[serde(rename = "v")]
pub volume: String,
#[serde(rename = "q")]
pub quote_volume: String,
#[serde(rename = "O")]
pub open_time: i64,
#[serde(rename = "C")]
pub close_time: i64,
#[serde(rename = "F")]
pub first_trade_id: i64,
#[serde(rename = "L")]
pub last_trade_id: i64,
#[serde(rename = "n")]
pub num_trades: i64,
}
#[derive(Debug, Clone, Serialize)]
pub struct BinanceFuturesWsSubscribeRequest {
pub method: BinanceWsMethod,
pub params: Vec<String>,
pub id: u64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesWsSubscribeResponse {
pub result: Option<serde_json::Value>,
pub id: u64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesWsErrorResponse {
pub code: i64,
pub msg: String,
pub id: Option<u64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesAccountUpdateMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "T")]
pub transaction_time: i64,
#[serde(rename = "a")]
pub account: AccountUpdateData,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AccountUpdateData {
#[serde(rename = "m")]
pub reason: AccountUpdateReason,
#[serde(rename = "B", default)]
pub balances: Vec<BalanceUpdate>,
#[serde(rename = "P", default)]
pub positions: Vec<PositionUpdate>,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AccountUpdateReason {
Deposit,
Withdraw,
Order,
FundingFee,
WithdrawReject,
Adjustment,
InsuranceClear,
AdminDeposit,
AdminWithdraw,
MarginTransfer,
MarginTypeChange,
AssetTransfer,
OptionsPremiumFee,
OptionsSettleProfit,
AutoExchange,
Adl,
CoinSwapDeposit,
CoinSwapWithdraw,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BalanceUpdate {
#[serde(rename = "a")]
pub asset: Ustr,
#[serde(rename = "wb")]
pub wallet_balance: String,
#[serde(rename = "cw")]
pub cross_wallet_balance: String,
#[serde(rename = "bc", default)]
pub balance_change: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PositionUpdate {
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "pa")]
pub position_amount: String,
#[serde(rename = "ep")]
pub entry_price: String,
#[serde(rename = "bep", default)]
pub break_even_price: Option<String>,
#[serde(rename = "cr")]
pub accumulated_realized: String,
#[serde(rename = "up")]
pub unrealized_pnl: String,
#[serde(rename = "mt")]
pub margin_type: BinanceMarginType,
#[serde(rename = "iw")]
pub isolated_wallet: String,
#[serde(rename = "ps")]
pub position_side: BinancePositionSide,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesOrderUpdateMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "T")]
pub transaction_time: i64,
#[serde(rename = "o")]
pub order: OrderUpdateData,
}
#[derive(Debug, Clone, Deserialize)]
pub struct OrderUpdateData {
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "c")]
pub client_order_id: String,
#[serde(rename = "S")]
pub side: BinanceSide,
#[serde(rename = "o")]
pub order_type: BinanceFuturesOrderType,
#[serde(rename = "f")]
pub time_in_force: BinanceTimeInForce,
#[serde(rename = "q")]
pub original_qty: String,
#[serde(rename = "p")]
pub original_price: String,
#[serde(rename = "ap")]
pub average_price: String,
#[serde(rename = "sp")]
pub stop_price: String,
#[serde(rename = "x")]
pub execution_type: BinanceExecutionType,
#[serde(rename = "X")]
pub order_status: BinanceOrderStatus,
#[serde(rename = "i")]
pub order_id: i64,
#[serde(rename = "l")]
pub last_filled_qty: String,
#[serde(rename = "z")]
pub cumulative_filled_qty: String,
#[serde(rename = "L")]
pub last_filled_price: String,
#[serde(rename = "N", default)]
pub commission_asset: Option<Ustr>,
#[serde(rename = "n", default)]
pub commission: Option<String>,
#[serde(rename = "T")]
pub trade_time: i64,
#[serde(rename = "t")]
pub trade_id: i64,
#[serde(rename = "b", default)]
pub bids_notional: Option<String>,
#[serde(rename = "a", default)]
pub asks_notional: Option<String>,
#[serde(rename = "m")]
pub is_maker: bool,
#[serde(rename = "R")]
pub is_reduce_only: bool,
#[serde(rename = "wt")]
pub working_type: BinanceWorkingType,
#[serde(rename = "ot")]
pub original_order_type: BinanceFuturesOrderType,
#[serde(rename = "ps")]
pub position_side: BinancePositionSide,
#[serde(rename = "cp", default)]
pub close_position: Option<bool>,
#[serde(rename = "AP", default)]
pub activation_price: Option<String>,
#[serde(rename = "cr", default)]
pub callback_rate: Option<String>,
#[serde(rename = "pP", default)]
pub price_protect: Option<bool>,
#[serde(rename = "rp")]
pub realized_profit: String,
#[serde(rename = "V", default)]
pub stp_mode: Option<BinanceSelfTradePreventionMode>,
#[serde(rename = "pm", default)]
pub price_match: Option<BinancePriceMatch>,
#[serde(rename = "gtd", default)]
pub good_till_date: Option<i64>,
}
impl OrderUpdateData {
#[must_use]
pub fn is_liquidation(&self) -> bool {
self.client_order_id.starts_with("autoclose-")
}
#[must_use]
pub fn is_adl(&self) -> bool {
self.client_order_id.starts_with("adl_autoclose")
}
#[must_use]
pub fn is_settlement(&self) -> bool {
self.client_order_id.starts_with("settlement_autoclose-")
}
#[must_use]
pub fn is_exchange_generated(&self) -> bool {
self.is_liquidation() || self.is_adl() || self.is_settlement()
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BinanceExecutionType {
New,
Canceled,
Calculated,
Expired,
Trade,
Amendment,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesMarginCallMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "cw")]
pub cross_wallet_balance: String,
#[serde(rename = "p")]
pub positions: Vec<MarginCallPosition>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MarginCallPosition {
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "ps")]
pub position_side: BinancePositionSide,
#[serde(rename = "pa")]
pub position_amount: String,
#[serde(rename = "mt")]
pub margin_type: BinanceMarginType,
#[serde(rename = "iw")]
pub isolated_wallet: String,
#[serde(rename = "mp")]
pub mark_price: String,
#[serde(rename = "up")]
pub unrealized_pnl: String,
#[serde(rename = "mm")]
pub maintenance_margin: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesAccountConfigMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "T")]
pub transaction_time: i64,
#[serde(rename = "ac", default)]
pub leverage_config: Option<LeverageConfig>,
#[serde(rename = "ai", default)]
pub asset_index: Option<AssetIndexConfig>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct LeverageConfig {
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "l")]
pub leverage: u32,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AssetIndexConfig {
#[serde(rename = "s")]
pub symbol: Ustr,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesAlgoUpdateMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
#[serde(rename = "T")]
pub transaction_time: i64,
#[serde(rename = "o", alias = "ao")]
pub algo_order: AlgoOrderUpdateData,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AlgoOrderUpdateData {
#[serde(rename = "caid")]
pub client_algo_id: String,
#[serde(rename = "aid")]
pub algo_id: i64,
#[serde(rename = "at")]
pub algo_type: BinanceAlgoType,
#[serde(rename = "o")]
pub order_type: BinanceFuturesOrderType,
#[serde(rename = "s")]
pub symbol: Ustr,
#[serde(rename = "S")]
pub side: BinanceSide,
#[serde(rename = "ps")]
pub position_side: BinancePositionSide,
#[serde(rename = "f")]
pub time_in_force: BinanceTimeInForce,
#[serde(rename = "q")]
pub quantity: String,
#[serde(rename = "X")]
pub algo_status: BinanceAlgoStatus,
#[serde(rename = "tp")]
pub trigger_price: String,
#[serde(rename = "p")]
pub price: String,
#[serde(rename = "wt")]
pub working_type: BinanceWorkingType,
#[serde(rename = "pm", default)]
pub price_match: Option<BinancePriceMatch>,
#[serde(rename = "cp", default)]
pub close_position: Option<bool>,
#[serde(rename = "pP", default)]
pub price_protect: Option<bool>,
#[serde(rename = "R", default)]
pub reduce_only: Option<bool>,
#[serde(rename = "tt", default)]
pub trigger_time: Option<i64>,
#[serde(rename = "gtd", default)]
pub good_till_date: Option<i64>,
#[serde(rename = "ai", default)]
pub actual_order_id: Option<String>,
#[serde(rename = "ap", default)]
pub avg_price: Option<String>,
#[serde(rename = "aq", default)]
pub executed_qty: Option<String>,
#[serde(rename = "act", default)]
pub actual_order_type: Option<String>,
#[serde(rename = "cr", default)]
pub callback_rate: Option<String>,
#[serde(rename = "V", default)]
pub stp_mode: Option<BinanceSelfTradePreventionMode>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BinanceFuturesListenKeyExpiredMsg {
#[serde(rename = "e")]
pub event_type: String,
#[serde(rename = "E")]
pub event_time: i64,
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
fn test_account_update_reason_adl_deserializes() {
let value: AccountUpdateReason = serde_json::from_str("\"ADL\"").unwrap();
assert_eq!(value, AccountUpdateReason::Adl);
}
#[rstest]
fn test_account_update_reason_unknown_fallback() {
let value: AccountUpdateReason = serde_json::from_str("\"SOMETHING_NEW\"").unwrap();
assert_eq!(value, AccountUpdateReason::Unknown);
}
}