1use nautilus_model::enums::{OrderType, TimeInForce};
19use serde::{Deserialize, Serialize};
20
21use crate::common::enums::BinanceTimeInForce;
22
23#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
25#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
26pub enum BinanceSpotOrderType {
27 Limit,
29 Market,
31 StopLoss,
33 StopLossLimit,
35 TakeProfit,
37 TakeProfitLimit,
39 LimitMaker,
41 #[serde(other)]
43 Unknown,
44}
45
46#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
48#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
49pub enum BinanceOrderResponseType {
50 Ack,
52 Result,
54 #[default]
56 Full,
57}
58
59#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
61#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
62pub enum BinanceCancelReplaceMode {
63 StopOnFailure,
65 AllowFailure,
67}
68
69#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
77#[serde(rename_all = "camelCase")]
78pub enum BinanceSpotUserDataEventType {
79 ExecutionReport,
81 OutboundAccountPosition,
83 BalanceUpdate,
85 ServerShutdown,
87 ListenKeyExpired,
89 ExternalLockUpdate,
91 EventStreamTerminated,
93 #[serde(other)]
95 Unknown,
96}
97
98pub fn order_type_to_binance_spot(
104 order_type: OrderType,
105 post_only: bool,
106) -> anyhow::Result<BinanceSpotOrderType> {
107 match (order_type, post_only) {
108 (OrderType::Market, _) => Ok(BinanceSpotOrderType::Market),
109 (OrderType::Limit, true) => Ok(BinanceSpotOrderType::LimitMaker),
110 (OrderType::Limit, false) => Ok(BinanceSpotOrderType::Limit),
111 (OrderType::StopMarket, _) => Ok(BinanceSpotOrderType::StopLoss),
112 (OrderType::StopLimit, _) => Ok(BinanceSpotOrderType::StopLossLimit),
113 (OrderType::MarketIfTouched, _) => Ok(BinanceSpotOrderType::TakeProfit),
114 (OrderType::LimitIfTouched, _) => Ok(BinanceSpotOrderType::TakeProfitLimit),
115 _ => anyhow::bail!("Unsupported order type for Binance Spot: {order_type:?}"),
116 }
117}
118
119pub fn time_in_force_to_binance_spot(tif: TimeInForce) -> anyhow::Result<BinanceTimeInForce> {
128 match tif {
129 TimeInForce::Gtc => Ok(BinanceTimeInForce::Gtc),
130 TimeInForce::Ioc => Ok(BinanceTimeInForce::Ioc),
131 TimeInForce::Fok => Ok(BinanceTimeInForce::Fok),
132 _ => anyhow::bail!("Unsupported time in force for Binance Spot: {tif:?}"),
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use rstest::rstest;
139
140 use super::*;
141
142 #[rstest]
143 #[case(OrderType::Market, false, BinanceSpotOrderType::Market)]
144 #[case(OrderType::Limit, false, BinanceSpotOrderType::Limit)]
145 #[case(OrderType::Limit, true, BinanceSpotOrderType::LimitMaker)]
146 #[case(OrderType::StopMarket, false, BinanceSpotOrderType::StopLoss)]
147 #[case(OrderType::StopLimit, false, BinanceSpotOrderType::StopLossLimit)]
148 #[case(OrderType::MarketIfTouched, false, BinanceSpotOrderType::TakeProfit)]
149 #[case(
150 OrderType::LimitIfTouched,
151 false,
152 BinanceSpotOrderType::TakeProfitLimit
153 )]
154 fn test_order_type_to_binance_spot(
155 #[case] order_type: OrderType,
156 #[case] post_only: bool,
157 #[case] expected: BinanceSpotOrderType,
158 ) {
159 let result = order_type_to_binance_spot(order_type, post_only).unwrap();
160 assert_eq!(result, expected);
161 }
162
163 #[rstest]
164 #[case(OrderType::TrailingStopMarket)]
165 fn test_order_type_to_binance_spot_unsupported(#[case] order_type: OrderType) {
166 let result = order_type_to_binance_spot(order_type, false);
167 result.unwrap_err();
168 }
169
170 #[rstest]
171 #[case(TimeInForce::Gtc, BinanceTimeInForce::Gtc)]
172 #[case(TimeInForce::Ioc, BinanceTimeInForce::Ioc)]
173 #[case(TimeInForce::Fok, BinanceTimeInForce::Fok)]
174 fn test_time_in_force_to_binance_spot(
175 #[case] tif: TimeInForce,
176 #[case] expected: BinanceTimeInForce,
177 ) {
178 let result = time_in_force_to_binance_spot(tif).unwrap();
179 assert_eq!(result, expected);
180 }
181
182 #[rstest]
183 #[case(TimeInForce::Gtd)]
184 fn test_time_in_force_to_binance_spot_rejects_gtd(#[case] tif: TimeInForce) {
185 let result = time_in_force_to_binance_spot(tif);
186 result.unwrap_err();
187 }
188}