rusty_model/
enums.rs

1//! Common enums used across the rusty trading system
2
3use serde::{Deserialize, Serialize};
4use smartstring::alias::String as SmartString;
5
6/// Order side (buy or sell)
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[repr(u8)]
9pub enum OrderSide {
10    /// Buy order
11    Buy = 0,
12    /// Sell order
13    Sell = 1,
14}
15
16impl OrderSide {
17    /// Get the opposite side
18    #[must_use]
19    pub const fn opposite(self) -> Self {
20        match self {
21            Self::Buy => Self::Sell,
22            Self::Sell => Self::Buy,
23        }
24    }
25}
26
27impl std::fmt::Display for OrderSide {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        match self {
30            Self::Buy => write!(f, "Buy"),
31            Self::Sell => write!(f, "Sell"),
32        }
33    }
34}
35
36impl Serialize for OrderSide {
37    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
38    where
39        S: serde::Serializer,
40    {
41        serializer.serialize_u8(*self as u8)
42    }
43}
44
45impl<'de> Deserialize<'de> for OrderSide {
46    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
47    where
48        D: serde::Deserializer<'de>,
49    {
50        match u8::deserialize(deserializer)? {
51            0 => Ok(Self::Buy),
52            1 => Ok(Self::Sell),
53            _ => Err(serde::de::Error::custom("invalid OrderSide")),
54        }
55    }
56}
57
58/// Order type
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
60#[repr(u8)]
61pub enum OrderType {
62    /// Market order (executed immediately at market price)
63    Market = 0,
64    /// Limit order (executed at specified price or better)
65    Limit = 1,
66    /// Stop order (market order when price reaches stop price)
67    Stop = 2,
68    /// Stop limit order (limit order when price reaches stop price)
69    StopLimit = 3,
70    /// Fill or kill (must be filled completely or canceled)
71    FillOrKill = 4,
72    /// Immediate or cancel (partial fills allowed, unfilled portion canceled)
73    ImmediateOrCancel = 5,
74    /// Post only (ensures maker fee, cancels if would immediately match)
75    PostOnly = 6,
76}
77
78impl std::fmt::Display for OrderType {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        match self {
81            Self::Market => write!(f, "Market"),
82            Self::Limit => write!(f, "Limit"),
83            Self::Stop => write!(f, "Stop"),
84            Self::StopLimit => write!(f, "StopLimit"),
85            Self::FillOrKill => write!(f, "FillOrKill"),
86            Self::ImmediateOrCancel => write!(f, "ImmediateOrCancel"),
87            Self::PostOnly => write!(f, "PostOnly"),
88        }
89    }
90}
91
92impl Serialize for OrderType {
93    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
94    where
95        S: serde::Serializer,
96    {
97        serializer.serialize_u8(*self as u8)
98    }
99}
100
101impl<'de> Deserialize<'de> for OrderType {
102    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103    where
104        D: serde::Deserializer<'de>,
105    {
106        match u8::deserialize(deserializer)? {
107            0 => Ok(Self::Market),
108            1 => Ok(Self::Limit),
109            2 => Ok(Self::Stop),
110            3 => Ok(Self::StopLimit),
111            4 => Ok(Self::FillOrKill),
112            5 => Ok(Self::ImmediateOrCancel),
113            6 => Ok(Self::PostOnly),
114            _ => Err(serde::de::Error::custom("invalid OrderType")),
115        }
116    }
117}
118
119/// Time in force
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
121#[repr(u8)]
122pub enum TimeInForce {
123    /// Good till cancelled
124    GTC = 0,
125    /// Immediate or cancel
126    IOC = 1,
127    /// Fill or kill
128    FOK = 2,
129    /// Good till date
130    GTD = 3,
131    /// Post only (maker only)
132    GTX = 4,
133}
134
135impl std::fmt::Display for TimeInForce {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        match self {
138            Self::GTC => write!(f, "GTC"),
139            Self::IOC => write!(f, "IOC"),
140            Self::FOK => write!(f, "FOK"),
141            Self::GTD => write!(f, "GTD"),
142            Self::GTX => write!(f, "GTX"),
143        }
144    }
145}
146
147impl Serialize for TimeInForce {
148    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
149    where
150        S: serde::Serializer,
151    {
152        serializer.serialize_u8(*self as u8)
153    }
154}
155
156impl<'de> Deserialize<'de> for TimeInForce {
157    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
158    where
159        D: serde::Deserializer<'de>,
160    {
161        match u8::deserialize(deserializer)? {
162            0 => Ok(Self::GTC),
163            1 => Ok(Self::IOC),
164            2 => Ok(Self::FOK),
165            3 => Ok(Self::GTD),
166            4 => Ok(Self::GTX),
167            _ => Err(serde::de::Error::custom("invalid TimeInForce")),
168        }
169    }
170}
171
172/// Order status
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
174#[repr(u8)]
175pub enum OrderStatus {
176    /// Order has been received by the system
177    New = 0,
178    /// Order is open/active in the market
179    Open = 1,
180    /// Order is partially filled
181    PartiallyFilled = 2,
182    /// Order is fully filled
183    Filled = 3,
184    /// Order has been cancelled
185    Cancelled = 4,
186    /// Order has been rejected
187    Rejected = 5,
188    /// Order has expired
189    Expired = 6,
190    /// Order is pending (waiting to be processed)
191    Pending = 7,
192    /// Order is in the process of being cancelled
193    PendingCancel = 8,
194    /// Unknown order status (for exchange responses that don't map to known statuses)
195    Unknown = 9,
196}
197
198impl std::fmt::Display for OrderStatus {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        match self {
201            Self::New => write!(f, "New"),
202            Self::Open => write!(f, "Open"),
203            Self::PartiallyFilled => write!(f, "PartiallyFilled"),
204            Self::Filled => write!(f, "Filled"),
205            Self::Cancelled => write!(f, "Cancelled"),
206            Self::Rejected => write!(f, "Rejected"),
207            Self::Expired => write!(f, "Expired"),
208            Self::Pending => write!(f, "Pending"),
209            Self::PendingCancel => write!(f, "PendingCancel"),
210            Self::Unknown => write!(f, "Unknown"),
211        }
212    }
213}
214
215impl Serialize for OrderStatus {
216    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
217    where
218        S: serde::Serializer,
219    {
220        serializer.serialize_u8(*self as u8)
221    }
222}
223
224impl<'de> Deserialize<'de> for OrderStatus {
225    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
226    where
227        D: serde::Deserializer<'de>,
228    {
229        match u8::deserialize(deserializer)? {
230            0 => Ok(Self::New),
231            1 => Ok(Self::Open),
232            2 => Ok(Self::PartiallyFilled),
233            3 => Ok(Self::Filled),
234            4 => Ok(Self::Cancelled),
235            5 => Ok(Self::Rejected),
236            6 => Ok(Self::Expired),
237            7 => Ok(Self::Pending),
238            8 => Ok(Self::PendingCancel),
239            9 => Ok(Self::Unknown),
240            _ => Err(serde::de::Error::custom("invalid OrderStatus")),
241        }
242    }
243}
244
245/// Instrument type
246#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
247#[repr(u8)]
248pub enum InstrumentType {
249    /// Spot trading
250    Spot = 1,
251    /// Futures contract
252    Future = 2,
253    /// Options contract
254    Option = 3,
255    /// Perpetual futures
256    Perpetual = 4,
257}
258
259impl std::fmt::Display for InstrumentType {
260    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261        match self {
262            Self::Spot => write!(f, "Spot"),
263            Self::Future => write!(f, "Future"),
264            Self::Option => write!(f, "Option"),
265            Self::Perpetual => write!(f, "Perpetual"),
266        }
267    }
268}
269
270impl Serialize for InstrumentType {
271    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
272    where
273        S: serde::Serializer,
274    {
275        serializer.serialize_u8(*self as u8)
276    }
277}
278
279impl<'de> Deserialize<'de> for InstrumentType {
280    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
281    where
282        D: serde::Deserializer<'de>,
283    {
284        match u8::deserialize(deserializer)? {
285            1 => Ok(Self::Spot),
286            2 => Ok(Self::Future),
287            3 => Ok(Self::Option),
288            4 => Ok(Self::Perpetual),
289            _ => Err(serde::de::Error::custom("invalid InstrumentType")),
290        }
291    }
292}
293
294/// Asset type
295#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
296#[repr(u8)]
297pub enum AssetType {
298    /// Cryptocurrency (BTC, ETH, etc.)
299    Cryptocurrency = 1,
300    /// Fiat currency (USD, EUR, etc.)
301    Fiat = 2,
302    /// Stablecoin (USDT, USDC, etc.)
303    Stablecoin = 3,
304}
305
306impl std::fmt::Display for AssetType {
307    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308        match self {
309            Self::Cryptocurrency => write!(f, "Cryptocurrency"),
310            Self::Fiat => write!(f, "Fiat"),
311            Self::Stablecoin => write!(f, "Stablecoin"),
312        }
313    }
314}
315
316impl Serialize for AssetType {
317    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
318    where
319        S: serde::Serializer,
320    {
321        serializer.serialize_u8(*self as u8)
322    }
323}
324
325impl<'de> Deserialize<'de> for AssetType {
326    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
327    where
328        D: serde::Deserializer<'de>,
329    {
330        match u8::deserialize(deserializer)? {
331            1 => Ok(Self::Cryptocurrency),
332            2 => Ok(Self::Fiat),
333            3 => Ok(Self::Stablecoin),
334            _ => Err(serde::de::Error::custom("invalid AssetType")),
335        }
336    }
337}
338
339/// Price type
340#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
341#[repr(u8)]
342pub enum PriceType {
343    /// Bid price
344    Bid = 1,
345    /// Ask price
346    Ask = 2,
347    /// Middle value between bid and ask prices
348    Mid = 3,
349    /// Last traded price
350    Last = 4,
351    /// Mark price (for futures/derivatives)
352    Mark = 5,
353    /// Index price
354    Index = 6,
355}
356
357impl std::fmt::Display for PriceType {
358    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359        match self {
360            Self::Bid => write!(f, "Bid"),
361            Self::Ask => write!(f, "Ask"),
362            Self::Mid => write!(f, "Mid"),
363            Self::Last => write!(f, "Last"),
364            Self::Mark => write!(f, "Mark"),
365            Self::Index => write!(f, "Index"),
366        }
367    }
368}
369
370impl Serialize for PriceType {
371    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
372    where
373        S: serde::Serializer,
374    {
375        serializer.serialize_u8(*self as u8)
376    }
377}
378
379impl<'de> Deserialize<'de> for PriceType {
380    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
381    where
382        D: serde::Deserializer<'de>,
383    {
384        match u8::deserialize(deserializer)? {
385            1 => Ok(Self::Bid),
386            2 => Ok(Self::Ask),
387            3 => Ok(Self::Mid),
388            4 => Ok(Self::Last),
389            5 => Ok(Self::Mark),
390            6 => Ok(Self::Index),
391            _ => Err(serde::de::Error::custom("invalid PriceType")),
392        }
393    }
394}
395
396/// Exchange names
397#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
398pub enum Exchange {
399    /// Binance
400    Binance,
401    /// Coinbase
402    Coinbase,
403    /// Bybit
404    Bybit,
405    /// Upbit
406    Upbit,
407    /// Bithumb
408    Bithumb,
409    /// Virtual exchange for backtesting
410    Virtual,
411}
412
413impl std::fmt::Display for Exchange {
414    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415        match self {
416            Self::Binance => write!(f, "Binance"),
417            Self::Coinbase => write!(f, "Coinbase"),
418            Self::Bybit => write!(f, "Bybit"),
419            Self::Upbit => write!(f, "Upbit"),
420            Self::Bithumb => write!(f, "Bithumb"),
421            Self::Virtual => write!(f, "Virtual"),
422        }
423    }
424}
425
426impl Serialize for Exchange {
427    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
428    where
429        S: serde::Serializer,
430    {
431        let s = match self {
432            Self::Binance => "binance",
433            Self::Coinbase => "coinbase",
434            Self::Bybit => "bybit",
435            Self::Upbit => "upbit",
436            Self::Bithumb => "bithumb",
437            Self::Virtual => "virtual",
438        };
439        serializer.serialize_str(s)
440    }
441}
442
443impl<'de> Deserialize<'de> for Exchange {
444    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
445    where
446        D: serde::Deserializer<'de>,
447    {
448        let s = SmartString::deserialize(deserializer)?;
449        match s.to_lowercase().as_str() {
450            "binance" => Ok(Self::Binance),
451            "coinbase" => Ok(Self::Coinbase),
452            "bybit" => Ok(Self::Bybit),
453            "upbit" => Ok(Self::Upbit),
454            "bithumb" => Ok(Self::Bithumb),
455            "virtual" => Ok(Self::Virtual),
456            _ => Err(serde::de::Error::custom(format!("unknown exchange: {s}"))),
457        }
458    }
459}
460
461#[cfg(test)]
462mod tests {
463    use super::*;
464    use rusty_common::json;
465
466    #[test]
467    fn test_order_side() {
468        assert_eq!(format!("{}", OrderSide::Buy), "Buy");
469        assert_eq!(format!("{}", OrderSide::Sell), "Sell");
470        assert_eq!(OrderSide::Buy.opposite(), OrderSide::Sell);
471        assert_eq!(OrderSide::Sell.opposite(), OrderSide::Buy);
472
473        // Serialization
474        assert_eq!(json::to_smartstring(&OrderSide::Buy).unwrap(), "0");
475        assert_eq!(json::to_smartstring(&OrderSide::Sell).unwrap(), "1");
476
477        // Deserialization
478        assert_eq!(json::parse::<OrderSide>("0").unwrap(), OrderSide::Buy);
479        assert_eq!(json::parse::<OrderSide>("1").unwrap(), OrderSide::Sell);
480    }
481
482    #[test]
483    fn test_order_side_opposite_const_context() {
484        // Verify that opposite() can be used in const contexts
485        const BUY_SIDE: OrderSide = OrderSide::Buy;
486        const SELL_SIDE: OrderSide = OrderSide::Sell;
487        const BUY_OPPOSITE: OrderSide = BUY_SIDE.opposite();
488        const SELL_OPPOSITE: OrderSide = SELL_SIDE.opposite();
489
490        assert_eq!(BUY_OPPOSITE, OrderSide::Sell);
491        assert_eq!(SELL_OPPOSITE, OrderSide::Buy);
492    }
493
494    #[test]
495    fn test_order_type() {
496        let types = vec![
497            (OrderType::Market, "0", "Market"),
498            (OrderType::Limit, "1", "Limit"),
499            (OrderType::Stop, "2", "Stop"),
500            (OrderType::StopLimit, "3", "StopLimit"),
501            (OrderType::FillOrKill, "4", "FillOrKill"),
502            (OrderType::ImmediateOrCancel, "5", "ImmediateOrCancel"),
503            (OrderType::PostOnly, "6", "PostOnly"),
504        ];
505
506        for (ot, num, name) in types {
507            assert_eq!(format!("{ot}"), name);
508            assert_eq!(json::to_smartstring(&ot).unwrap(), num);
509            assert_eq!(json::parse::<OrderType>(num).unwrap(), ot);
510        }
511    }
512
513    #[test]
514    fn test_exchange() {
515        assert_eq!(format!("{}", Exchange::Binance), "Binance");
516        assert_eq!(
517            json::to_smartstring(&Exchange::Binance).unwrap(),
518            "\"binance\""
519        );
520        assert_eq!(
521            json::parse::<Exchange>("\"binance\"").unwrap(),
522            Exchange::Binance
523        );
524        assert_eq!(
525            json::parse::<Exchange>("\"BINANCE\"").unwrap(),
526            Exchange::Binance
527        );
528    }
529}