Skip to main content

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}