use serde::Deserialize;
use super::ExtraFields;
use crate::model::NumberString;
fn array_value(values: &[NumberString], i: usize) -> NumberString {
values.get(i).cloned().unwrap_or_default()
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct InstrumentUpcomingParamChange {
#[serde(default)]
pub param: String,
#[serde(default)]
pub new_value: String,
#[serde(default)]
pub eff_time: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct InstrumentUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub series_id: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub inst_id_code: NumberString,
#[serde(default)]
pub uly: String,
#[serde(default)]
pub inst_family: String,
#[serde(default)]
pub category: String,
#[serde(default)]
pub base_ccy: String,
#[serde(default)]
pub quote_ccy: String,
#[serde(default)]
pub settle_ccy: String,
#[serde(default)]
pub ct_val: NumberString,
#[serde(default)]
pub ct_mult: NumberString,
#[serde(default)]
pub ct_val_ccy: String,
#[serde(default)]
pub opt_type: String,
#[serde(default)]
pub stk: NumberString,
#[serde(default)]
pub list_time: NumberString,
#[serde(default)]
pub cont_td_sw_time: NumberString,
#[serde(default)]
pub pre_mkt_sw_time: NumberString,
#[serde(default)]
pub exp_time: NumberString,
#[serde(default)]
pub lever: NumberString,
#[serde(default)]
pub tick_sz: NumberString,
#[serde(default)]
pub lot_sz: NumberString,
#[serde(default)]
pub min_sz: NumberString,
#[serde(default)]
pub ct_type: String,
#[serde(default)]
pub alias: String,
#[serde(default)]
pub state: String,
#[serde(default)]
pub open_type: String,
#[serde(default)]
pub rule_type: String,
#[serde(default)]
pub max_lmt_sz: NumberString,
#[serde(default)]
pub max_lmt_amt: NumberString,
#[serde(default)]
pub max_mkt_sz: NumberString,
#[serde(default)]
pub max_mkt_amt: NumberString,
#[serde(default)]
pub max_twap_sz: NumberString,
#[serde(default)]
pub max_iceberg_sz: NumberString,
#[serde(default)]
pub max_trigger_sz: NumberString,
#[serde(default)]
pub max_stop_sz: NumberString,
#[serde(default)]
pub auction_end_time: NumberString,
#[serde(default)]
pub future_settlement: bool,
#[serde(default)]
pub trade_quote_ccy_list: Vec<String>,
#[serde(default)]
pub inst_category: String,
#[serde(default)]
pub pos_lmt_amt: NumberString,
#[serde(default)]
pub pos_lmt_pct: NumberString,
#[serde(default)]
pub long_pos_remaining_quota: NumberString,
#[serde(default)]
pub short_pos_remaining_quota: NumberString,
#[serde(default)]
pub max_plat_oi_lmt: NumberString,
#[serde(default)]
pub group_id: String,
#[serde(default)]
pub upc_chg: Vec<InstrumentUpcomingParamChange>,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct EventContractMarketUpdate {
#[serde(default)]
pub series_id: String,
#[serde(default)]
pub event_id: String,
#[serde(default)]
pub market_id: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub title: String,
#[serde(default)]
pub state: String,
#[serde(default)]
pub category: String,
#[serde(default)]
pub rule_type: String,
#[serde(default)]
pub outcome: String,
#[serde(default)]
pub floor_strike: NumberString,
#[serde(default)]
pub settle_value: NumberString,
#[serde(default)]
pub disputed: bool,
#[serde(default)]
pub tick_sz: NumberString,
#[serde(default)]
pub min_sz: NumberString,
#[serde(default)]
pub list_time: NumberString,
#[serde(default)]
pub exp_time: NumberString,
#[serde(default)]
pub fix_time: NumberString,
#[serde(default)]
pub u_time: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct OpenInterestUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub oi: NumberString,
#[serde(default)]
pub oi_ccy: NumberString,
#[serde(default)]
pub oi_usd: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct FundingRateUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub funding_rate: NumberString,
#[serde(default)]
pub next_funding_rate: NumberString,
#[serde(default)]
pub funding_time: NumberString,
#[serde(default)]
pub next_funding_time: NumberString,
#[serde(default)]
pub min_funding_rate: NumberString,
#[serde(default)]
pub max_funding_rate: NumberString,
#[serde(default)]
pub method: String,
#[serde(default)]
pub formula_type: String,
#[serde(default)]
pub premium: NumberString,
#[serde(default)]
pub interest_rate: NumberString,
#[serde(default)]
pub impact_value: NumberString,
#[serde(default)]
pub sett_state: String,
#[serde(default)]
pub sett_funding_rate: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct PriceLimitUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub buy_lmt: NumberString,
#[serde(default)]
pub sell_lmt: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(default)]
pub enabled: bool,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct OptionSummaryUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub uly: String,
#[serde(default, rename = "deltaBS")]
pub delta_bs: NumberString,
pub delta: NumberString,
#[serde(default, rename = "gammaBS")]
pub gamma_bs: NumberString,
#[serde(default, rename = "gamma")]
pub gamma_pa: NumberString,
#[serde(default, rename = "vegaBS")]
pub vega_bs: NumberString,
#[serde(default, rename = "vega")]
pub vega_pa: NumberString,
#[serde(default, rename = "thetaBS")]
pub theta_bs: NumberString,
#[serde(default, rename = "theta")]
pub theta: NumberString,
#[serde(default)]
pub lever: NumberString,
#[serde(default)]
pub mark_vol: NumberString,
#[serde(default)]
pub bid_vol: NumberString,
#[serde(default)]
pub ask_vol: NumberString,
#[serde(default)]
pub real_vol: NumberString,
#[serde(default)]
pub vol_lv: NumberString,
#[serde(default)]
pub fwd_px: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct EstimatedPriceUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub settle_type: String,
#[serde(default)]
#[serde(alias = "settPx")]
pub settle_px: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct MarkPriceUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub mark_px: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct IndexTickerUpdate {
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub idx_px: NumberString,
#[serde(default)]
pub high24h: NumberString,
#[serde(default)]
pub open24h: NumberString,
#[serde(default)]
pub low24h: NumberString,
#[serde(default)]
pub sod_utc0: NumberString,
#[serde(default)]
pub sod_utc8: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct LiquidationOrderDetail {
#[serde(default)]
pub side: String,
#[serde(default)]
pub pos_side: String,
#[serde(default)]
pub bk_px: NumberString,
#[serde(default)]
pub sz: NumberString,
#[serde(default)]
pub bk_loss: NumberString,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct LiquidationOrderUpdate {
#[serde(default)]
pub inst_id: String,
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub uly: String,
#[serde(default)]
pub inst_family: String,
#[serde(default)]
pub details: Vec<LiquidationOrderDetail>,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AdlWarningUpdate {
#[serde(default)]
pub inst_type: String,
#[serde(default)]
pub inst_family: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub max_bal: NumberString,
#[serde(default)]
pub adl_rec_bal: NumberString,
#[serde(default)]
pub bal: NumberString,
#[serde(default)]
pub max_bal_ts: NumberString,
#[serde(default)]
pub adl_type: String,
#[serde(default)]
pub state: String,
#[serde(default)]
pub adl_bal: NumberString,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct EconomicCalendarUpdate {
#[serde(default)]
pub calendar_id: String,
#[serde(default)]
pub date: String,
#[serde(default)]
pub region: String,
#[serde(default)]
pub category: String,
#[serde(default)]
pub event: String,
#[serde(default)]
pub ref_date: String,
#[serde(default)]
pub actual: String,
#[serde(default)]
pub previous: String,
#[serde(default)]
pub forecast: String,
#[serde(default)]
pub importance: String,
#[serde(default)]
pub unit: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub prev_initial: String,
#[serde(default)]
pub ts: NumberString,
#[serde(flatten, default)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(from = "Vec<NumberString>")]
#[non_exhaustive]
pub struct IndexCandleUpdate {
pub ts: NumberString,
pub o: NumberString,
pub h: NumberString,
pub l: NumberString,
pub c: NumberString,
pub confirm: NumberString,
}
impl From<Vec<NumberString>> for IndexCandleUpdate {
fn from(values: Vec<NumberString>) -> Self {
Self {
ts: array_value(&values, 0),
o: array_value(&values, 1),
h: array_value(&values, 2),
l: array_value(&values, 3),
c: array_value(&values, 4),
confirm: array_value(&values, 5),
}
}
}
pub type MarkPriceCandleUpdate = IndexCandleUpdate;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn option_summary_greek_fields_deserialize_from_okx_json_keys() {
let row: OptionSummaryUpdate = serde_json::from_str(
r#"{
"instType":"OPTION","instId":"BTC-USD-241013-70000-P","uly":"BTC-USD",
"delta":"-1.1180902625","deltaBS":"-0.9999672034",
"gamma":"2.2361957091","gammaBS":"0.0000000002",
"vega":"0.0000000001","vegaBS":"0.0000114332",
"theta":"0.0000032334","thetaBS":"28.2649858387",
"lever":"8.465747567","markVol":"0.3675503331","bidVol":"0",
"askVol":"1.1669998535","realVol":"","volLv":"0.2044711229",
"fwdPx":"62604.6993093463","ts":"1728703155650"
}"#,
)
.unwrap();
assert_eq!(row.delta.as_str(), "-1.1180902625");
assert_eq!(row.delta_bs.as_str(), "-0.9999672034");
assert_eq!(row.gamma_pa.as_str(), "2.2361957091");
assert_eq!(row.gamma_bs.as_str(), "0.0000000002");
assert_eq!(row.vega_pa.as_str(), "0.0000000001");
assert_eq!(row.vega_bs.as_str(), "0.0000114332");
assert_eq!(row.theta.as_str(), "0.0000032334");
assert_eq!(row.theta_bs.as_str(), "28.2649858387");
}
#[test]
fn index_candle_parses_6_element_array() {
let row: IndexCandleUpdate = serde_json::from_str(
r#"["1597026383085","3811.31","3811.31","3811.31","3811.31","0"]"#,
)
.unwrap();
assert_eq!(row.ts.as_str(), "1597026383085");
assert_eq!(row.o.as_str(), "3811.31");
assert_eq!(row.c.as_str(), "3811.31");
assert_eq!(row.confirm.as_str(), "0");
}
#[test]
fn mark_price_candle_parses_6_element_array() {
let row: MarkPriceCandleUpdate =
serde_json::from_str(r#"["1597026383085","3.721","3.743","3.677","3.708","1"]"#)
.unwrap();
assert_eq!(row.ts.as_str(), "1597026383085");
assert_eq!(row.o.as_str(), "3.721");
assert_eq!(row.c.as_str(), "3.708");
assert_eq!(row.confirm.as_str(), "1");
}
#[test]
fn parses_current_instrument_and_estimated_price_fields() {
let instrument: InstrumentUpdate = serde_json::from_str(
r#"{"instType":"FUTURES","instId":"BTC-USD-260626","groupId":"g1","auctionEndTime":"1","contTdSwTime":"2","maxLmtAmt":"3","upcChg":[{"param":"tickSz","newValue":"0.1","effTime":"4"}]}"#,
)
.unwrap();
assert_eq!(instrument.group_id, "g1");
assert_eq!(instrument.upc_chg[0].new_value, "0.1");
let estimated: EstimatedPriceUpdate = serde_json::from_str(
r#"{"instType":"OPTION","instId":"BTC-USD-260626-100000-C","settlePx":"100","settleType":"exercise","ts":"5"}"#,
)
.unwrap();
assert_eq!(estimated.settle_px.as_str(), "100");
}
}