rust_okx/ws/model/grid.rs
1//! Trading bot channel models (`grid-orders-*`, `grid-positions`, `recurring-buy`, etc.).
2//!
3//! Private channels; login required.
4
5use serde::Deserialize;
6
7use super::ExtraFields;
8use crate::model::NumberString;
9
10/// Rebate transfer entry nested in [`GridOrderUpdate`].
11#[derive(Debug, Clone, Default, Deserialize)]
12#[serde(rename_all = "camelCase")]
13#[non_exhaustive]
14pub struct RebateTrans {
15 /// Rebate amount.
16 #[serde(default)]
17 pub rebate: NumberString,
18 /// Rebate currency.
19 #[serde(default)]
20 pub rebate_ccy: String,
21 /// Unrecognized fields retained for forward compatibility.
22 #[serde(flatten, default)]
23 pub extra: ExtraFields,
24}
25
26/// Trigger parameter entry nested in [`GridOrderUpdate`].
27///
28/// Fields marked "rsi only" are populated when `trigger_strategy` is `rsi`.
29/// Fields marked "price only" are populated when `trigger_strategy` is `price`.
30/// `stop_type` is populated only when `trigger_action` is `stop`.
31#[derive(Debug, Clone, Default, Deserialize)]
32#[serde(rename_all = "camelCase")]
33#[non_exhaustive]
34pub struct TriggerParam {
35 /// Trigger action: `start` or `stop`.
36 #[serde(default)]
37 pub trigger_action: String,
38 /// Trigger strategy: `instant`, `price`, or `rsi`.
39 #[serde(default)]
40 pub trigger_strategy: String,
41 /// Delay in seconds after the trigger condition is met.
42 #[serde(default)]
43 pub delay_seconds: String,
44 /// Actual time the action was triggered (Unix milliseconds).
45 #[serde(default)]
46 pub trigger_time: String,
47 /// How the action was triggered: `manual` or `auto`.
48 #[serde(default)]
49 pub trigger_type: String,
50 /// K-line type (rsi only): e.g. `3m`, `1H`, `1D`.
51 #[serde(default)]
52 pub timeframe: String,
53 /// RSI threshold integer 1–100 (rsi only).
54 #[serde(default)]
55 pub thold: String,
56 /// RSI trigger condition (rsi only): `cross_up`, `cross_down`, `above`, `below`, `cross`.
57 #[serde(default)]
58 pub trigger_cond: String,
59 /// RSI time period (rsi only): `14`.
60 #[serde(default)]
61 pub time_period: String,
62 /// Trigger price (price only).
63 #[serde(default)]
64 pub trigger_px: String,
65 /// Stop type when `trigger_action` is `stop`.
66 ///
67 /// Spot grid: `1` = sell base currency, `2` = keep base currency.
68 /// Contract grid: `1` = market close all positions, `2` = keep positions.
69 #[serde(default)]
70 pub stop_type: String,
71 /// Unrecognized fields retained for forward compatibility.
72 #[serde(flatten, default)]
73 pub extra: ExtraFields,
74}
75
76/// Spot/contract grid-order channel row.
77///
78/// Returned by both `grid-orders-spot` and `grid-orders-contract` channels.
79/// Spot-only and contract-only fields default to zero/empty when not present.
80///
81/// OKX docs: <https://www.okx.com/docs-v5/en/#trading-bot-websocket-grid-orders-channel>
82#[derive(Debug, Clone, Default, Deserialize)]
83#[serde(rename_all = "camelCase")]
84#[non_exhaustive]
85pub struct GridOrderUpdate {
86 /// Instrument ID, e.g., `BTC-USDT`.
87 #[serde(default)]
88 pub inst_id: String,
89 /// OKX-assigned algo order ID.
90 #[serde(default)]
91 pub algo_id: String,
92 /// Client-supplied algo order ID.
93 #[serde(default)]
94 pub algo_cl_ord_id: String,
95 /// Instrument type, e.g., `SPOT`, `SWAP`, `FUTURES`.
96 #[serde(default)]
97 pub inst_type: String,
98 /// Algo order type: `grid` (spot grid) or `contract_grid` (contract grid).
99 #[serde(default)]
100 pub algo_ord_type: String,
101 /// Current state: `starting`, `running`, `stopping`, `stopped`.
102 ///
103 /// Contract grid also has `no_close_position`.
104 #[serde(default)]
105 pub state: String,
106 /// Rebate transfer info.
107 #[serde(default)]
108 pub rebate_trans: Vec<RebateTrans>,
109 /// Trigger parameters for start/stop conditions.
110 #[serde(default)]
111 pub trigger_params: Vec<TriggerParam>,
112 /// Upper price of the grid range.
113 #[serde(default)]
114 pub max_px: NumberString,
115 /// Lower price of the grid range.
116 #[serde(default)]
117 pub min_px: NumberString,
118 /// Number of grid levels.
119 #[serde(default)]
120 pub grid_num: NumberString,
121 /// Grid spacing type: `1` = arithmetic, `2` = geometric.
122 #[serde(default)]
123 pub run_type: String,
124 /// Take-profit trigger price.
125 #[serde(default)]
126 pub tp_trigger_px: NumberString,
127 /// Stop-loss trigger price.
128 #[serde(default)]
129 pub sl_trigger_px: NumberString,
130 /// Number of trades executed.
131 #[serde(default)]
132 pub trade_num: NumberString,
133 /// Number of arbitrage cycles completed.
134 #[serde(default)]
135 pub arbitrage_num: NumberString,
136 /// Investment amount per grid.
137 #[serde(default)]
138 pub single_amt: NumberString,
139 /// Estimated minimum profit margin per grid.
140 #[serde(default)]
141 pub per_min_profit_rate: NumberString,
142 /// Estimated maximum profit margin per grid.
143 #[serde(default)]
144 pub per_max_profit_rate: NumberString,
145 /// Price at strategy launch.
146 #[serde(default)]
147 pub run_px: NumberString,
148 /// Total profit and loss since strategy start.
149 #[serde(default)]
150 pub total_pnl: NumberString,
151 /// P&L ratio since strategy start.
152 #[serde(default)]
153 pub pnl_ratio: NumberString,
154 /// Accumulated investment amount.
155 #[serde(default)]
156 pub investment: NumberString,
157 /// Profit from completed grid cycles.
158 #[serde(default)]
159 pub grid_profit: NumberString,
160 /// Unrealized (floating) P&L.
161 #[serde(default)]
162 pub float_profit: NumberString,
163 /// Total annualized rate of return.
164 #[serde(default)]
165 pub total_annualized_rate: NumberString,
166 /// Grid annualized rate of return.
167 #[serde(default)]
168 pub annualized_rate: NumberString,
169 /// Algo order stop reason code.
170 ///
171 /// `0` none, `1` manual, `2` take-profit, `3` stop-loss, `4` risk control,
172 /// `5` delivery, `6` signal.
173 #[serde(default)]
174 pub cancel_type: String,
175 /// Stop type when the strategy ended.
176 ///
177 /// Spot: `1` = sell base, `2` = keep base.
178 /// Contract: `1` = market close all, `2` = keep positions.
179 #[serde(default)]
180 pub stop_type: String,
181 /// Total count of pending sub-orders.
182 #[serde(default)]
183 pub active_ord_num: NumberString,
184 /// Order tag.
185 #[serde(default)]
186 pub tag: String,
187 /// Profit sharing ratio (empty for non-copy orders).
188 #[serde(default)]
189 pub profit_sharing_ratio: String,
190 /// Copy order type: `0` normal, `1` copy without sharing, `2` copy with sharing, `3` lead.
191 #[serde(default)]
192 pub copy_type: String,
193 /// Quote currency used for trading.
194 #[serde(default)]
195 pub trade_quote_ccy: String,
196 /// Strategy creation time (Unix milliseconds).
197 #[serde(default)]
198 pub c_time: NumberString,
199 /// Last update time (Unix milliseconds).
200 #[serde(default)]
201 pub u_time: NumberString,
202 /// Push time (Unix milliseconds).
203 #[serde(default)]
204 pub p_time: NumberString,
205
206 // ── Spot-grid-only fields ────────────────────────────────────────────────
207 /// Quote currency investment amount (spot only).
208 #[serde(default)]
209 pub quote_sz: NumberString,
210 /// Base currency investment amount (spot only).
211 #[serde(default)]
212 pub base_sz: NumberString,
213 /// Current quote currency holdings (spot only).
214 #[serde(default)]
215 pub cur_quote_sz: NumberString,
216 /// Current base currency holdings (spot only).
217 #[serde(default)]
218 pub cur_base_sz: NumberString,
219 /// Available profit in quote currency (spot only).
220 #[serde(default)]
221 pub profit: NumberString,
222 /// Stop result (spot only): `0` default, `1` sold at market, `-1` failed to sell.
223 #[serde(default)]
224 pub stop_result: String,
225
226 // ── Contract-grid-only fields ────────────────────────────────────────────
227 /// Contract grid type: `long`, `short`, or `neutral` (contract only).
228 #[serde(default)]
229 pub direction: String,
230 /// Whether a position was opened at strategy activation (contract only).
231 #[serde(default)]
232 pub base_pos: bool,
233 /// Used margin in USDT (contract only).
234 #[serde(default)]
235 pub sz: NumberString,
236 /// Leverage (contract only).
237 #[serde(default)]
238 pub lever: NumberString,
239 /// Actual leverage (contract only).
240 #[serde(default)]
241 pub actual_lever: NumberString,
242 /// Estimated liquidation price (contract only).
243 #[serde(default)]
244 pub liq_px: NumberString,
245 /// Margin frozen by pending orders (contract only).
246 #[serde(default)]
247 pub ord_frozen: NumberString,
248 /// Available margin (contract only).
249 #[serde(default)]
250 pub avail_eq: NumberString,
251 /// Total equity of the strategy account (contract only).
252 #[serde(default)]
253 pub eq: NumberString,
254 /// Take-profit ratio, e.g. `0.1` = 10% (contract only).
255 #[serde(default)]
256 pub tp_ratio: NumberString,
257 /// Stop-loss ratio, e.g. `0.1` = 10% (contract only).
258 #[serde(default)]
259 pub sl_ratio: NumberString,
260 /// Accumulated trading fee (contract only).
261 #[serde(default)]
262 pub fee: NumberString,
263 /// Accumulated funding fee (contract only).
264 #[serde(default)]
265 pub funding_fee: NumberString,
266
267 /// Unrecognized fields retained for forward compatibility.
268 #[serde(flatten, default)]
269 pub extra: ExtraFields,
270}
271
272/// Backward-compatible name for grid-order updates.
273pub type TradingBotUpdate = GridOrderUpdate;
274
275/// `grid-positions` channel row.
276///
277/// OKX docs: <https://www.okx.com/docs-v5/en/#trading-bot-websocket-grid-positions-channel>
278#[derive(Debug, Clone, Default, Deserialize)]
279#[serde(rename_all = "camelCase")]
280#[non_exhaustive]
281pub struct GridPositionUpdate {
282 /// OKX-assigned algo order ID of the grid strategy.
283 #[serde(default)]
284 pub algo_id: String,
285 /// Client-supplied algo order ID.
286 #[serde(default)]
287 pub algo_cl_ord_id: String,
288 /// Instrument ID, e.g., `BTC-USDT-SWAP`.
289 #[serde(default)]
290 pub inst_id: String,
291 /// Instrument type, e.g., `SWAP`, `FUTURES`.
292 #[serde(default)]
293 pub inst_type: String,
294 /// Margin currency.
295 #[serde(default)]
296 pub ccy: String,
297 /// Margin mode: `cross` or `isolated`.
298 #[serde(default)]
299 pub mgn_mode: String,
300 /// Position side: `long`, `short`, or `net`.
301 #[serde(default)]
302 pub pos_side: String,
303 /// Position quantity (number of contracts).
304 #[serde(default)]
305 pub pos: NumberString,
306 /// Average entry price of the position.
307 #[serde(default)]
308 pub avg_px: NumberString,
309 /// Unrealized profit and loss.
310 #[serde(default)]
311 pub upl: NumberString,
312 /// Unrealized profit and loss ratio.
313 #[serde(default)]
314 pub upl_ratio: NumberString,
315 /// Leverage.
316 #[serde(default)]
317 pub lever: NumberString,
318 /// Estimated liquidation price.
319 #[serde(default)]
320 pub liq_px: NumberString,
321 /// Mark price.
322 #[serde(default)]
323 pub mark_px: NumberString,
324 /// Latest traded price.
325 #[serde(default)]
326 pub last: NumberString,
327 /// Notional value of the position in USD.
328 #[serde(default)]
329 pub notional_usd: NumberString,
330 /// ADL signal area (1–5; lower = weaker deleveraging risk).
331 #[serde(default)]
332 pub adl: String,
333 /// Maintenance margin ratio.
334 #[serde(default)]
335 pub mgn_ratio: NumberString,
336 /// Initial margin requirement.
337 #[serde(default)]
338 pub imr: NumberString,
339 /// Maintenance margin requirement.
340 #[serde(default)]
341 pub mmr: NumberString,
342 /// Algo order created time (Unix milliseconds).
343 #[serde(default)]
344 pub c_time: NumberString,
345 /// Last update time (Unix milliseconds).
346 #[serde(default)]
347 pub u_time: NumberString,
348 /// Push time (Unix milliseconds).
349 #[serde(default)]
350 pub p_time: NumberString,
351 /// Unrecognized fields retained for forward compatibility.
352 #[serde(flatten, default)]
353 pub extra: ExtraFields,
354}
355
356/// `grid-sub-orders` channel row.
357///
358/// OKX docs: <https://www.okx.com/docs-v5/en/#trading-bot-websocket-grid-sub-orders-channel>
359#[derive(Debug, Clone, Default, Deserialize)]
360#[serde(rename_all = "camelCase")]
361#[non_exhaustive]
362pub struct GridSubOrderUpdate {
363 /// Parent algo order ID of the grid strategy.
364 #[serde(default)]
365 pub algo_id: String,
366 /// Client-supplied algo order ID.
367 #[serde(default)]
368 pub algo_cl_ord_id: String,
369 /// Instrument ID, e.g., `BTC-USDT`.
370 #[serde(default)]
371 pub inst_id: String,
372 /// Instrument type, e.g., `SWAP`, `SPOT`.
373 #[serde(default)]
374 pub inst_type: String,
375 /// Algo order type: `grid` or `contract_grid`.
376 #[serde(default)]
377 pub algo_ord_type: String,
378 /// Group ID.
379 #[serde(default)]
380 pub group_id: String,
381 /// OKX-assigned order ID of this sub-order.
382 #[serde(default)]
383 pub ord_id: String,
384 /// Sub-order trade mode: `cross`, `isolated`, or `cash`.
385 #[serde(default)]
386 pub td_mode: String,
387 /// Order tag.
388 #[serde(default)]
389 pub tag: String,
390 /// Order type: `market`, `limit`, or `ioc`.
391 #[serde(default)]
392 pub ord_type: String,
393 /// Order quantity to buy or sell.
394 #[serde(default)]
395 pub sz: NumberString,
396 /// Sub-order state: `live`, `partially_filled`, `filled`, `canceled`, `cancelling`.
397 #[serde(default)]
398 pub state: String,
399 /// Order side: `buy` or `sell`.
400 #[serde(default)]
401 pub side: String,
402 /// Order price.
403 #[serde(default)]
404 pub px: NumberString,
405 /// Fee amount.
406 #[serde(default)]
407 pub fee: NumberString,
408 /// Fee currency.
409 #[serde(default)]
410 pub fee_ccy: String,
411 /// Rebate amount.
412 #[serde(default)]
413 pub rebate: NumberString,
414 /// Rebate currency.
415 #[serde(default)]
416 pub rebate_ccy: String,
417 /// Average fill price.
418 #[serde(default)]
419 pub avg_px: NumberString,
420 /// Accumulated filled quantity.
421 #[serde(default)]
422 pub acc_fill_sz: NumberString,
423 /// Position side: `net`.
424 #[serde(default)]
425 pub pos_side: String,
426 /// Profit and loss for this sub-order.
427 #[serde(default)]
428 pub pnl: NumberString,
429 /// Contract value (futures/swap/option only).
430 #[serde(default)]
431 pub ct_val: NumberString,
432 /// Leverage.
433 #[serde(default)]
434 pub lever: NumberString,
435 /// Sub-order created time (Unix milliseconds).
436 #[serde(default)]
437 pub c_time: NumberString,
438 /// Last update time (Unix milliseconds).
439 #[serde(default)]
440 pub u_time: NumberString,
441 /// Push time (Unix milliseconds).
442 #[serde(default)]
443 pub p_time: NumberString,
444 /// Unrecognized fields retained for forward compatibility.
445 #[serde(flatten, default)]
446 pub extra: ExtraFields,
447}
448
449/// Currency allocation entry nested in [`RecurringBuyOrderUpdate`].
450#[derive(Debug, Clone, Default, Deserialize)]
451#[serde(rename_all = "camelCase")]
452#[non_exhaustive]
453pub struct RecurringBuyAllocation {
454 /// Currency code, e.g., `BTC`.
455 #[serde(default)]
456 pub ccy: String,
457 /// Allocation ratio for this currency (0–1).
458 #[serde(default)]
459 pub ratio: NumberString,
460 /// Minimum price of price range (`""` = no limit).
461 #[serde(default)]
462 pub min_px: String,
463 /// Maximum price of price range (`""` = no limit).
464 #[serde(default)]
465 pub max_px: String,
466 /// Accumulated quantity in units of this currency.
467 #[serde(default)]
468 pub total_amt: NumberString,
469 /// Profit in units of `investment_ccy`.
470 #[serde(default)]
471 pub profit: NumberString,
472 /// Average recurring buy price, quoted in `investment_ccy`.
473 #[serde(default)]
474 pub avg_px: NumberString,
475 /// Current market price, quoted in `investment_ccy`.
476 #[serde(default)]
477 pub px: NumberString,
478 /// Unrecognized fields retained for forward compatibility.
479 #[serde(flatten, default)]
480 pub extra: ExtraFields,
481}
482
483/// `algo-recurring-buy` channel row.
484///
485/// OKX docs: <https://www.okx.com/docs-v5/en/#trading-bot-websocket-recurring-buy-orders-channel>
486#[derive(Debug, Clone, Default, Deserialize)]
487#[serde(rename_all = "camelCase")]
488#[non_exhaustive]
489pub struct RecurringBuyOrderUpdate {
490 /// OKX-assigned algo order ID.
491 #[serde(default)]
492 pub algo_id: String,
493 /// Client-supplied algo order ID.
494 #[serde(default)]
495 pub algo_cl_ord_id: String,
496 /// Instrument type covered by this strategy (typically `SPOT`).
497 #[serde(default)]
498 pub inst_type: String,
499 /// Algo order type: `recurring`.
500 #[serde(default)]
501 pub algo_ord_type: String,
502 /// Strategy state: `running`, `stopping`, `stopped`, or `pause`.
503 #[serde(default)]
504 pub state: String,
505 /// User-defined strategy name (up to 40 characters).
506 #[serde(default)]
507 pub stgy_name: String,
508 /// List of currencies and their allocation ratios.
509 #[serde(default)]
510 pub recurring_list: Vec<RecurringBuyAllocation>,
511 /// Recurrence period: `monthly`, `weekly`, `daily`, or `hourly`.
512 #[serde(default)]
513 pub period: String,
514 /// Day of month (1–28) or week (1–7) for monthly/weekly periods.
515 #[serde(default)]
516 pub recurring_day: String,
517 /// Hour interval for hourly period: `1`, `4`, `8`, or `12`.
518 #[serde(default)]
519 pub recurring_hour: String,
520 /// Clock hour within the day to execute (0–23).
521 #[serde(default)]
522 pub recurring_time: String,
523 /// UTC time zone offset, e.g., `8` for UTC+8.
524 #[serde(default)]
525 pub time_zone: String,
526 /// Quote-currency amount spent per execution cycle.
527 #[serde(default)]
528 pub amt: NumberString,
529 /// Accumulated quote-currency amount invested to date.
530 #[serde(default)]
531 pub investment_amt: NumberString,
532 /// Investment currency: `USDT` or `USDC`.
533 #[serde(default)]
534 pub investment_ccy: String,
535 /// Next scheduled investment time (Unix milliseconds).
536 #[serde(default)]
537 pub next_invest_time: NumberString,
538 /// Total profit and loss since strategy start.
539 #[serde(default)]
540 pub total_pnl: NumberString,
541 /// Total annualized rate of yield.
542 #[serde(default)]
543 pub total_ann_rate: NumberString,
544 /// Rate of yield (P&L ratio).
545 #[serde(default)]
546 pub pnl_ratio: NumberString,
547 /// Market value of purchased assets in USDT.
548 #[serde(default)]
549 pub mkt_cap: NumberString,
550 /// Number of completed buy cycles.
551 #[serde(default)]
552 pub cycles: NumberString,
553 /// Order tag.
554 #[serde(default)]
555 pub tag: String,
556 /// Quote currency used for trading.
557 #[serde(default)]
558 pub trade_quote_ccy: String,
559 /// Recurring buy time type.
560 #[serde(default)]
561 pub recurring_time_type: String,
562 /// Custom recurring buy minutes.
563 #[serde(default)]
564 pub recurring_time_minutes: String,
565 /// Strategy creation time (Unix milliseconds).
566 #[serde(default)]
567 pub c_time: NumberString,
568 /// Last update time (Unix milliseconds).
569 #[serde(default)]
570 pub u_time: NumberString,
571 /// Push time (Unix milliseconds).
572 #[serde(default)]
573 pub p_time: NumberString,
574 /// Unrecognized fields retained for forward compatibility.
575 #[serde(flatten, default)]
576 pub extra: ExtraFields,
577}
578
579/// `copytrading-lead-notification` channel row.
580///
581/// OKX docs: <https://www.okx.com/docs-v5/en/#copy-trading-websocket-copy-trading-notification-channel>
582#[derive(Debug, Clone, Default, Deserialize)]
583#[serde(rename_all = "camelCase")]
584#[non_exhaustive]
585pub struct CopyTradingNotification {
586 /// Notification type.
587 ///
588 /// `1` lead trading failed: max position limit reached.
589 /// `2` lead trading failed: max daily lead count reached.
590 /// `3` lead trading failed: USDT equity below minimum.
591 #[serde(default)]
592 pub info_type: String,
593 /// Instrument type, e.g., `SWAP`.
594 #[serde(default)]
595 pub inst_type: String,
596 /// Instrument ID, e.g., `BTC-USDT-SWAP`.
597 #[serde(default)]
598 pub inst_id: String,
599 /// Order side: `buy` or `sell`.
600 #[serde(default)]
601 pub side: String,
602 /// Position side: `long`, `short`, or `net`.
603 #[serde(default)]
604 pub pos_side: String,
605 /// Lead position ID assigned by OKX.
606 #[serde(default)]
607 pub sub_pos_id: String,
608 /// Unique code of the lead trader.
609 #[serde(default)]
610 pub unique_code: String,
611 /// Maximum daily number of lead trades.
612 #[serde(default)]
613 pub max_lead_trader_num: NumberString,
614 /// Minimum USDT equity required for lead trading.
615 #[serde(default)]
616 pub min_lead_eq: NumberString,
617 /// Unrecognized fields retained for forward compatibility.
618 #[serde(flatten, default)]
619 pub extra: ExtraFields,
620}