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}