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}