nautilus_okx/http/
models.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use serde::{Deserialize, Serialize};
17use ustr::Ustr;
18
19use crate::common::parse::{deserialize_empty_string_as_none, deserialize_empty_ustr_as_none};
20
21/// Represents a trade tick from the GET /api/v5/market/trades endpoint.
22#[derive(Clone, Debug, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase")]
24pub struct OKXTrade {
25    /// Instrument ID.
26    pub inst_id: Ustr,
27    /// Trade price.
28    pub px: String,
29    /// Trade size.
30    pub sz: String,
31    /// Trade side: buy or sell.
32    pub side: OKXSide,
33    /// Trade ID assigned by OKX.
34    pub trade_id: Ustr,
35    /// Trade timestamp in milliseconds.
36    #[serde(deserialize_with = "deserialize_string_to_u64")]
37    pub ts: u64,
38}
39
40/// Represents a candlestick from the GET /api/v5/market/history-candles endpoint.
41/// The tuple contains [timestamp(ms), open, high, low, close, volume, turnover, base_volume, count].
42#[derive(Clone, Debug, Serialize, Deserialize)]
43pub struct OKXCandlestick(
44    /// Timestamp in milliseconds.
45    pub String,
46    /// Open price.
47    pub String,
48    /// High price.
49    pub String,
50    /// Low price.
51    pub String,
52    /// Close price.
53    pub String,
54    /// Volume.
55    pub String,
56    /// Turnover in quote currency.
57    pub String,
58    /// Base volume.
59    pub String,
60    /// Record count.
61    pub String,
62);
63
64use crate::common::{
65    enums::{OKXExecType, OKXInstrumentType, OKXMarginMode, OKXPositionSide, OKXSide},
66    parse::deserialize_string_to_u64,
67};
68
69/// Represents a mark price from the GET /api/v5/public/mark-price endpoint.
70#[derive(Clone, Debug, Serialize, Deserialize)]
71#[serde(rename_all = "camelCase")]
72pub struct OKXMarkPrice {
73    /// Underlying.
74    pub uly: Option<Ustr>,
75    /// Instrument ID.
76    pub inst_id: Ustr,
77    /// The mark price.
78    pub mark_px: String,
79    /// The timestamp for the mark price.
80    #[serde(deserialize_with = "deserialize_string_to_u64")]
81    pub ts: u64,
82}
83
84/// Represents an index price from the GET /api/v5/public/index-tickers endpoint.
85#[derive(Clone, Debug, Serialize, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct OKXIndexTicker {
88    /// Instrument ID.
89    pub inst_id: Ustr,
90    /// The index price.
91    pub idx_px: String,
92    /// The timestamp for the index price.
93    #[serde(deserialize_with = "deserialize_string_to_u64")]
94    pub ts: u64,
95}
96
97/// Represents a position tier from the GET /api/v5/public/position-tiers endpoint.
98#[derive(Clone, Debug, Serialize, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct OKXPositionTier {
101    /// Underlying.
102    pub uly: Ustr,
103    /// Instrument family.
104    pub inst_family: String,
105    /// Instrument ID.
106    pub inst_id: Ustr,
107    /// Tier level.
108    pub tier: String,
109    /// Minimum size/amount for the tier.
110    pub min_sz: String,
111    /// Maximum size/amount for the tier.
112    pub max_sz: String,
113    /// Maintenance margin requirement rate.
114    pub mmr: String,
115    /// Initial margin requirement rate.
116    pub imr: String,
117    /// Maximum available leverage.
118    pub max_lever: String,
119    /// Option Margin Coefficient (only applicable to options).
120    pub opt_mgn_factor: String,
121    /// Quote currency borrowing amount.
122    pub quote_max_loan: String,
123    /// Base currency borrowing amount.
124    pub base_max_loan: String,
125}
126
127/// Represents an account balance snapshot from `GET /api/v5/account/balance`.
128#[derive(Clone, Debug, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct OKXAccount {
131    /// Adjusted/Effective equity in USD.
132    pub adj_eq: String,
133    /// Borrow frozen amount.
134    pub borrow_froz: String,
135    /// Account details by currency.
136    pub details: Vec<OKXBalanceDetail>,
137    /// Initial margin requirement.
138    pub imr: String,
139    /// Isolated margin equity.
140    pub iso_eq: String,
141    /// Margin ratio.
142    pub mgn_ratio: String,
143    /// Maintenance margin requirement.
144    pub mmr: String,
145    /// Notional value in USD for borrow.
146    pub notional_usd_for_borrow: String,
147    /// Notional value in USD for futures.
148    pub notional_usd_for_futures: String,
149    /// Notional value in USD for option.
150    pub notional_usd_for_option: String,
151    /// Notional value in USD for swap.
152    pub notional_usd_for_swap: String,
153    /// Notional value in USD.
154    pub notional_usd: String,
155    /// Order frozen.
156    pub ord_froz: String,
157    /// Total equity in USD.
158    pub total_eq: String,
159    /// Last update time, Unix timestamp in milliseconds.
160    #[serde(deserialize_with = "deserialize_string_to_u64")]
161    pub u_time: u64,
162    /// Unrealized profit and loss.
163    pub upl: String,
164}
165
166/// Represents a balance detail for a single currency in an OKX account.
167#[derive(Clone, Debug, Serialize, Deserialize)]
168#[serde(rename_all = "camelCase")]
169pub struct OKXBalanceDetail {
170    /// Available balance.
171    pub avail_bal: String,
172    /// Available equity.
173    pub avail_eq: String,
174    /// Borrow frozen amount.
175    pub borrow_froz: String,
176    /// Cash balance.
177    pub cash_bal: String,
178    /// Currency.
179    pub ccy: Ustr,
180    /// Cross liability.
181    pub cross_liab: String,
182    /// Discount equity in USD.
183    pub dis_eq: String,
184    /// Equity.
185    pub eq: String,
186    /// Equity in USD.
187    pub eq_usd: String,
188    /// Same-token equity.
189    pub smt_sync_eq: String,
190    /// Copy trading equity.
191    pub spot_copy_trading_eq: String,
192    /// Fixed balance.
193    pub fixed_bal: String,
194    /// Frozen balance.
195    pub frozen_bal: String,
196    /// Initial margin requirement.
197    pub imr: String,
198    /// Interest.
199    pub interest: String,
200    /// Isolated margin equity.
201    pub iso_eq: String,
202    /// Isolated margin liability.
203    pub iso_liab: String,
204    /// Isolated unrealized profit and loss.
205    pub iso_upl: String,
206    /// Liability.
207    pub liab: String,
208    /// Maximum loan amount.
209    pub max_loan: String,
210    /// Margin ratio.
211    pub mgn_ratio: String,
212    /// Maintenance margin requirement.
213    pub mmr: String,
214    /// Notional leverage.
215    pub notional_lever: String,
216    /// Order frozen.
217    pub ord_frozen: String,
218    /// Reward balance.
219    pub reward_bal: String,
220    /// Spot in use amount.
221    #[serde(alias = "spotInUse")]
222    pub spot_in_use_amt: String,
223    /// Cross liability spot in use amount.
224    #[serde(alias = "clSpotInUse")]
225    pub cl_spot_in_use_amt: String,
226    /// Maximum spot in use amount.
227    #[serde(alias = "maxSpotInUse")]
228    pub max_spot_in_use_amt: String,
229    /// Spot isolated balance.
230    pub spot_iso_bal: String,
231    /// Strategy equity.
232    pub stgy_eq: String,
233    /// Time-weighted average price.
234    pub twap: String,
235    /// Last update time, Unix timestamp in milliseconds.
236    #[serde(deserialize_with = "deserialize_string_to_u64")]
237    pub u_time: u64,
238    /// Unrealized profit and loss.
239    pub upl: String,
240    /// Unrealized profit and loss liability.
241    pub upl_liab: String,
242    /// Spot balance.
243    pub spot_bal: String,
244    /// Open average price.
245    pub open_avg_px: String,
246    /// Accumulated average price.
247    pub acc_avg_px: String,
248    /// Spot unrealized profit and loss.
249    pub spot_upl: String,
250    /// Spot unrealized profit and loss ratio.
251    pub spot_upl_ratio: String,
252    /// Total profit and loss.
253    pub total_pnl: String,
254    /// Total profit and loss ratio.
255    pub total_pnl_ratio: String,
256}
257
258/// Represents a single open position from `GET /api/v5/account/positions`.
259#[derive(Clone, Debug, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase")]
261pub struct OKXPosition {
262    /// Instrument ID.
263    pub inst_id: Ustr,
264    /// Instrument type.
265    pub inst_type: OKXInstrumentType,
266    /// Margin mode: isolated/cross.
267    pub mgn_mode: OKXMarginMode,
268    /// Position ID.
269    #[serde(default, deserialize_with = "deserialize_empty_ustr_as_none")]
270    pub pos_id: Option<Ustr>,
271    /// Position side: long/short.
272    pub pos_side: OKXPositionSide,
273    /// Position size.
274    pub pos: String,
275    /// Base currency balance.
276    pub base_bal: String,
277    /// Position currency.
278    pub ccy: String,
279    /// Trading fee.
280    pub fee: String,
281    /// Position leverage.
282    pub lever: String,
283    /// Last traded price.
284    pub last: String,
285    /// Mark price.
286    pub mark_px: String,
287    /// Liquidation price.
288    pub liq_px: String,
289    /// Maintenance margin requirement.
290    pub mmr: String,
291    /// Interest.
292    pub interest: String,
293    /// Trade ID.
294    pub trade_id: Ustr,
295    /// Notional value of position in USD.
296    pub notional_usd: String,
297    /// Average entry price.
298    pub avg_px: String,
299    /// Unrealized profit and loss.
300    pub upl: String,
301    /// Unrealized profit and loss ratio.
302    pub upl_ratio: String,
303    /// Last update time, Unix timestamp in milliseconds.
304    #[serde(deserialize_with = "deserialize_string_to_u64")]
305    pub u_time: u64,
306    /// Position margin.
307    pub margin: String,
308    /// Margin ratio.
309    pub mgn_ratio: String,
310    /// Auto-deleveraging (ADL) ranking.
311    pub adl: String,
312    /// Creation time, Unix timestamp in milliseconds.
313    pub c_time: String,
314    /// Realized profit and loss.
315    pub realized_pnl: String,
316    /// Unrealized profit and loss at last price.
317    pub upl_last_px: String,
318    /// Unrealized profit and loss ratio at last price.
319    pub upl_ratio_last_px: String,
320    /// Available position that can be closed.
321    pub avail_pos: String,
322    /// Breakeven price.
323    pub be_px: String,
324    /// Funding fee.
325    pub funding_fee: String,
326    /// Index price.
327    pub idx_px: String,
328    /// Liquidation penalty.
329    pub liq_penalty: String,
330    /// Option value.
331    pub opt_val: String,
332    /// Pending close order liability value.
333    pub pending_close_ord_liab_val: String,
334    /// Total profit and loss.
335    pub pnl: String,
336    /// Position currency.
337    pub pos_ccy: String,
338    /// Quote currency balance.
339    pub quote_bal: String,
340    /// Borrowed amount in quote currency.
341    pub quote_borrowed: String,
342    /// Interest on quote currency.
343    pub quote_interest: String,
344    /// Amount in use for spot trading.
345    #[serde(alias = "spotInUse")]
346    pub spot_in_use_amt: String,
347    /// Currency in use for spot trading.
348    pub spot_in_use_ccy: String,
349    /// USD price.
350    pub usd_px: String,
351}
352
353/// Represents the response from `POST /api/v5/trade/order` (place order).
354/// This model is designed to be flexible and handle the minimal fields that the API returns.
355#[derive(Clone, Debug, Serialize, Deserialize)]
356#[serde(rename_all = "camelCase")]
357pub struct OKXPlaceOrderResponse {
358    /// Order ID.
359    #[serde(default)]
360    pub ord_id: Option<Ustr>,
361    /// Client order ID.
362    #[serde(default)]
363    pub cl_ord_id: Option<Ustr>,
364    /// Order tag.
365    #[serde(default)]
366    pub tag: Option<String>,
367    /// Instrument ID (optional - might not be in response).
368    #[serde(default)]
369    pub inst_id: Option<Ustr>,
370    /// Order side (optional).
371    #[serde(default)]
372    pub side: Option<OKXSide>,
373    /// Order type (optional).
374    #[serde(default)]
375    pub ord_type: Option<String>,
376    /// Order size (optional).
377    #[serde(default)]
378    pub sz: Option<String>,
379    /// Order state (optional).
380    #[serde(default)]
381    pub state: Option<String>,
382    /// Price (optional).
383    #[serde(default)]
384    pub px: Option<String>,
385    /// Average price (optional).
386    #[serde(default)]
387    pub avg_px: Option<String>,
388    /// Accumulated filled size.
389    #[serde(default)]
390    pub acc_fill_sz: Option<String>,
391    /// Fill size (optional).
392    #[serde(default)]
393    pub fill_sz: Option<String>,
394    /// Fill price (optional).
395    #[serde(default)]
396    pub fill_px: Option<String>,
397    /// Trade ID (optional).
398    #[serde(default)]
399    pub trade_id: Option<Ustr>,
400    /// Fill time (optional).
401    #[serde(default)]
402    pub fill_time: Option<String>,
403    /// Fee (optional).
404    #[serde(default)]
405    pub fee: Option<String>,
406    /// Fee currency (optional).
407    #[serde(default)]
408    pub fee_ccy: Option<String>,
409    /// Request ID (optional).
410    #[serde(default)]
411    pub req_id: Option<Ustr>,
412    /// Position side (optional).
413    #[serde(default)]
414    pub pos_side: Option<OKXPositionSide>,
415    /// Reduce-only flag (optional).
416    #[serde(default)]
417    pub reduce_only: Option<String>,
418    /// Target currency (optional).
419    #[serde(default)]
420    pub tgt_ccy: Option<String>,
421    /// Creation time.
422    #[serde(default)]
423    pub c_time: Option<String>,
424    /// Last update time (optional).
425    #[serde(default)]
426    pub u_time: Option<String>,
427}
428
429/// Represents a single historical order record from `GET /api/v5/trade/orders-history`.
430#[derive(Clone, Debug, Serialize, Deserialize)]
431#[serde(rename_all = "camelCase")]
432pub struct OKXOrderHistory {
433    /// Order ID.
434    pub ord_id: Ustr,
435    /// Client order ID.
436    pub cl_ord_id: Ustr,
437    /// Client account ID (may be omitted by OKX).
438    #[serde(default)]
439    pub cl_act_id: Option<Ustr>,
440    /// Order tag.
441    pub tag: String,
442    /// Instrument type.
443    pub inst_type: OKXInstrumentType,
444    /// Underlying (optional).
445    pub uly: Option<Ustr>,
446    /// Instrument ID.
447    pub inst_id: Ustr,
448    /// Order type.
449    pub ord_type: String,
450    /// Order size.
451    pub sz: String,
452    /// Price (optional).
453    pub px: String,
454    /// Side.
455    pub side: OKXSide,
456    /// Position side.
457    pub pos_side: OKXPositionSide,
458    /// Trade mode.
459    pub td_mode: String,
460    /// Reduce-only flag.
461    pub reduce_only: String,
462    /// Target currency (optional).
463    pub tgt_ccy: String,
464    /// Order state.
465    pub state: String,
466    /// Average price (optional).
467    pub avg_px: String,
468    /// Execution fee.
469    pub fee: String,
470    /// Fee currency.
471    pub fee_ccy: String,
472    /// Filled size (optional).
473    pub fill_sz: String,
474    /// Fill price (optional).
475    pub fill_px: String,
476    /// Trade ID (optional).
477    pub trade_id: Ustr,
478    /// Fill time, Unix timestamp in milliseconds.
479    #[serde(deserialize_with = "deserialize_string_to_u64")]
480    pub fill_time: u64,
481    /// Accumulated filled size.
482    pub acc_fill_sz: String,
483    /// Fill fee (optional, may be omitted).
484    #[serde(default)]
485    pub fill_fee: Option<String>,
486    /// Request ID (optional).
487    #[serde(default)]
488    pub req_id: Option<Ustr>,
489    /// Cancelled filled size (optional).
490    #[serde(default)]
491    pub cancel_fill_sz: Option<String>,
492    /// Cancelled total size (optional).
493    #[serde(default)]
494    pub cancel_total_sz: Option<String>,
495    /// Fee discount (optional).
496    #[serde(default)]
497    pub fee_discount: Option<String>,
498    /// Category (optional).
499    pub category: String,
500    /// Last update time, Unix timestamp in milliseconds.
501    #[serde(deserialize_with = "deserialize_string_to_u64")]
502    pub u_time: u64,
503    /// Creation time.
504    #[serde(deserialize_with = "deserialize_string_to_u64")]
505    pub c_time: u64,
506}
507
508/// Represents a transaction detail (fill) from `GET /api/v5/trade/fills`.
509#[derive(Clone, Debug, Serialize, Deserialize)]
510#[serde(rename_all = "camelCase")]
511pub struct OKXTransactionDetail {
512    /// Product type (SPOT, MARGIN, SWAP, FUTURES, OPTION).
513    pub inst_type: OKXInstrumentType,
514    /// Instrument ID, e.g. "BTC-USDT".
515    pub inst_id: Ustr,
516    /// Trade ID.
517    pub trade_id: Ustr,
518    /// Order ID.
519    pub ord_id: Ustr,
520    /// Client order ID.
521    pub cl_ord_id: Ustr,
522    /// Bill ID.
523    pub bill_id: Ustr,
524    /// Last filled price.
525    pub fill_px: String,
526    /// Last filled quantity.
527    pub fill_sz: String,
528    /// Trade side: buy or sell.
529    pub side: OKXSide,
530    /// Execution type.
531    pub exec_type: OKXExecType,
532    /// Fee currency.
533    pub fee_ccy: String,
534    /// Fee amount.
535    #[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
536    pub fee: Option<String>,
537    /// Timestamp, Unix timestamp format in milliseconds.
538    #[serde(deserialize_with = "deserialize_string_to_u64")]
539    pub ts: u64,
540}
541
542/// Represents a single historical position record from `GET /api/v5/account/positions-history`.
543#[derive(Clone, Debug, Serialize, Deserialize)]
544#[serde(rename_all = "camelCase")]
545pub struct OKXPositionHistory {
546    /// Instrument type (e.g. "SWAP", "FUTURES", etc.).
547    pub inst_type: OKXInstrumentType,
548    /// Instrument ID (e.g. "BTC-USD-SWAP").
549    pub inst_id: Ustr,
550    /// Margin mode: e.g. "cross", "isolated".
551    pub mgn_mode: OKXMarginMode,
552    /// The type of the last close, e.g. "1" (close partially), "2" (close all), etc.
553    /// See OKX docs for the meaning of each numeric code.
554    #[serde(rename = "type")]
555    pub r#type: Ustr,
556    /// Creation time of the position (Unix timestamp in milliseconds).
557    pub c_time: String,
558    /// Last update time, Unix timestamp in milliseconds.
559    #[serde(deserialize_with = "deserialize_string_to_u64")]
560    pub u_time: u64,
561    /// Average price of opening position.
562    pub open_avg_px: String,
563    /// Average price of closing position (if applicable).
564    #[serde(skip_serializing_if = "Option::is_none")]
565    pub close_avg_px: Option<String>,
566    /// The position ID.
567    #[serde(default, deserialize_with = "deserialize_empty_ustr_as_none")]
568    pub pos_id: Option<Ustr>,
569    /// Max quantity of the position at open time.
570    #[serde(skip_serializing_if = "Option::is_none")]
571    pub open_max_pos: Option<String>,
572    /// Cumulative closed volume of the position.
573    #[serde(skip_serializing_if = "Option::is_none")]
574    pub close_total_pos: Option<String>,
575    /// Realized profit and loss (only for FUTURES/SWAP/OPTION).
576    #[serde(skip_serializing_if = "Option::is_none")]
577    pub realized_pnl: Option<String>,
578    /// Accumulated fee for the position.
579    #[serde(skip_serializing_if = "Option::is_none")]
580    pub fee: Option<String>,
581    /// Accumulated funding fee (for perpetual swaps).
582    #[serde(skip_serializing_if = "Option::is_none")]
583    pub funding_fee: Option<String>,
584    /// Accumulated liquidation penalty. Negative if there was a penalty.
585    #[serde(skip_serializing_if = "Option::is_none")]
586    pub liq_penalty: Option<String>,
587    /// Profit and loss (realized or unrealized depending on status).
588    #[serde(skip_serializing_if = "Option::is_none")]
589    pub pnl: Option<String>,
590    /// PnL ratio.
591    #[serde(skip_serializing_if = "Option::is_none")]
592    pub pnl_ratio: Option<String>,
593    /// Position side: "long" / "short" / "net".
594    pub pos_side: OKXPositionSide,
595    /// Leverage used (the JSON field is "lev", but we rename it in Rust).
596    pub lever: String,
597    /// Direction: "long" or "short" (only for MARGIN/FUTURES/SWAP/OPTION).
598    #[serde(skip_serializing_if = "Option::is_none")]
599    pub direction: Option<String>,
600    /// Trigger mark price. Populated if `type` indicates liquidation or ADL.
601    #[serde(skip_serializing_if = "Option::is_none")]
602    pub trigger_px: Option<String>,
603    /// The underlying (e.g. "BTC-USD" for futures or swap).
604    #[serde(skip_serializing_if = "Option::is_none")]
605    pub uly: Option<String>,
606    /// Currency (e.g. "BTC"). May or may not appear in all responses.
607    #[serde(skip_serializing_if = "Option::is_none")]
608    pub ccy: Option<String>,
609}