use serde::{Deserialize, Serialize};
use ustr::Ustr;
use crate::common::parse::{
deserialize_empty_string_as_none, deserialize_empty_ustr_as_none,
deserialize_target_currency_as_none,
};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXTrade {
pub inst_id: Ustr,
pub px: String,
pub sz: String,
pub side: OKXSide,
pub trade_id: Ustr,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub ts: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OKXCandlestick(
pub String,
pub String,
pub String,
pub String,
pub String,
pub String,
pub String,
pub String,
pub String,
);
use crate::common::{
enums::{
OKXAlgoOrderType, OKXExecType, OKXInstrumentType, OKXMarginMode, OKXOrderCategory,
OKXOrderStatus, OKXOrderType, OKXPositionSide, OKXSide, OKXTargetCurrency, OKXTradeMode,
OKXTriggerType, OKXVipLevel,
},
parse::deserialize_string_to_u64,
};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXMarkPrice {
pub uly: Option<Ustr>,
pub inst_id: Ustr,
pub mark_px: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub ts: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXOptionSummary {
pub inst_type: OKXInstrumentType,
pub inst_id: Ustr,
pub uly: Ustr,
pub bid_vol: String,
pub ask_vol: String,
pub mark_vol: String,
pub fwd_px: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub ts: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXIndexTicker {
pub inst_id: Ustr,
pub idx_px: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub ts: u64,
}
pub type OKXOrderBookLevel = (String, String, String, String);
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXOrderBookSnapshot {
pub asks: Vec<OKXOrderBookLevel>,
pub bids: Vec<OKXOrderBookLevel>,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub ts: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXFundingRateHistory {
pub inst_type: OKXInstrumentType,
pub inst_id: Ustr,
pub funding_rate: String,
pub realized_rate: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub funding_time: u64,
#[serde(default)]
pub method: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXPositionTier {
pub uly: Ustr,
pub inst_family: String,
pub inst_id: Ustr,
pub tier: String,
pub min_sz: String,
pub max_sz: String,
pub mmr: String,
pub imr: String,
pub max_lever: String,
pub opt_mgn_factor: String,
pub quote_max_loan: String,
pub base_max_loan: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXAccount {
pub adj_eq: String,
pub borrow_froz: String,
pub details: Vec<OKXBalanceDetail>,
pub imr: String,
pub iso_eq: String,
pub mgn_ratio: String,
pub mmr: String,
pub notional_usd_for_borrow: String,
pub notional_usd_for_futures: String,
pub notional_usd_for_option: String,
pub notional_usd_for_swap: String,
pub notional_usd: String,
pub ord_froz: String,
pub total_eq: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub u_time: u64,
pub upl: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "python", pyo3::pyclass(from_py_object))]
#[cfg_attr(
feature = "python",
pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.okx")
)]
pub struct OKXBalanceDetail {
pub avail_bal: String,
pub avail_eq: String,
pub borrow_froz: String,
pub cash_bal: String,
pub ccy: Ustr,
pub cross_liab: String,
pub dis_eq: String,
pub eq: String,
pub eq_usd: String,
pub smt_sync_eq: String,
pub spot_copy_trading_eq: String,
pub fixed_bal: String,
pub frozen_bal: String,
pub imr: String,
pub interest: String,
pub iso_eq: String,
pub iso_liab: String,
pub iso_upl: String,
pub liab: String,
pub max_loan: String,
pub mgn_ratio: String,
pub mmr: String,
pub notional_lever: String,
pub ord_frozen: String,
pub reward_bal: String,
#[serde(alias = "spotInUse")]
pub spot_in_use_amt: String,
#[serde(alias = "clSpotInUse")]
pub cl_spot_in_use_amt: String,
#[serde(alias = "maxSpotInUse")]
pub max_spot_in_use_amt: String,
pub spot_iso_bal: String,
pub stgy_eq: String,
pub twap: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub u_time: u64,
pub upl: String,
pub upl_liab: String,
pub spot_bal: String,
pub open_avg_px: String,
pub acc_avg_px: String,
pub spot_upl: String,
pub spot_upl_ratio: String,
pub total_pnl: String,
pub total_pnl_ratio: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXPosition {
pub inst_id: Ustr,
pub inst_type: OKXInstrumentType,
pub mgn_mode: OKXMarginMode,
#[serde(default, deserialize_with = "deserialize_empty_ustr_as_none")]
pub pos_id: Option<Ustr>,
pub pos_side: OKXPositionSide,
pub pos: String,
pub base_bal: String,
pub ccy: String,
pub fee: String,
pub lever: String,
pub last: String,
pub mark_px: String,
pub liq_px: String,
pub mmr: String,
pub interest: String,
pub trade_id: Ustr,
pub notional_usd: String,
pub avg_px: String,
pub upl: String,
pub upl_ratio: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub u_time: u64,
pub margin: String,
pub mgn_ratio: String,
pub adl: String,
pub c_time: String,
pub realized_pnl: String,
pub upl_last_px: String,
pub upl_ratio_last_px: String,
pub avail_pos: String,
pub be_px: String,
pub funding_fee: String,
pub idx_px: String,
pub liq_penalty: String,
pub opt_val: String,
pub pending_close_ord_liab_val: String,
pub pnl: String,
pub pos_ccy: String,
pub quote_bal: String,
pub quote_borrowed: String,
pub quote_interest: String,
#[serde(alias = "spotInUse")]
pub spot_in_use_amt: String,
pub spot_in_use_ccy: String,
pub usd_px: String,
#[serde(default)]
pub delta_bs: String,
#[serde(default)]
pub gamma_bs: String,
#[serde(default)]
pub theta_bs: String,
#[serde(default)]
pub vega_bs: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXPlaceOrderResponse {
#[serde(default)]
pub ord_id: Option<Ustr>,
#[serde(default)]
pub cl_ord_id: Option<Ustr>,
#[serde(default)]
pub tag: Option<String>,
#[serde(default)]
pub inst_id: Option<Ustr>,
#[serde(default)]
pub side: Option<OKXSide>,
#[serde(default)]
pub ord_type: Option<OKXOrderType>,
#[serde(default)]
pub sz: Option<String>,
pub state: Option<OKXOrderStatus>,
#[serde(default)]
pub px: Option<String>,
#[serde(default)]
pub avg_px: Option<String>,
#[serde(default)]
pub acc_fill_sz: Option<String>,
#[serde(default)]
pub fill_sz: Option<String>,
#[serde(default)]
pub fill_px: Option<String>,
#[serde(default)]
pub trade_id: Option<Ustr>,
#[serde(default)]
pub fill_time: Option<String>,
#[serde(default)]
pub fee: Option<String>,
#[serde(default)]
pub fee_ccy: Option<String>,
#[serde(default)]
pub req_id: Option<Ustr>,
#[serde(default)]
pub pos_side: Option<OKXPositionSide>,
#[serde(default)]
pub reduce_only: Option<String>,
#[serde(default, deserialize_with = "deserialize_target_currency_as_none")]
pub tgt_ccy: Option<OKXTargetCurrency>,
#[serde(default)]
pub c_time: Option<String>,
#[serde(default)]
pub u_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s_msg: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXAttachAlgoOrdRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub attach_algo_cl_ord_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sl_trigger_px: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sl_ord_px: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sl_trigger_px_type: Option<OKXTriggerType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tp_trigger_px: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tp_ord_px: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tp_trigger_px_type: Option<OKXTriggerType>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXPlaceOrderRequest {
pub inst_id: String,
pub td_mode: OKXTradeMode,
#[serde(skip_serializing_if = "Option::is_none")]
pub ccy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cl_ord_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
pub side: OKXSide,
#[serde(skip_serializing_if = "Option::is_none")]
pub pos_side: Option<OKXPositionSide>,
pub ord_type: OKXOrderType,
pub sz: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub px: Option<String>,
#[serde(rename = "pxUsd", skip_serializing_if = "Option::is_none")]
pub px_usd: Option<String>,
#[serde(rename = "pxVol", skip_serializing_if = "Option::is_none")]
pub px_vol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reduce_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tgt_ccy: Option<OKXTargetCurrency>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attach_algo_ords: Option<Vec<OKXAttachAlgoOrdRequest>>,
}
pub use crate::common::models::OKXAttachedAlgoOrd;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXOrderHistory {
pub ord_id: Ustr,
pub cl_ord_id: Ustr,
#[serde(default)]
pub algo_id: Option<Ustr>,
#[serde(default)]
pub algo_cl_ord_id: Option<Ustr>,
#[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
pub attach_algo_cl_ord_id: Option<String>,
#[serde(default)]
pub attach_algo_ords: Vec<OKXAttachedAlgoOrd>,
#[serde(default)]
pub cl_act_id: Option<Ustr>,
pub tag: String,
pub inst_type: OKXInstrumentType,
pub uly: Option<Ustr>,
pub inst_id: Ustr,
pub ord_type: OKXOrderType,
pub sz: String,
pub px: String,
#[serde(default)]
pub px_usd: String,
#[serde(default)]
pub px_vol: String,
pub side: OKXSide,
pub pos_side: OKXPositionSide,
pub td_mode: OKXTradeMode,
pub reduce_only: String,
#[serde(default, deserialize_with = "deserialize_target_currency_as_none")]
pub tgt_ccy: Option<OKXTargetCurrency>,
pub state: OKXOrderStatus,
pub avg_px: String,
pub fee: String,
pub fee_ccy: String,
pub fill_sz: String,
pub fill_px: String,
pub trade_id: Ustr,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub fill_time: u64,
pub acc_fill_sz: String,
#[serde(default)]
pub fill_fee: Option<String>,
#[serde(default)]
pub req_id: Option<Ustr>,
#[serde(default)]
pub cancel_fill_sz: Option<String>,
#[serde(default)]
pub cancel_total_sz: Option<String>,
#[serde(default)]
pub fee_discount: Option<String>,
pub category: OKXOrderCategory,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub u_time: u64,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub c_time: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXOrderAlgo {
pub algo_id: String,
#[serde(default)]
pub algo_cl_ord_id: String,
#[serde(default)]
pub cl_ord_id: String,
#[serde(default)]
pub ord_id: String,
pub inst_id: Ustr,
pub inst_type: OKXInstrumentType,
pub ord_type: OKXAlgoOrderType,
pub state: OKXOrderStatus,
pub side: OKXSide,
pub pos_side: OKXPositionSide,
#[serde(default)]
pub sz: String,
#[serde(default)]
pub trigger_px: String,
#[serde(default)]
pub trigger_px_type: Option<OKXTriggerType>,
#[serde(default)]
pub sl_trigger_px: String,
#[serde(default)]
pub sl_ord_px: String,
#[serde(default)]
pub sl_trigger_px_type: Option<OKXTriggerType>,
#[serde(default)]
pub tp_trigger_px: String,
#[serde(default)]
pub tp_ord_px: String,
#[serde(default)]
pub tp_trigger_px_type: Option<OKXTriggerType>,
#[serde(default)]
pub ord_px: String,
pub td_mode: OKXTradeMode,
#[serde(default)]
pub lever: String,
#[serde(default)]
pub reduce_only: String,
#[serde(default)]
pub close_fraction: String,
#[serde(default)]
pub actual_px: String,
#[serde(default)]
pub actual_sz: String,
#[serde(default)]
pub notional_usd: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub c_time: u64,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub u_time: u64,
#[serde(default)]
pub trigger_time: String,
#[serde(default)]
pub tag: String,
#[serde(default)]
pub callback_ratio: String,
#[serde(default)]
pub callback_spread: String,
#[serde(default)]
pub active_px: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXTransactionDetail {
pub inst_type: OKXInstrumentType,
pub inst_id: Ustr,
pub trade_id: Ustr,
pub ord_id: Ustr,
pub cl_ord_id: Ustr,
pub bill_id: Ustr,
pub fill_px: String,
pub fill_sz: String,
pub side: OKXSide,
pub exec_type: OKXExecType,
pub fee_ccy: String,
#[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
pub fee: Option<String>,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub ts: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXPositionHistory {
pub inst_type: OKXInstrumentType,
pub inst_id: Ustr,
pub mgn_mode: OKXMarginMode,
#[serde(rename = "type")]
pub r#type: Ustr,
pub c_time: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub u_time: u64,
pub open_avg_px: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub close_avg_px: Option<String>,
#[serde(default, deserialize_with = "deserialize_empty_ustr_as_none")]
pub pos_id: Option<Ustr>,
#[serde(skip_serializing_if = "Option::is_none")]
pub open_max_pos: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub close_total_pos: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub realized_pnl: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fee: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub funding_fee: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub liq_penalty: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pnl: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pnl_ratio: Option<String>,
pub pos_side: OKXPositionSide,
pub lever: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub direction: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_px: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uly: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ccy: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXPlaceAlgoOrderRequest {
#[serde(rename = "instId")]
pub inst_id: String,
#[serde(rename = "instIdCode", skip_serializing_if = "Option::is_none")]
pub inst_id_code: Option<u64>,
#[serde(rename = "tdMode")]
pub td_mode: OKXTradeMode,
pub side: OKXSide,
#[serde(rename = "ordType")]
pub ord_type: OKXAlgoOrderType,
#[serde(skip_serializing_if = "Option::is_none")]
pub sz: Option<String>,
#[serde(rename = "algoClOrdId", skip_serializing_if = "Option::is_none")]
pub algo_cl_ord_id: Option<String>,
#[serde(rename = "triggerPx", skip_serializing_if = "Option::is_none")]
pub trigger_px: Option<String>,
#[serde(rename = "orderPx", skip_serializing_if = "Option::is_none")]
pub order_px: Option<String>,
#[serde(rename = "triggerPxType", skip_serializing_if = "Option::is_none")]
pub trigger_px_type: Option<OKXTriggerType>,
#[serde(rename = "slTriggerPx", skip_serializing_if = "Option::is_none")]
pub sl_trigger_px: Option<String>,
#[serde(rename = "slOrdPx", skip_serializing_if = "Option::is_none")]
pub sl_ord_px: Option<String>,
#[serde(rename = "slTriggerPxType", skip_serializing_if = "Option::is_none")]
pub sl_trigger_px_type: Option<OKXTriggerType>,
#[serde(rename = "tpTriggerPx", skip_serializing_if = "Option::is_none")]
pub tp_trigger_px: Option<String>,
#[serde(rename = "tpOrdPx", skip_serializing_if = "Option::is_none")]
pub tp_ord_px: Option<String>,
#[serde(rename = "tpTriggerPxType", skip_serializing_if = "Option::is_none")]
pub tp_trigger_px_type: Option<OKXTriggerType>,
#[serde(rename = "tgtCcy", skip_serializing_if = "Option::is_none")]
pub tgt_ccy: Option<OKXTargetCurrency>,
#[serde(rename = "posSide", skip_serializing_if = "Option::is_none")]
pub pos_side: Option<OKXPositionSide>,
#[serde(rename = "closePosition", skip_serializing_if = "Option::is_none")]
pub close_position: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
#[serde(rename = "reduceOnly", skip_serializing_if = "Option::is_none")]
pub reduce_only: Option<bool>,
#[serde(rename = "closeFraction", skip_serializing_if = "Option::is_none")]
pub close_fraction: Option<String>,
#[serde(rename = "callbackRatio", skip_serializing_if = "Option::is_none")]
pub callback_ratio: Option<String>,
#[serde(rename = "callbackSpread", skip_serializing_if = "Option::is_none")]
pub callback_spread: Option<String>,
#[serde(rename = "activePx", skip_serializing_if = "Option::is_none")]
pub active_px: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXPlaceAlgoOrderResponse {
pub algo_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub algo_cl_ord_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s_msg: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub req_id: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXCancelAlgoOrderRequest {
pub inst_id: String,
#[serde(rename = "instIdCode", skip_serializing_if = "Option::is_none")]
pub inst_id_code: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub algo_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub algo_cl_ord_id: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXCancelAlgoOrderResponse {
pub algo_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub s_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s_msg: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXAmendAlgoOrderRequest {
pub inst_id: String,
pub algo_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub algo_cl_ord_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub new_sz: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub new_trigger_px: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub new_order_px: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub new_callback_ratio: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub new_callback_spread: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub new_active_px: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXAmendAlgoOrderResponse {
pub algo_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub algo_cl_ord_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s_msg: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub req_id: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXServerTime {
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub ts: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXFeeRate {
#[serde(deserialize_with = "crate::common::parse::deserialize_vip_level")]
pub level: OKXVipLevel,
pub taker: String,
pub maker: String,
pub taker_u: String,
pub maker_u: String,
#[serde(default)]
pub delivery: String,
#[serde(default)]
pub exercise: String,
pub inst_type: OKXInstrumentType,
#[serde(default)]
pub category: String,
#[serde(deserialize_with = "deserialize_string_to_u64")]
pub ts: u64,
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use serde_json;
use super::*;
#[rstest]
fn test_algo_order_request_serialization() {
let request = OKXPlaceAlgoOrderRequest {
inst_id: "ETH-USDT-SWAP".to_string(),
inst_id_code: None,
td_mode: OKXTradeMode::Isolated,
side: OKXSide::Buy,
ord_type: OKXAlgoOrderType::Trigger,
sz: Some("0.01".to_string()),
algo_cl_ord_id: Some("test123".to_string()),
trigger_px: Some("3000".to_string()),
order_px: Some("-1".to_string()),
trigger_px_type: Some(OKXTriggerType::Last),
sl_trigger_px: None,
sl_ord_px: None,
sl_trigger_px_type: None,
tp_trigger_px: None,
tp_ord_px: None,
tp_trigger_px_type: None,
tgt_ccy: None,
pos_side: None,
close_position: None,
tag: None,
reduce_only: None,
close_fraction: None,
callback_ratio: None,
callback_spread: None,
active_px: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"instId\":\"ETH-USDT-SWAP\""));
assert!(json.contains("\"tdMode\":\"isolated\""));
assert!(json.contains("\"ordType\":\"trigger\""));
assert!(json.contains("\"algoClOrdId\":\"test123\""));
assert!(json.contains("\"triggerPx\":\"3000\""));
assert!(json.contains("\"orderPx\":\"-1\""));
assert!(json.contains("\"triggerPxType\":\"last\""));
assert!(!json.contains("tgtCcy"));
assert!(!json.contains("posSide"));
assert!(!json.contains("closePosition"));
assert!(!json.contains("closeFraction"));
}
#[rstest]
fn test_algo_order_request_serializes_close_fraction() {
let request = OKXPlaceAlgoOrderRequest {
inst_id: "ETH-USDT-SWAP".to_string(),
inst_id_code: None,
td_mode: OKXTradeMode::Cross,
side: OKXSide::Sell,
ord_type: OKXAlgoOrderType::Conditional,
sz: None,
algo_cl_ord_id: Some("close-frac-123".to_string()),
trigger_px: None,
order_px: None,
trigger_px_type: None,
sl_trigger_px: Some("3000".to_string()),
sl_ord_px: Some("-1".to_string()),
sl_trigger_px_type: Some(OKXTriggerType::Last),
tp_trigger_px: None,
tp_ord_px: None,
tp_trigger_px_type: None,
tgt_ccy: None,
pos_side: Some(OKXPositionSide::Net),
close_position: None,
tag: None,
reduce_only: Some(true),
close_fraction: Some("1".to_string()),
callback_ratio: None,
callback_spread: None,
active_px: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"ordType\":\"conditional\""));
assert!(json.contains("\"closeFraction\":\"1\""));
assert!(json.contains("\"slTriggerPx\":\"3000\""));
assert!(json.contains("\"slOrdPx\":\"-1\""));
assert!(json.contains("\"slTriggerPxType\":\"last\""));
assert!(json.contains("\"reduceOnly\":true"));
assert!(!json.contains("\"sz\""));
assert!(!json.contains("triggerPx"));
}
#[rstest]
fn test_algo_order_request_array_serialization() {
let request = OKXPlaceAlgoOrderRequest {
inst_id: "BTC-USDT".to_string(),
inst_id_code: Some(10459),
td_mode: OKXTradeMode::Cross,
side: OKXSide::Sell,
ord_type: OKXAlgoOrderType::Trigger,
sz: Some("0.1".to_string()),
algo_cl_ord_id: None,
trigger_px: Some("50000".to_string()),
order_px: Some("49900".to_string()),
trigger_px_type: Some(OKXTriggerType::Mark),
sl_trigger_px: None,
sl_ord_px: None,
sl_trigger_px_type: None,
tp_trigger_px: None,
tp_ord_px: None,
tp_trigger_px_type: None,
tgt_ccy: Some(OKXTargetCurrency::BaseCcy),
pos_side: Some(OKXPositionSide::Net),
close_position: None,
tag: None,
reduce_only: Some(true),
close_fraction: None,
callback_ratio: None,
callback_spread: None,
active_px: None,
};
let json = serde_json::to_string(&[request]).unwrap();
assert!(json.starts_with('['));
assert!(json.ends_with(']'));
assert!(json.contains("\"instId\":\"BTC-USDT\""));
assert!(json.contains("\"tdMode\":\"cross\""));
assert!(json.contains("\"triggerPx\":\"50000\""));
assert!(json.contains("\"orderPx\":\"49900\""));
assert!(json.contains("\"triggerPxType\":\"mark\""));
assert!(json.contains("\"tgtCcy\":\"base_ccy\""));
assert!(json.contains("\"posSide\":\"net\""));
assert!(json.contains("\"reduceOnly\":true"));
}
#[rstest]
fn test_cancel_algo_order_request_serialization() {
let request = OKXCancelAlgoOrderRequest {
inst_id: "ETH-USDT-SWAP".to_string(),
inst_id_code: None,
algo_id: Some("123456".to_string()),
algo_cl_ord_id: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"instId\":\"ETH-USDT-SWAP\""));
assert!(json.contains("\"algoId\":\"123456\""));
assert!(!json.contains("algoClOrdId"));
}
#[rstest]
fn test_cancel_algo_order_with_client_id_serialization() {
let request = OKXCancelAlgoOrderRequest {
inst_id: "BTC-USDT".to_string(),
inst_id_code: Some(10459),
algo_id: None,
algo_cl_ord_id: Some("client123".to_string()),
};
let json = serde_json::to_string(&[request]).unwrap();
assert!(json.starts_with('['));
assert!(json.contains("\"instId\":\"BTC-USDT\""));
assert!(json.contains("\"algoClOrdId\":\"client123\""));
assert!(!json.contains("\"algoId\""));
}
#[rstest]
fn test_amend_algo_order_trigger_serialization() {
let request = OKXAmendAlgoOrderRequest {
inst_id: "ETH-USDT-SWAP".to_string(),
algo_id: "123456".to_string(),
algo_cl_ord_id: None,
new_sz: None,
new_trigger_px: Some("3500".to_string()),
new_order_px: Some("3490".to_string()),
new_callback_ratio: None,
new_callback_spread: None,
new_active_px: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"instId\":\"ETH-USDT-SWAP\""));
assert!(json.contains("\"algoId\":\"123456\""));
assert!(json.contains("\"newTriggerPx\":\"3500\""));
assert!(json.contains("\"newOrderPx\":\"3490\""));
assert!(!json.contains("newSz"));
assert!(!json.contains("algoClOrdId"));
assert!(!json.contains("newCallbackRatio"));
}
#[rstest]
fn test_amend_algo_order_trailing_stop_serialization() {
let request = OKXAmendAlgoOrderRequest {
inst_id: "BTC-USDT-SWAP".to_string(),
algo_id: "789012".to_string(),
algo_cl_ord_id: Some("client456".to_string()),
new_sz: Some("0.1".to_string()),
new_trigger_px: None,
new_order_px: None,
new_callback_ratio: Some("0.02".to_string()),
new_callback_spread: None,
new_active_px: Some("50000".to_string()),
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"instId\":\"BTC-USDT-SWAP\""));
assert!(json.contains("\"algoId\":\"789012\""));
assert!(json.contains("\"algoClOrdId\":\"client456\""));
assert!(json.contains("\"newSz\":\"0.1\""));
assert!(json.contains("\"newCallbackRatio\":\"0.02\""));
assert!(json.contains("\"newActivePx\":\"50000\""));
assert!(!json.contains("newTriggerPx"));
assert!(!json.contains("newOrderPx"));
}
#[rstest]
fn test_trailing_stop_request_callback_ratio_serialization() {
let request = OKXPlaceAlgoOrderRequest {
inst_id: "BTC-USDT-SWAP".to_string(),
inst_id_code: None,
td_mode: OKXTradeMode::Cross,
side: OKXSide::Buy,
ord_type: OKXAlgoOrderType::MoveOrderStop,
sz: Some("0.1".to_string()),
algo_cl_ord_id: Some("trail-001".to_string()),
trigger_px: None,
order_px: None,
trigger_px_type: None,
sl_trigger_px: None,
sl_ord_px: None,
sl_trigger_px_type: None,
tp_trigger_px: None,
tp_ord_px: None,
tp_trigger_px_type: None,
tgt_ccy: None,
pos_side: None,
close_position: None,
tag: None,
reduce_only: None,
close_fraction: None,
callback_ratio: Some("0.01".to_string()),
callback_spread: None,
active_px: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"ordType\":\"move_order_stop\""));
assert!(json.contains("\"callbackRatio\":\"0.01\""));
assert!(!json.contains("callbackSpread"));
assert!(!json.contains("activePx"));
}
#[rstest]
fn test_trailing_stop_request_callback_spread_serialization() {
let request = OKXPlaceAlgoOrderRequest {
inst_id: "ETH-USDT-SWAP".to_string(),
inst_id_code: None,
td_mode: OKXTradeMode::Isolated,
side: OKXSide::Sell,
ord_type: OKXAlgoOrderType::MoveOrderStop,
sz: Some("1.0".to_string()),
algo_cl_ord_id: None,
trigger_px: None,
order_px: None,
trigger_px_type: None,
sl_trigger_px: None,
sl_ord_px: None,
sl_trigger_px_type: None,
tp_trigger_px: None,
tp_ord_px: None,
tp_trigger_px_type: None,
tgt_ccy: None,
pos_side: None,
close_position: None,
tag: None,
reduce_only: Some(true),
close_fraction: None,
callback_ratio: None,
callback_spread: Some("50.5".to_string()),
active_px: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"callbackSpread\":\"50.5\""));
assert!(!json.contains("callbackRatio"));
assert!(!json.contains("activePx"));
}
#[rstest]
fn test_trailing_stop_request_with_activation_price_serialization() {
let request = OKXPlaceAlgoOrderRequest {
inst_id: "BTC-USDT-SWAP".to_string(),
inst_id_code: None,
td_mode: OKXTradeMode::Cross,
side: OKXSide::Buy,
ord_type: OKXAlgoOrderType::MoveOrderStop,
sz: Some("0.5".to_string()),
algo_cl_ord_id: None,
trigger_px: None,
order_px: None,
trigger_px_type: None,
sl_trigger_px: None,
sl_ord_px: None,
sl_trigger_px_type: None,
tp_trigger_px: None,
tp_ord_px: None,
tp_trigger_px_type: None,
tgt_ccy: None,
pos_side: None,
close_position: None,
tag: None,
reduce_only: None,
close_fraction: None,
callback_ratio: Some("0.005".to_string()),
callback_spread: None,
active_px: Some("65000".to_string()),
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"callbackRatio\":\"0.005\""));
assert!(json.contains("\"activePx\":\"65000\""));
assert!(!json.contains("callbackSpread"));
}
#[rstest]
fn test_amend_algo_order_callback_spread_serialization() {
let request = OKXAmendAlgoOrderRequest {
inst_id: "ETH-USDT-SWAP".to_string(),
algo_id: "456789".to_string(),
algo_cl_ord_id: None,
new_sz: None,
new_trigger_px: None,
new_order_px: None,
new_callback_ratio: None,
new_callback_spread: Some("25.0".to_string()),
new_active_px: Some("4000".to_string()),
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"newCallbackSpread\":\"25.0\""));
assert!(json.contains("\"newActivePx\":\"4000\""));
assert!(!json.contains("newCallbackRatio"));
assert!(!json.contains("newTriggerPx"));
assert!(!json.contains("newSz"));
}
#[rstest]
fn test_amend_algo_order_size_only_serialization() {
let request = OKXAmendAlgoOrderRequest {
inst_id: "BTC-USDT-SWAP".to_string(),
algo_id: "111222".to_string(),
algo_cl_ord_id: None,
new_sz: Some("0.5".to_string()),
new_trigger_px: None,
new_order_px: None,
new_callback_ratio: None,
new_callback_spread: None,
new_active_px: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"newSz\":\"0.5\""));
assert!(!json.contains("newTriggerPx"));
assert!(!json.contains("newOrderPx"));
assert!(!json.contains("newCallbackRatio"));
assert!(!json.contains("newCallbackSpread"));
assert!(!json.contains("newActivePx"));
}
#[rstest]
fn test_amend_algo_order_all_fields_serialization() {
let request = OKXAmendAlgoOrderRequest {
inst_id: "BTC-USDT-SWAP".to_string(),
algo_id: "333444".to_string(),
algo_cl_ord_id: Some("client789".to_string()),
new_sz: Some("1.0".to_string()),
new_trigger_px: Some("60000".to_string()),
new_order_px: Some("59900".to_string()),
new_callback_ratio: Some("0.015".to_string()),
new_callback_spread: Some("100".to_string()),
new_active_px: Some("62000".to_string()),
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"instId\":\"BTC-USDT-SWAP\""));
assert!(json.contains("\"algoId\":\"333444\""));
assert!(json.contains("\"algoClOrdId\":\"client789\""));
assert!(json.contains("\"newSz\":\"1.0\""));
assert!(json.contains("\"newTriggerPx\":\"60000\""));
assert!(json.contains("\"newOrderPx\":\"59900\""));
assert!(json.contains("\"newCallbackRatio\":\"0.015\""));
assert!(json.contains("\"newCallbackSpread\":\"100\""));
assert!(json.contains("\"newActivePx\":\"62000\""));
}
#[rstest]
fn test_place_order_request_serializes_px_usd() {
let request = OKXPlaceOrderRequest {
inst_id: "BTC-USD-250328-50000-C".to_string(),
td_mode: OKXTradeMode::Cross,
ccy: None,
cl_ord_id: Some("test-opt-1".to_string()),
tag: None,
side: OKXSide::Buy,
pos_side: Some(OKXPositionSide::Net),
ord_type: OKXOrderType::Limit,
sz: "1".to_string(),
px: None,
px_usd: Some("100.5".to_string()),
px_vol: None,
reduce_only: None,
tgt_ccy: None,
attach_algo_ords: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"pxUsd\":\"100.5\""));
assert!(!json.contains("\"pxVol\""));
assert!(!json.contains("\"px\":"));
}
#[rstest]
fn test_place_order_request_serializes_px_vol() {
let request = OKXPlaceOrderRequest {
inst_id: "BTC-USD-250328-50000-C".to_string(),
td_mode: OKXTradeMode::Cross,
ccy: None,
cl_ord_id: Some("test-opt-2".to_string()),
tag: None,
side: OKXSide::Buy,
pos_side: Some(OKXPositionSide::Net),
ord_type: OKXOrderType::Limit,
sz: "1".to_string(),
px: None,
px_usd: None,
px_vol: Some("0.55".to_string()),
reduce_only: None,
tgt_ccy: None,
attach_algo_ords: None,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"pxVol\":\"0.55\""));
assert!(!json.contains("\"pxUsd\""));
assert!(!json.contains("\"px\":"));
}
}