Skip to main content

brokapi_bcs_protocol/models/
enums.rs

1//! Enumerations of the BCS API.
2//!
3//! The broker writes most of its enums as strings of digits (`"1"` for buy) or as upper-case names.
4//! These types serialize to exactly that, so you can use them in requests and read them from replies.
5//! Enums that read replies have an `Unknown` variant (through `#[serde(other)]`), so a value the
6//! broker adds later does not break parsing of a whole frame.
7
8use serde::{Deserialize, Serialize};
9
10/// Instrument type, as the broker names it in its upper-case form.
11///
12/// This enum covers seven kinds only. The broker's reference data has more (`FOREIGN_STOCK`, `NOTES`,
13/// `DEPOSITARY_RECEIPTS`, `EURO_BONDS`, `GOODS`, `INDICES`) and spells bonds `BONDS`, not `BOND`. The
14/// request and response models of [`crate::http::models`] therefore keep the instrument type as a
15/// string, and this enum is for code that works with the narrow set. Do not rely on it to parse all
16/// broker replies.
17///
18/// # Examples
19///
20/// ```
21/// use brokapi_bcs_protocol::models::enums::InstrumentType;
22///
23/// let t: InstrumentType = serde_json::from_str("\"MUTUAL_FUNDS\"")?;
24/// assert_eq!(t, InstrumentType::MutualFunds);
25/// assert_eq!(serde_json::to_string(&InstrumentType::Etf)?, "\"ETF\"");
26/// # Ok::<(), serde_json::Error>(())
27/// ```
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
30pub enum InstrumentType {
31    /// Share (`STOCK`).
32    Stock,
33    /// Bond (`BOND`). The broker's own name for bonds in reference data is `BONDS`.
34    Bond,
35    /// Option (`OPTIONS`).
36    Options,
37    /// Future (`FUTURES`).
38    Futures,
39    /// Mutual fund (`MUTUAL_FUNDS`).
40    MutualFunds,
41    /// Currency (`CURRENCY`).
42    Currency,
43    /// Exchange-traded fund (`ETF`).
44    Etf,
45}
46
47/// Exchange code, as the broker writes it in the `exchange` fields.
48///
49/// # Examples
50///
51/// ```
52/// use brokapi_bcs_protocol::models::enums::Exchange;
53///
54/// let e: Exchange = serde_json::from_str("\"SPB\"")?;
55/// assert_eq!(e, Exchange::Spb);
56/// assert_eq!(serde_json::to_string(&Exchange::Moex)?, "\"MOEX\"");
57/// # Ok::<(), serde_json::Error>(())
58/// ```
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
61pub enum Exchange {
62    /// Moscow Exchange (`MOEX`).
63    Moex,
64    /// SPB Exchange (`SPB`).
65    #[serde(rename = "SPB")]
66    Spb,
67}
68
69/// Top-level kind of a portfolio position (the `upperType` field).
70///
71/// The broker docs call the field "top-level instrument type" and do not list its values. These four
72/// are the ones the code knows, not verified against the live API. The portfolio models keep the field
73/// as a string for that reason.
74///
75/// # Examples
76///
77/// ```
78/// use brokapi_bcs_protocol::models::enums::UpperType;
79///
80/// let t: UpperType = serde_json::from_str("\"FUTURES\"")?;
81/// assert_eq!(t, UpperType::Futures);
82/// # Ok::<(), serde_json::Error>(())
83/// ```
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
85#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
86pub enum UpperType {
87    /// Russian securities (`RUSSIA`).
88    Russia,
89    /// Foreign securities (`FOREIGN`).
90    Foreign,
91    /// Currency (`CURRENCY`).
92    Currency,
93    /// Futures (`FUTURES`).
94    Futures,
95}
96
97/// Status of a trading session: `OPEN` or `CLOSE`.
98///
99/// # Examples
100///
101/// ```
102/// use brokapi_bcs_protocol::models::enums::TradingSessionStatus;
103///
104/// let s: TradingSessionStatus = serde_json::from_str("\"CLOSE\"")?;
105/// assert_eq!(s, TradingSessionStatus::Close);
106/// # Ok::<(), serde_json::Error>(())
107/// ```
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
109#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
110pub enum TradingSessionStatus {
111    /// Trading is open (`OPEN`).
112    Open,
113    /// Trading is closed (`CLOSE`).
114    Close,
115}
116
117/// Side of an order: buy or sell. The wire form is the digit as a string.
118///
119/// `"1"` is buy, `"2"` is sell. Any other text reads as [`OrderSide::Unknown`]. Never send
120/// `Unknown` in a request: it would be written as the text `Unknown`, which the broker does not know.
121///
122/// # Examples
123///
124/// ```
125/// use brokapi_bcs_protocol::models::enums::OrderSide;
126///
127/// assert_eq!(serde_json::to_string(&OrderSide::Buy)?, "\"1\"");
128/// assert_eq!(serde_json::from_str::<OrderSide>("\"2\"")?, OrderSide::Sell);
129/// assert_eq!(serde_json::from_str::<OrderSide>("\"7\"")?, OrderSide::Unknown);
130/// assert_eq!(OrderSide::Buy.to_string(), "Buy");
131/// # Ok::<(), serde_json::Error>(())
132/// ```
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
134pub enum OrderSide {
135    /// Buy (`"1"`).
136    #[serde(rename = "1")]
137    Buy,
138    /// Sell (`"2"`).
139    #[serde(rename = "2")]
140    Sell,
141    /// A value this library does not know. It appears when reading, never use it when sending.
142    #[serde(other)]
143    Unknown,
144}
145
146impl std::fmt::Display for OrderSide {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        match self {
149            OrderSide::Buy => write!(f, "Buy"),
150            OrderSide::Sell => write!(f, "Sell"),
151            OrderSide::Unknown => write!(f, "Unknown"),
152        }
153    }
154}
155
156/// Type of an order as it appears in **order events** of the WebSocket (`orderType` of an
157/// order message).
158///
159/// Only market and limit orders are named. The broker has more types (algorithmic, iceberg,
160/// conditional, see [`crate::http::models::OrderType`]), and they read here as [`OrderType::Unknown`].
161/// The wire form is the digit as a string.
162///
163/// # Examples
164///
165/// ```
166/// use brokapi_bcs_protocol::models::enums::OrderType;
167///
168/// assert_eq!(serde_json::from_str::<OrderType>("\"2\"")?, OrderType::Limit);
169/// assert_eq!(serde_json::from_str::<OrderType>("\"5\"")?, OrderType::Unknown);
170/// # Ok::<(), serde_json::Error>(())
171/// ```
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
173pub enum OrderType {
174    /// Market order (`"1"`).
175    #[serde(rename = "1")]
176    Market,
177    /// Limit order (`"2"`).
178    #[serde(rename = "2")]
179    Limit,
180    /// Any other type, or a value this library does not know.
181    #[serde(other)]
182    Unknown,
183}
184impl std::fmt::Display for OrderType {
185    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186        match self {
187            OrderType::Market => write!(f, "Market"),
188            OrderType::Limit => write!(f, "Limit"),
189            OrderType::Unknown => write!(f, "Unknown"),
190        }
191    }
192}
193
194/// State of an order in an event or a state reply (`orderStatus`). The wire form is a digit string.
195///
196/// The final states are `Filled`, `Cancelled`, `Replaced` and `Rejected`. The others are in
197/// progress. Do not mix this with the status codes of the **order list** (`orders/search`), which are
198/// numbers with other meanings, see [`crate::http::models::OrderRecord::order_status`].
199///
200/// # Examples
201///
202/// ```
203/// use brokapi_bcs_protocol::models::enums::OrderStatus;
204///
205/// assert_eq!(serde_json::from_str::<OrderStatus>("\"8\"")?, OrderStatus::Rejected);
206/// assert_eq!(serde_json::from_str::<OrderStatus>("\"77\"")?, OrderStatus::Unknown);
207/// # Ok::<(), serde_json::Error>(())
208/// ```
209#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
210pub enum OrderStatus {
211    /// New, accepted and working (`"0"`).
212    #[serde(rename = "0")]
213    New,
214    /// Partly filled, the rest is still working (`"1"`).
215    #[serde(rename = "1")]
216    PartiallyFilled,
217    /// Fully filled (`"2"`).
218    #[serde(rename = "2")]
219    Filled,
220    /// Cancelled (`"4"`).
221    #[serde(rename = "4")]
222    Cancelled,
223    /// Replaced by a new order, after an edit (`"5"`).
224    #[serde(rename = "5")]
225    Replaced,
226    /// Being cancelled, the cancel is not done yet (`"6"`).
227    #[serde(rename = "6")]
228    PendingCancel,
229    /// Rejected, see the reject reason of the message (`"8"`).
230    #[serde(rename = "8")]
231    Rejected,
232    /// Being replaced, for example while an edit is processed (`"9"`).
233    #[serde(rename = "9")]
234    PendingReplace,
235    /// Waiting for confirmation of the new order (`"10"`).
236    #[serde(rename = "10")]
237    PendingConfirmation,
238    /// A value this library does not know.
239    #[serde(other)]
240    Unknown,
241}
242
243/// Type of an execution report (`executionType`): why the broker sent this message.
244///
245/// A fill arrives as `PartialFill` or `Fill`, a trade as `Trade`, and `OrderStatus` is a plain status
246/// report with no new fill. The wire form is a digit string.
247///
248/// # Examples
249///
250/// ```
251/// use brokapi_bcs_protocol::models::enums::ExecutionType;
252///
253/// assert_eq!(serde_json::from_str::<ExecutionType>("\"11\"")?, ExecutionType::Trade);
254/// # Ok::<(), serde_json::Error>(())
255/// ```
256#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
257pub enum ExecutionType {
258    /// The order is new (`"0"`).
259    #[serde(rename = "0")]
260    New,
261    /// Partly filled (`"1"`).
262    #[serde(rename = "1")]
263    PartialFill,
264    /// Fully filled (`"2"`).
265    #[serde(rename = "2")]
266    Fill,
267    /// Cancelled (`"4"`).
268    #[serde(rename = "4")]
269    Cancelled,
270    /// Replaced (`"5"`).
271    #[serde(rename = "5")]
272    Replaced,
273    /// Waiting for cancel (`"6"`).
274    #[serde(rename = "6")]
275    PendingCancel,
276    /// Rejected (`"8"`).
277    #[serde(rename = "8")]
278    Rejected,
279    /// Suspended (`"9"`).
280    #[serde(rename = "9")]
281    Suspended,
282    /// Waiting for confirmation of the new order (`"10"`).
283    #[serde(rename = "10")]
284    PendingNew,
285    /// A trade (`"11"`).
286    #[serde(rename = "11")]
287    Trade,
288    /// A status report (`"12"`).
289    #[serde(rename = "12")]
290    OrderStatus,
291    /// A corrected report (`"13"`).
292    #[serde(rename = "13")]
293    Corrected,
294    /// A value this library does not know.
295    #[serde(other)]
296    Unknown,
297}
298
299/// Kind of a market data subscription command: subscribe or unsubscribe.
300///
301/// The wire form of the variants is the digit as a string, but the market data requests in
302/// [`crate::ws::models::MarketDataSubscribe`] send `subscribeType` as a number, as the broker docs
303/// show. This enum is not used by that struct.
304///
305/// # Examples
306///
307/// ```
308/// use brokapi_bcs_protocol::models::enums::SubscribeType;
309///
310/// assert_eq!(SubscribeType::Unsubscribe as u8, 1);
311/// assert_eq!(serde_json::to_string(&SubscribeType::Subscribe)?, "\"0\"");
312/// # Ok::<(), serde_json::Error>(())
313/// ```
314#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
315pub enum SubscribeType {
316    /// Subscribe (`0`).
317    #[serde(rename = "0")]
318    Subscribe = 0,
319    /// Unsubscribe (`1`).
320    #[serde(rename = "1")]
321    Unsubscribe = 1,
322}
323
324/// Candle time frame, in the names of the broker.
325///
326/// The names are serialized as written (`M1`, `H4`, `D`, `MN`) and are exactly the list of the broker
327/// docs: `M1 M5 M15 M30 H1 H4 D W MN`. There is no ten-minute frame at BCS. The request models of this
328/// crate take the time frame as a string, so this enum is a convenience: `TimeFrame::D.name()` is the
329/// string to send.
330///
331/// # Examples
332///
333/// ```
334/// use brokapi_bcs_protocol::models::enums::TimeFrame;
335///
336/// assert_eq!(serde_json::to_string(&TimeFrame::H1)?, "\"H1\"");
337/// assert_eq!(TimeFrame::D.name(), "D");
338/// // Names the broker does not know are refused when reading.
339/// assert!(serde_json::from_str::<TimeFrame>("\"D1\"").is_err());
340/// # Ok::<(), serde_json::Error>(())
341/// ```
342#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
343pub enum TimeFrame {
344    /// One minute.
345    M1,
346    /// Five minutes.
347    M5,
348    /// Fifteen minutes.
349    M15,
350    /// Thirty minutes.
351    M30,
352    /// One hour.
353    H1,
354    /// Four hours.
355    H4,
356    /// One day (`D`).
357    D,
358    /// One week (`W`).
359    W,
360    /// One month.
361    MN,
362}
363
364impl TimeFrame {
365    /// The name the broker uses, the one to put into a request.
366    #[must_use]
367    pub fn name(self) -> &'static str {
368        match self {
369            Self::M1 => "M1",
370            Self::M5 => "M5",
371            Self::M15 => "M15",
372            Self::M30 => "M30",
373            Self::H1 => "H1",
374            Self::H4 => "H4",
375            Self::D => "D",
376            Self::W => "W",
377            Self::MN => "MN",
378        }
379    }
380}
381
382/// Side of a trade on the anonymous trades tape (WebSocket `side` as text).
383///
384/// The tape sends `BUY` and `SELL` as text. The REST tape sends the digits `"1"` and `"2"` instead, as
385/// a string in [`crate::http::models::LastTradeRecord::side`].
386///
387/// # Examples
388///
389/// ```
390/// use brokapi_bcs_protocol::models::enums::TradeSide;
391///
392/// assert_eq!(serde_json::from_str::<TradeSide>("\"SELL\"")?, TradeSide::Sell);
393/// # Ok::<(), serde_json::Error>(())
394/// ```
395#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
396#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
397pub enum TradeSide {
398    /// A buy (`BUY`).
399    Buy,
400    /// A sell (`SELL`).
401    Sell,
402}
403
404/// Kind of a portfolio record (`type` of a portfolio position), in camelCase.
405///
406/// The values the code knows are `moneyLimit` and `depoLimit`. The broker docs describe the field as
407/// "position type" and list no values, so the portfolio models keep it as a string.
408///
409/// # Examples
410///
411/// ```
412/// use brokapi_bcs_protocol::models::enums::PortfolioPositionType;
413///
414/// let t: PortfolioPositionType = serde_json::from_str("\"depoLimit\"")?;
415/// assert_eq!(t, PortfolioPositionType::DepoLimit);
416/// # Ok::<(), serde_json::Error>(())
417/// ```
418#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
419#[serde(rename_all = "camelCase")]
420pub enum PortfolioPositionType {
421    /// Money balance (`moneyLimit`).
422    MoneyLimit,
423    /// Securities position (`depoLimit`).
424    DepoLimit,
425}