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#[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 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}