architect_api/cpty/
okx.rs

1use crate::{
2    folio::FolioMessage,
3    orderflow::{
4        AberrantFill, Ack, Cancel, CancelAll, Fill, Order, OrderflowMessage, Out, Reject,
5        RejectReason,
6    },
7    symbology::market::{MinOrderQuantityUnit, NormalizedMarketInfo},
8    Amount, Dir, OrderId,
9};
10use chrono::{DateTime, Utc};
11use compact_str::CompactString;
12#[cfg(feature = "netidx")]
13use derive::FromValue;
14#[cfg(feature = "netidx")]
15use netidx_derive::Pack;
16use rust_decimal::Decimal;
17use schemars::JsonSchema;
18use serde_derive::{Deserialize, Serialize};
19use std::{ops::Deref, sync::Arc};
20
21const LIVE: CompactString = CompactString::new_inline("live");
22
23#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
24#[cfg_attr(feature = "netidx", derive(Pack))]
25pub struct OkxMarketInfo {
26    pub tick_sz: Decimal,
27    pub min_sz: Decimal,
28    pub state: Option<CompactString>,
29}
30
31impl NormalizedMarketInfo for OkxMarketInfo {
32    fn tick_size(&self) -> Decimal {
33        self.tick_sz
34    }
35
36    fn step_size(&self) -> Decimal {
37        self.min_sz
38    }
39
40    fn min_order_quantity(&self) -> Amount<Decimal, MinOrderQuantityUnit> {
41        return Amount::new(self.min_sz, MinOrderQuantityUnit::Base);
42    }
43
44    fn is_delisted(&self) -> bool {
45        !(self.state == Some(LIVE))
46    }
47}
48
49impl std::fmt::Display for OkxMarketInfo {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        write!(f, "{}", serde_json::to_string_pretty(self).unwrap())?;
52        Ok(())
53    }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
57#[cfg_attr(feature = "netidx", derive(Pack))]
58#[cfg_attr(feature = "netidx", derive(FromValue))]
59pub enum OkxMessage {
60    Order(OkxOrder),
61    Cancel(Cancel),
62    CancelAll(OkxCancelAll),
63    Reject(Reject),
64    Ack(OkxAck),
65    Fill(Result<Fill, AberrantFill>),
66    Out(Out),
67    Folio(FolioMessage),
68    ExchangeSnapshot(Arc<OkxSnapshot>),
69    ExchangeAccountConfig(OkxAccountConfig),
70    ExchangeOrderUpdate(OkxExchangeOrderUpdate),
71    ExchangeReject(Reject),
72}
73
74// Copied from AccountLevel, just making sure we're clear about which types are internal and which are external
75#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
76#[cfg_attr(feature = "netidx", derive(Pack))]
77#[cfg_attr(feature = "netidx", derive(FromValue))]
78pub enum OkxAccountLevel {
79    Simple,
80    SingleCurrencyMargin,
81    MultiCurrencyMargin,
82    PortfolioMargin,
83}
84
85#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
86#[cfg_attr(feature = "netidx", derive(Pack))]
87#[cfg_attr(feature = "netidx", derive(FromValue))]
88pub enum OkxMarginMode {
89    Cross,
90    Isolated,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
94#[cfg_attr(feature = "netidx", derive(Pack))]
95#[cfg_attr(feature = "netidx", derive(FromValue))]
96pub struct OkxAccountConfig {
97    pub account_level: OkxAccountLevel,
98}
99
100#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
101#[cfg_attr(feature = "netidx", derive(Pack))]
102pub struct OkxOrder {
103    #[serde(flatten)]
104    pub order: Order,
105    pub margin_mode: OkxMarginMode,
106}
107
108impl Deref for OkxOrder {
109    type Target = Order;
110
111    fn deref(&self) -> &Self::Target {
112        &self.order
113    }
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
117#[cfg_attr(feature = "netidx", derive(Pack))]
118pub struct OkxAck {
119    #[serde(flatten)]
120    pub ack: Ack,
121}
122
123impl Deref for OkxAck {
124    type Target = Ack;
125
126    fn deref(&self) -> &Self::Target {
127        &self.ack
128    }
129}
130
131#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
132#[cfg_attr(feature = "netidx", derive(Pack))]
133pub struct OkxCancelAll {}
134
135#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
136#[cfg_attr(feature = "netidx", derive(Pack))]
137pub struct OkxExchangeOrderUpdate {
138    pub order_id: OrderId,
139    pub exchange_order_id: CompactString,
140    pub state: OkxExchangeState,
141    pub fill: Option<OkxFill>,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
145#[cfg_attr(feature = "netidx", derive(Pack))]
146pub struct OkxFill {
147    pub order_id: OrderId,
148    pub exchange_order_id: CompactString,
149    pub fill_sz: Option<Decimal>,
150    pub fill_px: Option<Decimal>,
151    pub fill_time: Option<DateTime<Utc>>,
152    pub trade_id: Option<CompactString>,
153    pub dir: Dir,
154    pub is_maker: Option<bool>,
155    pub acc_sz: Decimal,
156    pub fee: Option<Decimal>,
157    pub fee_ccy: Option<CompactString>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
161#[cfg_attr(feature = "netidx", derive(Pack))]
162pub enum OkxExchangeState {
163    Live,
164    Rejected(RejectReason),
165    Canceled,
166    Filled,
167    PartiallyFilled,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
171#[cfg_attr(feature = "netidx", derive(Pack))]
172pub struct OkxSnapshot {
173    pub open_order_ids: Vec<OrderId>,
174}
175
176impl TryFrom<&OkxMessage> for OrderflowMessage {
177    type Error = ();
178
179    fn try_from(value: &OkxMessage) -> Result<Self, Self::Error> {
180        match value {
181            OkxMessage::Order(o) => Ok(OrderflowMessage::Order(**o)),
182            OkxMessage::Cancel(c) => Ok(OrderflowMessage::Cancel(*c)),
183            OkxMessage::Reject(r) => Ok(OrderflowMessage::Reject(r.clone())),
184            OkxMessage::Ack(a) => Ok(OrderflowMessage::Ack(**a)),
185            OkxMessage::CancelAll(_) => {
186                Ok(OrderflowMessage::CancelAll(CancelAll { venue_id: None }))
187            }
188            OkxMessage::Fill(f) => Ok(OrderflowMessage::Fill(f.clone())),
189            OkxMessage::Out(o) => Ok(OrderflowMessage::Out(*o)),
190            OkxMessage::Folio(_)
191            | OkxMessage::ExchangeAccountConfig(_)
192            | OkxMessage::ExchangeSnapshot(_)
193            | OkxMessage::ExchangeReject(_)
194            | OkxMessage::ExchangeOrderUpdate(_) => Err(()),
195        }
196    }
197}
198
199impl TryFrom<&OrderflowMessage> for OkxMessage {
200    type Error = ();
201
202    fn try_from(value: &OrderflowMessage) -> Result<Self, Self::Error> {
203        match value {
204            // CR-someday arao: Make this a parameter the OMS can pass to us
205            OrderflowMessage::Order(o) => Ok(OkxMessage::Order(OkxOrder {
206                order: *o,
207                margin_mode: OkxMarginMode::Isolated,
208            })),
209            OrderflowMessage::Cancel(c) => Ok(OkxMessage::Cancel(*c)),
210            OrderflowMessage::CancelAll(_) => Ok(OkxMessage::CancelAll(OkxCancelAll {})),
211            OrderflowMessage::Reject(r) => Ok(OkxMessage::Reject(r.clone())),
212            OrderflowMessage::Ack(_a) => Err(()),
213            OrderflowMessage::Fill(f) => Ok(OkxMessage::Fill(f.clone())),
214            OrderflowMessage::Out(o) => Ok(OkxMessage::Out(*o)),
215        }
216    }
217}
218
219impl TryFrom<&OkxMessage> for FolioMessage {
220    type Error = ();
221
222    fn try_from(value: &OkxMessage) -> Result<Self, Self::Error> {
223        match value {
224            OkxMessage::Folio(f) => Ok(f.clone()),
225            _ => Err(()),
226        }
227    }
228}
229
230impl TryFrom<&FolioMessage> for OkxMessage {
231    type Error = ();
232
233    fn try_from(value: &FolioMessage) -> Result<Self, Self::Error> {
234        Ok(OkxMessage::Folio(value.clone()))
235    }
236}