1use serde::Deserialize;
6
7use super::ExtraFields;
8use crate::model::NumberString;
9
10fn array_value(values: &[NumberString], i: usize) -> NumberString {
11 values.get(i).cloned().unwrap_or_default()
12}
13
14#[derive(Debug, Clone, Default, Deserialize)]
16#[serde(rename_all = "camelCase")]
17#[non_exhaustive]
18pub struct InstrumentUpcomingParamChange {
19 #[serde(default)]
21 pub param: String,
22 #[serde(default)]
24 pub new_value: String,
25 #[serde(default)]
27 pub eff_time: NumberString,
28 #[serde(flatten, default)]
30 pub extra: ExtraFields,
31}
32
33#[derive(Debug, Clone, Default, Deserialize)]
37#[serde(rename_all = "camelCase")]
38#[non_exhaustive]
39pub struct InstrumentUpdate {
40 #[serde(default)]
42 pub inst_type: String,
43 #[serde(default)]
45 pub series_id: String,
46 #[serde(default)]
48 pub inst_id: String,
49 #[serde(default)]
51 pub inst_id_code: NumberString,
52 #[serde(default)]
54 pub uly: String,
55 #[serde(default)]
57 pub inst_family: String,
58 #[serde(default)]
60 pub category: String,
61 #[serde(default)]
63 pub base_ccy: String,
64 #[serde(default)]
66 pub quote_ccy: String,
67 #[serde(default)]
69 pub settle_ccy: String,
70 #[serde(default)]
72 pub ct_val: NumberString,
73 #[serde(default)]
75 pub ct_mult: NumberString,
76 #[serde(default)]
78 pub ct_val_ccy: String,
79 #[serde(default)]
81 pub opt_type: String,
82 #[serde(default)]
84 pub stk: NumberString,
85 #[serde(default)]
87 pub list_time: NumberString,
88 #[serde(default)]
90 pub cont_td_sw_time: NumberString,
91 #[serde(default)]
93 pub pre_mkt_sw_time: NumberString,
94 #[serde(default)]
96 pub exp_time: NumberString,
97 #[serde(default)]
99 pub lever: NumberString,
100 #[serde(default)]
102 pub tick_sz: NumberString,
103 #[serde(default)]
105 pub lot_sz: NumberString,
106 #[serde(default)]
108 pub min_sz: NumberString,
109 #[serde(default)]
111 pub ct_type: String,
112 #[serde(default)]
114 pub alias: String,
115 #[serde(default)]
119 pub state: String,
120 #[serde(default)]
124 pub open_type: String,
125 #[serde(default)]
127 pub rule_type: String,
128 #[serde(default)]
130 pub max_lmt_sz: NumberString,
131 #[serde(default)]
133 pub max_lmt_amt: NumberString,
134 #[serde(default)]
136 pub max_mkt_sz: NumberString,
137 #[serde(default)]
139 pub max_mkt_amt: NumberString,
140 #[serde(default)]
142 pub max_twap_sz: NumberString,
143 #[serde(default)]
145 pub max_iceberg_sz: NumberString,
146 #[serde(default)]
148 pub max_trigger_sz: NumberString,
149 #[serde(default)]
151 pub max_stop_sz: NumberString,
152 #[serde(default)]
154 pub auction_end_time: NumberString,
155 #[serde(default)]
157 pub future_settlement: bool,
158 #[serde(default)]
160 pub trade_quote_ccy_list: Vec<String>,
161 #[serde(default)]
163 pub inst_category: String,
164 #[serde(default)]
166 pub pos_lmt_amt: NumberString,
167 #[serde(default)]
169 pub pos_lmt_pct: NumberString,
170 #[serde(default)]
172 pub long_pos_remaining_quota: NumberString,
173 #[serde(default)]
175 pub short_pos_remaining_quota: NumberString,
176 #[serde(default)]
178 pub max_plat_oi_lmt: NumberString,
179 #[serde(default)]
181 pub group_id: String,
182 #[serde(default)]
184 pub upc_chg: Vec<InstrumentUpcomingParamChange>,
185 #[serde(flatten, default)]
187 pub extra: ExtraFields,
188}
189
190#[derive(Debug, Clone, Default, Deserialize)]
194#[serde(rename_all = "camelCase")]
195#[non_exhaustive]
196pub struct EventContractMarketUpdate {
197 #[serde(default)]
199 pub series_id: String,
200 #[serde(default)]
202 pub event_id: String,
203 #[serde(default)]
205 pub market_id: String,
206 #[serde(default)]
208 pub inst_id: String,
209 #[serde(default)]
211 pub title: String,
212 #[serde(default)]
214 pub state: String,
215 #[serde(default)]
217 pub category: String,
218 #[serde(default)]
220 pub rule_type: String,
221 #[serde(default)]
223 pub outcome: String,
224 #[serde(default)]
226 pub floor_strike: NumberString,
227 #[serde(default)]
229 pub settle_value: NumberString,
230 #[serde(default)]
232 pub disputed: bool,
233 #[serde(default)]
235 pub tick_sz: NumberString,
236 #[serde(default)]
238 pub min_sz: NumberString,
239 #[serde(default)]
241 pub list_time: NumberString,
242 #[serde(default)]
244 pub exp_time: NumberString,
245 #[serde(default)]
247 pub fix_time: NumberString,
248 #[serde(default)]
250 pub u_time: NumberString,
251 #[serde(flatten, default)]
253 pub extra: ExtraFields,
254}
255
256#[derive(Debug, Clone, Default, Deserialize)]
260#[serde(rename_all = "camelCase")]
261#[non_exhaustive]
262pub struct OpenInterestUpdate {
263 #[serde(default)]
265 pub inst_type: String,
266 #[serde(default)]
268 pub inst_id: String,
269 #[serde(default)]
271 pub oi: NumberString,
272 #[serde(default)]
274 pub oi_ccy: NumberString,
275 #[serde(default)]
277 pub oi_usd: NumberString,
278 #[serde(default)]
280 pub ts: NumberString,
281 #[serde(flatten, default)]
283 pub extra: ExtraFields,
284}
285
286#[derive(Debug, Clone, Default, Deserialize)]
290#[serde(rename_all = "camelCase")]
291#[non_exhaustive]
292pub struct FundingRateUpdate {
293 #[serde(default)]
295 pub inst_type: String,
296 #[serde(default)]
298 pub inst_id: String,
299 #[serde(default)]
301 pub funding_rate: NumberString,
302 #[serde(default)]
304 pub next_funding_rate: NumberString,
305 #[serde(default)]
307 pub funding_time: NumberString,
308 #[serde(default)]
310 pub next_funding_time: NumberString,
311 #[serde(default)]
313 pub min_funding_rate: NumberString,
314 #[serde(default)]
316 pub max_funding_rate: NumberString,
317 #[serde(default)]
319 pub method: String,
320 #[serde(default)]
322 pub formula_type: String,
323 #[serde(default)]
325 pub premium: NumberString,
326 #[serde(default)]
328 pub interest_rate: NumberString,
329 #[serde(default)]
331 pub impact_value: NumberString,
332 #[serde(default)]
334 pub sett_state: String,
335 #[serde(default)]
337 pub sett_funding_rate: NumberString,
338 #[serde(default)]
340 pub ts: NumberString,
341 #[serde(flatten, default)]
343 pub extra: ExtraFields,
344}
345
346#[derive(Debug, Clone, Default, Deserialize)]
350#[serde(rename_all = "camelCase")]
351#[non_exhaustive]
352pub struct PriceLimitUpdate {
353 #[serde(default)]
355 pub inst_type: String,
356 #[serde(default)]
358 pub inst_id: String,
359 #[serde(default)]
361 pub buy_lmt: NumberString,
362 #[serde(default)]
364 pub sell_lmt: NumberString,
365 #[serde(default)]
367 pub ts: NumberString,
368 #[serde(default)]
370 pub enabled: bool,
371 #[serde(flatten, default)]
373 pub extra: ExtraFields,
374}
375
376#[derive(Debug, Clone, Default, Deserialize)]
380#[serde(rename_all = "camelCase")]
381#[non_exhaustive]
382pub struct OptionSummaryUpdate {
383 #[serde(default)]
385 pub inst_type: String,
386 #[serde(default)]
388 pub inst_id: String,
389 #[serde(default)]
391 pub uly: String,
392 #[serde(default, rename = "deltaBS")]
394 pub delta_bs: NumberString,
395 pub delta: NumberString,
397 #[serde(default, rename = "gammaBS")]
399 pub gamma_bs: NumberString,
400 #[serde(default, rename = "gamma")]
402 pub gamma_pa: NumberString,
403 #[serde(default, rename = "vegaBS")]
405 pub vega_bs: NumberString,
406 #[serde(default, rename = "vega")]
408 pub vega_pa: NumberString,
409 #[serde(default, rename = "thetaBS")]
411 pub theta_bs: NumberString,
412 #[serde(default, rename = "theta")]
414 pub theta: NumberString,
415 #[serde(default)]
417 pub lever: NumberString,
418 #[serde(default)]
420 pub mark_vol: NumberString,
421 #[serde(default)]
423 pub bid_vol: NumberString,
424 #[serde(default)]
426 pub ask_vol: NumberString,
427 #[serde(default)]
429 pub real_vol: NumberString,
430 #[serde(default)]
432 pub vol_lv: NumberString,
433 #[serde(default)]
435 pub fwd_px: NumberString,
436 #[serde(default)]
438 pub ts: NumberString,
439 #[serde(flatten, default)]
441 pub extra: ExtraFields,
442}
443
444#[derive(Debug, Clone, Default, Deserialize)]
448#[serde(rename_all = "camelCase")]
449#[non_exhaustive]
450pub struct EstimatedPriceUpdate {
451 #[serde(default)]
453 pub inst_type: String,
454 #[serde(default)]
456 pub inst_id: String,
457 #[serde(default)]
459 pub settle_type: String,
460 #[serde(default)]
464 #[serde(alias = "settPx")]
465 pub settle_px: NumberString,
466 #[serde(default)]
468 pub ts: NumberString,
469 #[serde(flatten, default)]
471 pub extra: ExtraFields,
472}
473
474#[derive(Debug, Clone, Default, Deserialize)]
478#[serde(rename_all = "camelCase")]
479#[non_exhaustive]
480pub struct MarkPriceUpdate {
481 #[serde(default)]
483 pub inst_type: String,
484 #[serde(default)]
486 pub inst_id: String,
487 #[serde(default)]
489 pub mark_px: NumberString,
490 #[serde(default)]
492 pub ts: NumberString,
493 #[serde(flatten, default)]
495 pub extra: ExtraFields,
496}
497
498#[derive(Debug, Clone, Default, Deserialize)]
502#[serde(rename_all = "camelCase")]
503#[non_exhaustive]
504pub struct IndexTickerUpdate {
505 #[serde(default)]
507 pub inst_id: String,
508 #[serde(default)]
510 pub idx_px: NumberString,
511 #[serde(default)]
513 pub high24h: NumberString,
514 #[serde(default)]
516 pub open24h: NumberString,
517 #[serde(default)]
519 pub low24h: NumberString,
520 #[serde(default)]
522 pub sod_utc0: NumberString,
523 #[serde(default)]
525 pub sod_utc8: NumberString,
526 #[serde(default)]
528 pub ts: NumberString,
529 #[serde(flatten, default)]
531 pub extra: ExtraFields,
532}
533
534#[derive(Debug, Clone, Default, Deserialize)]
536#[serde(rename_all = "camelCase")]
537#[non_exhaustive]
538pub struct LiquidationOrderDetail {
539 #[serde(default)]
541 pub side: String,
542 #[serde(default)]
544 pub pos_side: String,
545 #[serde(default)]
547 pub bk_px: NumberString,
548 #[serde(default)]
550 pub sz: NumberString,
551 #[serde(default)]
553 pub bk_loss: NumberString,
554 #[serde(default)]
556 pub ccy: String,
557 #[serde(default)]
559 pub ts: NumberString,
560 #[serde(flatten, default)]
562 pub extra: ExtraFields,
563}
564
565#[derive(Debug, Clone, Default, Deserialize)]
569#[serde(rename_all = "camelCase")]
570#[non_exhaustive]
571pub struct LiquidationOrderUpdate {
572 #[serde(default)]
574 pub inst_id: String,
575 #[serde(default)]
577 pub inst_type: String,
578 #[serde(default)]
580 pub uly: String,
581 #[serde(default)]
583 pub inst_family: String,
584 #[serde(default)]
586 pub details: Vec<LiquidationOrderDetail>,
587 #[serde(flatten, default)]
589 pub extra: ExtraFields,
590}
591
592#[derive(Debug, Clone, Default, Deserialize)]
596#[serde(rename_all = "camelCase")]
597#[non_exhaustive]
598pub struct AdlWarningUpdate {
599 #[serde(default)]
601 pub inst_type: String,
602 #[serde(default)]
604 pub inst_family: String,
605 #[serde(default)]
607 pub ccy: String,
608 #[serde(default)]
610 pub max_bal: NumberString,
611 #[serde(default)]
613 pub adl_rec_bal: NumberString,
614 #[serde(default)]
616 pub bal: NumberString,
617 #[serde(default)]
619 pub max_bal_ts: NumberString,
620 #[serde(default)]
624 pub adl_type: String,
625 #[serde(default)]
629 pub state: String,
630 #[serde(default)]
632 pub adl_bal: NumberString,
633 #[serde(default)]
635 pub ts: NumberString,
636 #[serde(flatten, default)]
638 pub extra: ExtraFields,
639}
640
641#[derive(Debug, Clone, Default, Deserialize)]
645#[serde(rename_all = "camelCase")]
646#[non_exhaustive]
647pub struct EconomicCalendarUpdate {
648 #[serde(default)]
650 pub calendar_id: String,
651 #[serde(default)]
653 pub date: String,
654 #[serde(default)]
656 pub region: String,
657 #[serde(default)]
659 pub category: String,
660 #[serde(default)]
662 pub event: String,
663 #[serde(default)]
665 pub ref_date: String,
666 #[serde(default)]
668 pub actual: String,
669 #[serde(default)]
671 pub previous: String,
672 #[serde(default)]
674 pub forecast: String,
675 #[serde(default)]
677 pub importance: String,
678 #[serde(default)]
680 pub unit: String,
681 #[serde(default)]
683 pub ccy: String,
684 #[serde(default)]
686 pub prev_initial: String,
687 #[serde(default)]
689 pub ts: NumberString,
690 #[serde(flatten, default)]
692 pub extra: ExtraFields,
693}
694
695#[derive(Debug, Clone, Default, Deserialize)]
701#[serde(from = "Vec<NumberString>")]
702#[non_exhaustive]
703pub struct IndexCandleUpdate {
704 pub ts: NumberString,
706 pub o: NumberString,
708 pub h: NumberString,
710 pub l: NumberString,
712 pub c: NumberString,
714 pub confirm: NumberString,
716}
717
718impl From<Vec<NumberString>> for IndexCandleUpdate {
719 fn from(values: Vec<NumberString>) -> Self {
720 Self {
721 ts: array_value(&values, 0),
722 o: array_value(&values, 1),
723 h: array_value(&values, 2),
724 l: array_value(&values, 3),
725 c: array_value(&values, 4),
726 confirm: array_value(&values, 5),
727 }
728 }
729}
730
731pub type MarkPriceCandleUpdate = IndexCandleUpdate;
737
738#[cfg(test)]
739mod tests {
740 use super::*;
741
742 #[test]
743 fn option_summary_greek_fields_deserialize_from_okx_json_keys() {
744 let row: OptionSummaryUpdate = serde_json::from_str(
745 r#"{
746 "instType":"OPTION","instId":"BTC-USD-241013-70000-P","uly":"BTC-USD",
747 "delta":"-1.1180902625","deltaBS":"-0.9999672034",
748 "gamma":"2.2361957091","gammaBS":"0.0000000002",
749 "vega":"0.0000000001","vegaBS":"0.0000114332",
750 "theta":"0.0000032334","thetaBS":"28.2649858387",
751 "lever":"8.465747567","markVol":"0.3675503331","bidVol":"0",
752 "askVol":"1.1669998535","realVol":"","volLv":"0.2044711229",
753 "fwdPx":"62604.6993093463","ts":"1728703155650"
754 }"#,
755 )
756 .unwrap();
757 assert_eq!(row.delta.as_str(), "-1.1180902625");
758 assert_eq!(row.delta_bs.as_str(), "-0.9999672034");
759 assert_eq!(row.gamma_pa.as_str(), "2.2361957091");
760 assert_eq!(row.gamma_bs.as_str(), "0.0000000002");
761 assert_eq!(row.vega_pa.as_str(), "0.0000000001");
762 assert_eq!(row.vega_bs.as_str(), "0.0000114332");
763 assert_eq!(row.theta.as_str(), "0.0000032334");
764 assert_eq!(row.theta_bs.as_str(), "28.2649858387");
765 }
766
767 #[test]
768 fn index_candle_parses_6_element_array() {
769 let row: IndexCandleUpdate = serde_json::from_str(
770 r#"["1597026383085","3811.31","3811.31","3811.31","3811.31","0"]"#,
771 )
772 .unwrap();
773 assert_eq!(row.ts.as_str(), "1597026383085");
774 assert_eq!(row.o.as_str(), "3811.31");
775 assert_eq!(row.c.as_str(), "3811.31");
776 assert_eq!(row.confirm.as_str(), "0");
777 }
778
779 #[test]
780 fn mark_price_candle_parses_6_element_array() {
781 let row: MarkPriceCandleUpdate =
782 serde_json::from_str(r#"["1597026383085","3.721","3.743","3.677","3.708","1"]"#)
783 .unwrap();
784 assert_eq!(row.ts.as_str(), "1597026383085");
785 assert_eq!(row.o.as_str(), "3.721");
786 assert_eq!(row.c.as_str(), "3.708");
787 assert_eq!(row.confirm.as_str(), "1");
788 }
789
790 #[test]
791 fn parses_current_instrument_and_estimated_price_fields() {
792 let instrument: InstrumentUpdate = serde_json::from_str(
793 r#"{"instType":"FUTURES","instId":"BTC-USD-260626","groupId":"g1","auctionEndTime":"1","contTdSwTime":"2","maxLmtAmt":"3","upcChg":[{"param":"tickSz","newValue":"0.1","effTime":"4"}]}"#,
794 )
795 .unwrap();
796 assert_eq!(instrument.group_id, "g1");
797 assert_eq!(instrument.upc_chg[0].new_value, "0.1");
798
799 let estimated: EstimatedPriceUpdate = serde_json::from_str(
800 r#"{"instType":"OPTION","instId":"BTC-USD-260626-100000-C","settlePx":"100","settleType":"exercise","ts":"5"}"#,
801 )
802 .unwrap();
803 assert_eq!(estimated.settle_px.as_str(), "100");
804 }
805}