nautilus-okx 0.61.0

OKX exchange integration adapter for the Nautilus trading engine
Documentation
// -------------------------------------------------------------------------------------------------
//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
//  https://nautechsystems.io
//
//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
//  You may not use this file except in compliance with the License.
//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// -------------------------------------------------------------------------------------------------

//! Data models representing OKX API payloads consumed by the adapter.

use nautilus_core::serialization::{
    deserialize_decimal_from_str, deserialize_optional_decimal_from_str, deserialize_string_to_u64,
    serialize_decimal_as_str, serialize_optional_decimal_as_str,
};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize, Serializer};
use ustr::Ustr;

use super::enums::{OKXOptionType, OKXTriggerType};
use crate::common::{
    enums::{
        OKXContractType, OKXInstrumentCategory, OKXInstrumentStatus, OKXInstrumentType,
        OKXRpiPermission,
    },
    parse::{deserialize_empty_ustr_as_none, deserialize_optional_string_to_u64},
};

/// Attached TP/SL child order metadata returned by OKX on parent orders.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXAttachedAlgoOrd {
    /// Attached algo order ID, if assigned by OKX.
    #[serde(default)]
    pub attach_algo_id: String,
    /// Attached child client order ID.
    #[serde(default)]
    pub attach_algo_cl_ord_id: String,
    /// Stop-loss trigger price.
    #[serde(default)]
    pub sl_trigger_px: String,
    /// Stop-loss order price.
    #[serde(default)]
    pub sl_ord_px: String,
    /// Stop-loss trigger price type.
    #[serde(default)]
    pub sl_trigger_px_type: Option<OKXTriggerType>,
    /// Take-profit trigger price.
    #[serde(default)]
    pub tp_trigger_px: String,
    /// Take-profit order price.
    #[serde(default)]
    pub tp_ord_px: String,
    /// Take-profit trigger price type.
    #[serde(default)]
    pub tp_trigger_px_type: Option<OKXTriggerType>,
    /// Callback ratio for attached trailing stop orders.
    #[serde(default)]
    pub callback_ratio: String,
    /// Callback spread for attached trailing stop orders.
    #[serde(default)]
    pub callback_spread: String,
    /// Activation price for attached trailing stop orders.
    #[serde(default)]
    pub active_px: String,
}

/// Represents a Retail Price Improvement order book level.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct OKXRpiBookLevel(
    /// Price.
    #[serde(
        serialize_with = "serialize_decimal_as_str",
        deserialize_with = "deserialize_decimal_from_str"
    )]
    pub Decimal,
    /// Total quantity, including RPI liquidity.
    #[serde(
        serialize_with = "serialize_decimal_as_str",
        deserialize_with = "deserialize_decimal_from_str"
    )]
    pub Decimal,
    /// Quantity excluding RPI liquidity.
    #[serde(
        serialize_with = "serialize_decimal_as_str",
        deserialize_with = "deserialize_decimal_from_str"
    )]
    pub Decimal,
    /// Number of orders at this price.
    #[serde(
        serialize_with = "serialize_u64_as_str",
        deserialize_with = "deserialize_string_to_u64"
    )]
    pub u64,
);

fn serialize_u64_as_str<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_str(&value.to_string())
}

/// Represents an instrument on the OKX exchange.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OKXInstrument {
    /// Product type (SPOT, MARGIN, SWAP, FUTURES, OPTION).
    pub inst_type: OKXInstrumentType,
    /// Instrument ID, e.g. "BTC-USD-SWAP".
    pub inst_id: Ustr,
    /// Instrument ID code (numeric). Required for WebSocket order operations.
    /// E.g., 10458 for BTC-USD-SWAP.
    #[serde(default)]
    pub inst_id_code: Option<u64>,
    /// Underlying of the instrument, e.g. "BTC-USD". Only applicable to FUTURES/SWAP/OPTION.
    pub uly: Ustr,
    /// Instrument family, e.g. "BTC-USD". Only applicable to FUTURES/SWAP/OPTION.
    pub inst_family: Ustr,
    /// Event contract series ID. Only applicable to EVENTS.
    #[serde(default, deserialize_with = "deserialize_empty_ustr_as_none")]
    pub series_id: Option<Ustr>,
    /// Instrument category (the OKX `instCategory` field; the deprecated, distinct
    /// `category` field is intentionally ignored).
    #[serde(default)]
    pub inst_category: Option<OKXInstrumentCategory>,
    /// Initial price-limit band for newly listed contracts.
    #[serde(default)]
    pub init_px_lmt_pct: String,
    /// Floating price-limit band during normal trading.
    #[serde(default)]
    pub float_px_lmt_pct: String,
    /// Maximum price-limit cap.
    #[serde(default)]
    pub max_px_lmt_pct: String,
    /// Base currency, e.g. "BTC" in BTC-USDT. Applicable to SPOT/MARGIN.
    pub base_ccy: Ustr,
    /// Quote currency, e.g. "USDT" in BTC-USDT.
    pub quote_ccy: Ustr,
    /// Settlement currency, e.g. "BTC" for BTC-USD-SWAP.
    pub settle_ccy: Ustr,
    /// Contract value. Only applicable to FUTURES/SWAP/OPTION.
    pub ct_val: String,
    /// Contract multiplier. Only applicable to FUTURES/SWAP/OPTION.
    pub ct_mult: String,
    /// Contract value currency. Only applicable to FUTURES/SWAP/OPTION.
    pub ct_val_ccy: String,
    /// Option type, "C" for call options, "P" for put options. Only applicable to OPTION.
    pub opt_type: OKXOptionType,
    /// Strike price. Only applicable to OPTION.
    pub stk: String,
    /// Listing time, Unix timestamp format in milliseconds, e.g. "1597026383085".
    #[serde(deserialize_with = "deserialize_optional_string_to_u64")]
    pub list_time: Option<u64>,
    /// Expiry time, Unix timestamp format in milliseconds, e.g. "1597026383085".
    #[serde(deserialize_with = "deserialize_optional_string_to_u64")]
    pub exp_time: Option<u64>,
    /// Leverage. Not applicable to SPOT.
    pub lever: String,
    /// Tick size, e.g. "0.1".
    pub tick_sz: String,
    /// Lot size, e.g. "1".
    pub lot_sz: String,
    /// Minimum order size.
    pub min_sz: String,
    /// Contract type. linear: "linear", inverse: "inverse". Only applicable to FUTURES/SWAP.
    pub ct_type: OKXContractType,
    /// Instrument status.
    pub state: OKXInstrumentStatus,
    /// Rule type, e.g. "DynamicPL", "CT", etc.
    pub rule_type: String,
    /// Maximum limit order size.
    #[serde(default)]
    pub max_lmt_sz: String,
    /// Maximum market order size.
    #[serde(default)]
    pub max_mkt_sz: String,
    /// Maximum limit order amount.
    #[serde(default)]
    pub max_lmt_amt: String,
    /// Maximum market order amount.
    #[serde(default)]
    pub max_mkt_amt: String,
    /// Maximum TWAP order size.
    #[serde(default)]
    pub max_twap_sz: String,
    /// Maximum iceberg order size.
    #[serde(default)]
    pub max_iceberg_sz: String,
    /// Maximum trigger order size.
    #[serde(default)]
    pub max_trigger_sz: String,
    /// Maximum stop order size.
    #[serde(default)]
    pub max_stop_sz: String,
    /// RPI maker permission: 0 disabled, 1 enabled without permission, 2 permitted.
    #[serde(default, alias = "elp")]
    pub rpi: Option<OKXRpiPermission>,
    /// Minimum number of price levels between RPI buy and sell orders.
    #[serde(default, deserialize_with = "deserialize_optional_string_to_u64")]
    pub rpi_min_level: Option<u64>,
    /// Minimum distance from the opposite organic best price, in basis points.
    #[serde(
        default,
        serialize_with = "serialize_optional_decimal_as_str",
        deserialize_with = "deserialize_optional_decimal_from_str"
    )]
    pub rpi_min_px_band: Option<Decimal>,
}