Skip to main content

rithmic_rs/
types.rs

1//! Order enums with serde support and protobuf conversions.
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use std::fmt;
7use std::str::FromStr;
8
9use crate::rti::{
10    request_bracket_order, request_modify_order, request_new_order, request_oco_order,
11};
12
13/// Buy or sell.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[non_exhaustive]
17pub enum OrderSide {
18    /// Buy side.
19    #[default]
20    Buy,
21    /// Sell side.
22    Sell,
23}
24
25impl fmt::Display for OrderSide {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            Self::Buy => write!(f, "BUY"),
29            Self::Sell => write!(f, "SELL"),
30        }
31    }
32}
33
34/// Error returned when parsing an invalid [`OrderSide`] string.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct ParseOrderSideError(String);
37
38impl fmt::Display for ParseOrderSideError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "invalid order side: '{}'", self.0)
41    }
42}
43
44impl std::error::Error for ParseOrderSideError {}
45
46impl FromStr for OrderSide {
47    type Err = ParseOrderSideError;
48
49    fn from_str(s: &str) -> Result<Self, Self::Err> {
50        match s.to_uppercase().as_str() {
51            "BUY" | "B" => Ok(Self::Buy),
52            "SELL" | "S" => Ok(Self::Sell),
53            _ => Err(ParseOrderSideError(s.to_string())),
54        }
55    }
56}
57
58impl From<OrderSide> for request_new_order::TransactionType {
59    fn from(side: OrderSide) -> Self {
60        match side {
61            OrderSide::Buy => Self::Buy,
62            OrderSide::Sell => Self::Sell,
63        }
64    }
65}
66
67impl From<OrderSide> for request_bracket_order::TransactionType {
68    fn from(side: OrderSide) -> Self {
69        match side {
70            OrderSide::Buy => Self::Buy,
71            OrderSide::Sell => Self::Sell,
72        }
73    }
74}
75
76impl From<OrderSide> for request_oco_order::TransactionType {
77    fn from(side: OrderSide) -> Self {
78        match side {
79            OrderSide::Buy => Self::Buy,
80            OrderSide::Sell => Self::Sell,
81        }
82    }
83}
84
85/// Order price type.
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
87#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
88#[non_exhaustive]
89pub enum OrderType {
90    /// Market order — executes immediately at the best available price.
91    Market,
92    /// Limit order — executes at the specified price or better.
93    #[default]
94    Limit,
95    /// Stop market order — becomes a market order when the stop price is reached.
96    StopMarket,
97    /// Stop limit order — becomes a limit order when the stop price is reached.
98    StopLimit,
99}
100
101impl fmt::Display for OrderType {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        match self {
104            Self::Market => write!(f, "MARKET"),
105            Self::Limit => write!(f, "LIMIT"),
106            Self::StopMarket => write!(f, "STOP_MARKET"),
107            Self::StopLimit => write!(f, "STOP_LIMIT"),
108        }
109    }
110}
111
112/// Error returned when parsing an invalid [`OrderType`] string.
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct ParseOrderTypeError(String);
115
116impl fmt::Display for ParseOrderTypeError {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        write!(f, "invalid order type: '{}'", self.0)
119    }
120}
121
122impl std::error::Error for ParseOrderTypeError {}
123
124impl FromStr for OrderType {
125    type Err = ParseOrderTypeError;
126
127    fn from_str(s: &str) -> Result<Self, Self::Err> {
128        match s.to_uppercase().as_str() {
129            "MARKET" | "MKT" => Ok(Self::Market),
130            "LIMIT" | "LMT" => Ok(Self::Limit),
131            "STOPMARKET" | "STPMKT" | "STOP_MARKET" | "STOP-MARKET" => Ok(Self::StopMarket),
132            "STOPLIMIT" | "STPLMT" | "STOP_LIMIT" | "STOP-LIMIT" => Ok(Self::StopLimit),
133            _ => Err(ParseOrderTypeError(s.to_string())),
134        }
135    }
136}
137
138impl From<OrderType> for request_new_order::PriceType {
139    fn from(order_type: OrderType) -> Self {
140        match order_type {
141            OrderType::Market => Self::Market,
142            OrderType::Limit => Self::Limit,
143            OrderType::StopMarket => Self::StopMarket,
144            OrderType::StopLimit => Self::StopLimit,
145        }
146    }
147}
148
149impl From<OrderType> for request_modify_order::PriceType {
150    fn from(order_type: OrderType) -> Self {
151        match order_type {
152            OrderType::Market => Self::Market,
153            OrderType::Limit => Self::Limit,
154            OrderType::StopMarket => Self::StopMarket,
155            OrderType::StopLimit => Self::StopLimit,
156        }
157    }
158}
159
160impl From<OrderType> for request_bracket_order::PriceType {
161    fn from(order_type: OrderType) -> Self {
162        match order_type {
163            OrderType::Market => Self::Market,
164            OrderType::Limit => Self::Limit,
165            OrderType::StopMarket => Self::StopMarket,
166            OrderType::StopLimit => Self::StopLimit,
167        }
168    }
169}
170
171impl From<OrderType> for request_oco_order::PriceType {
172    fn from(order_type: OrderType) -> Self {
173        match order_type {
174            OrderType::Market => Self::Market,
175            OrderType::Limit => Self::Limit,
176            OrderType::StopMarket => Self::StopMarket,
177            OrderType::StopLimit => Self::StopLimit,
178        }
179    }
180}
181
182/// How long an order remains active before expiring.
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
184#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
185#[non_exhaustive]
186pub enum TimeInForce {
187    /// Good for the current trading day only.
188    #[default]
189    Day,
190    /// Good till cancelled.
191    Gtc,
192    /// Immediate or cancel — fill what you can, cancel the rest.
193    Ioc,
194    /// Fill or kill — fill the entire order or cancel it.
195    Fok,
196}
197
198impl fmt::Display for TimeInForce {
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        match self {
201            Self::Day => write!(f, "DAY"),
202            Self::Gtc => write!(f, "GTC"),
203            Self::Ioc => write!(f, "IOC"),
204            Self::Fok => write!(f, "FOK"),
205        }
206    }
207}
208
209/// Error returned when parsing an invalid [`TimeInForce`] string.
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub struct ParseTimeInForceError(String);
212
213impl fmt::Display for ParseTimeInForceError {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        write!(f, "invalid time-in-force: '{}'", self.0)
216    }
217}
218
219impl std::error::Error for ParseTimeInForceError {}
220
221impl FromStr for TimeInForce {
222    type Err = ParseTimeInForceError;
223
224    fn from_str(s: &str) -> Result<Self, Self::Err> {
225        match s.to_uppercase().as_str() {
226            "DAY" => Ok(Self::Day),
227            "GTC" | "GOODTILLCANCELLED" | "GOOD_TILL_CANCELLED" | "GOOD-TILL-CANCELLED" => {
228                Ok(Self::Gtc)
229            }
230            "IOC" | "IMMEDIATEORCANCEL" | "IMMEDIATE_OR_CANCEL" | "IMMEDIATE-OR-CANCEL" => {
231                Ok(Self::Ioc)
232            }
233            "FOK" | "FILLORKILL" | "FILL_OR_KILL" | "FILL-OR-KILL" => Ok(Self::Fok),
234            _ => Err(ParseTimeInForceError(s.to_string())),
235        }
236    }
237}
238
239impl From<TimeInForce> for request_new_order::Duration {
240    fn from(tif: TimeInForce) -> Self {
241        match tif {
242            TimeInForce::Day => Self::Day,
243            TimeInForce::Gtc => Self::Gtc,
244            TimeInForce::Ioc => Self::Ioc,
245            TimeInForce::Fok => Self::Fok,
246        }
247    }
248}
249
250impl From<TimeInForce> for request_bracket_order::Duration {
251    fn from(tif: TimeInForce) -> Self {
252        match tif {
253            TimeInForce::Day => Self::Day,
254            TimeInForce::Gtc => Self::Gtc,
255            TimeInForce::Ioc => Self::Ioc,
256            TimeInForce::Fok => Self::Fok,
257        }
258    }
259}
260
261impl From<TimeInForce> for request_oco_order::Duration {
262    fn from(tif: TimeInForce) -> Self {
263        match tif {
264            TimeInForce::Day => Self::Day,
265            TimeInForce::Gtc => Self::Gtc,
266            TimeInForce::Ioc => Self::Ioc,
267            TimeInForce::Fok => Self::Fok,
268        }
269    }
270}