소스 검색

添加bitget
添加okx

gepangpang 2 년 전
부모
커밋
945a6fb05a

+ 381 - 0
standard/src/bitget_spot.rs

@@ -0,0 +1,381 @@
+use std::collections::{BTreeMap};
+use std::io::{Error, ErrorKind};
+use std::str::FromStr;
+use tokio::sync::mpsc::Sender;
+use async_trait::async_trait;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::ToPrimitive;
+use rust_decimal_macros::dec;
+use serde_json::json;
+use tracing::error;
+use exchanges::kucoin_swap_rest::KucoinSwapRest;
+use global::trace_stack::TraceStack;
+use crate::exchange::ExchangeEnum;
+use crate::{Account, bitget_spot_handle, Market, Order, OrderCommand, Platform, Position, Ticker, utils};
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct BitgetSpot {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: KucoinSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl BitgetSpot {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BitgetSpot {
+        let market = Market::new();
+        let mut kucoin_swap = BitgetSpot {
+            exchange: ExchangeEnum::KucoinSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: KucoinSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+        kucoin_swap.market = BitgetSpot::get_market(&mut kucoin_swap).await.unwrap_or(kucoin_swap.market);
+        return kucoin_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for BitgetSpot {
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+
+    fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::BitgetSpot }
+
+    fn get_self_symbol(&self) -> String { self.symbol.clone() }
+
+    fn get_self_is_colo(&self) -> bool { self.is_colo }
+
+    fn get_self_params(&self) -> BTreeMap<String, String> { self.params.clone() }
+
+    fn get_self_market(&self) -> Market { self.market.clone() }
+
+    fn get_request_delays(&self) -> Vec<i64> { self.request.get_delays() }
+
+    fn get_request_avg_delay(&self) -> Decimal { self.request.get_avg_delay() }
+
+    fn get_request_max_delay(&self) -> i64 { self.request.get_max_delay() }
+
+    async fn get_server_time(&mut self) -> Result<String, Error> {
+        let res_data = self.request.get_server_time().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let result = res_data_json["serverTime"].as_str().unwrap().to_string();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        let symbol_array: Vec<&str> = self.symbol.split("_").collect();
+        let res_data = self.request.get_account(symbol_array[1].to_string().to_uppercase()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let account_info = res_data_json[0].clone();
+
+            let available_balance = Decimal::from_str(account_info["available"].as_str().unwrap()).unwrap();
+            let frozen_balance = Decimal::from_str(account_info["frozen"].as_str().unwrap()).unwrap();
+            let balance = available_balance + frozen_balance;
+
+            let result = Account {
+                balance,
+                available_balance,
+                frozen_balance,
+                stocks: dec!(0),
+                available_stocks: dec!(0),
+                frozen_stocks: dec!(0),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> { todo!() }
+
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> { todo!() }
+
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let res_data = self.request.get_ticker(symbol_format).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let time = (Decimal::from_str(&*res_data_json["ts"].as_str().unwrap()).unwrap() / dec!(1000)).floor().to_i64().unwrap();
+            let result = Ticker {
+                time,
+                high: Decimal::from_str(res_data_json["high24h"].as_str().unwrap()).unwrap(),
+                low: Decimal::from_str(res_data_json["low24h"].as_str().unwrap()).unwrap(),
+                sell: Decimal::from_str(res_data_json["askPr"].as_str().unwrap()).unwrap(),
+                buy: Decimal::from_str(res_data_json["bidPr"].as_str().unwrap()).unwrap(),
+                last: Decimal::from_str(res_data_json["lastPr"].as_str().unwrap()).unwrap(),
+                volume: Decimal::from_str(res_data_json["quoteVolume"].as_str().unwrap()).unwrap(),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
+        todo!()
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let res_data = self.request.get_market_details().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let market_info = res_data_json.iter().find(|item| item["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("Gate:获取Market信息错误!\nget_market:market_info={:?}", market_info);
+                    panic!("Gate:获取Market信息错误!\nget_market:market_info={:?}", market_info)
+                }
+                Some(value) => {
+                    let base_asset = value["baseCoin"].as_str().unwrap().to_string();
+                    let quote_asset = value["quoteCoin"].as_str().unwrap().to_string();
+                    let price_precision = Decimal::from_str(value["pricePrecision"].as_str().unwrap()).unwrap();
+                    let amount_precision = Decimal::from_str(value["quantityPrecision"].as_str().unwrap()).unwrap();
+                    let tick_size = Decimal::from_str(&format!("0.{}", "0".repeat(usize::try_from(price_precision).unwrap()))).unwrap();
+                    let amount_size = Decimal::from_str(&format!("0.{}", "0".repeat(usize::try_from(amount_precision).unwrap()))).unwrap();
+
+                    let result = Market {
+                        symbol: format!("{}_{}", base_asset, quote_asset),
+                        base_asset,
+                        quote_asset,
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty: Decimal::from_str(&value["minTradeAmount"].as_str().unwrap()).unwrap(),
+                        max_qty: Decimal::from_str(&value["maxTradeAmount"].as_str().unwrap()).unwrap(),
+                        min_notional: Default::default(),
+                        max_notional: Default::default(),
+                        ct_val: Default::default(),
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        todo!()
+    }
+
+    async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let amount_size = self.market.amount_size;
+        let id = if order_id.eq("") { custom_id.to_string() } else { order_id.to_string() };
+        let res_data = self.request.get_orders(id, "".to_string()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let order_info = res_data_json[0].clone();
+            let result = bitget_spot_handle::format_order_item(order_info, amount_size);
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_orders_list(&mut self, status: &str) -> Result<Vec<Order>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let amount_size = self.market.amount_size;
+        let res_data = self.request.get_orders(status.to_string(), symbol_format.to_string()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
+            let order_info: Vec<_> = res_data_json.iter().filter(|item| item["symbol"].as_str().unwrap_or("") == symbol_format.to_lowercase()).collect();
+            let result = order_info.iter().map(|&item| bitget_spot_handle::format_order_item(item.clone(), amount_size)).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let mut params = json!({
+            "symbol": symbol_format.to_string(),
+            "clientOid": custom_id,
+            "orderType": "limit",
+            "price": price.to_string(),
+            "force": "gtc"
+        });
+        let size = amount;
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+                params["size"] = json!(size);
+            }
+            "pd" => {
+                params["side"] = json!(true);
+                params["size"] = json!(size);
+            }
+            "kk" => {
+                params["side"] = json!(false);
+                params["size"] = json!(size);
+            }
+            "pk" => {
+                params["side"] = json!(true);
+                params["size"] = json!(size);
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let result = Order {
+                id: res_data_json["orderId"].as_str().unwrap().to_string(),
+                custom_id: res_data_json["clientOid"].as_str().unwrap().to_string(),
+                price: dec!(0),
+                amount: dec!(0),
+                deal_amount: dec!(0),
+                avg_price: dec!(0),
+                status: "NEW".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("245 bitget_spot".to_string()),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn take_order_symbol(&mut self, symbol: String, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let mut params = json!({
+            "symbol": symbol_format.to_string(),
+            "clientOid": custom_id,
+            "orderType": "limit",
+            "price": price.to_string(),
+            "force": "gtc"
+        });
+        let size = amount;
+        match origin_side {
+            "kd" => {
+                params["side"] = json!("buy");
+                params["size"] = json!(size);
+            }
+            "pd" => {
+                params["side"] = json!(true);
+                params["size"] = json!(size);
+            }
+            "kk" => {
+                params["side"] = json!(false);
+                params["size"] = json!(size);
+            }
+            "pk" => {
+                params["side"] = json!(true);
+                params["size"] = json!(size);
+            }
+            _ => { error!("下单参数错误"); }
+        };
+        let res_data = self.request.swap_order(params).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let result = Order {
+                id: res_data_json["orderId"].as_str().unwrap().to_string(),
+                custom_id: res_data_json["clientOid"].as_str().unwrap().to_string(),
+                price: dec!(0),
+                amount: dec!(0),
+                deal_amount: dec!(0),
+                avg_price: dec!(0),
+                status: "NEW".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("295 bitget_spot".to_string()),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let id = if order_id.eq("") { custom_id.to_string() } else { order_id.to_string() };
+        let res_data = self.request.cancel_order(order_id.to_string(), custom_id.to_string()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let result = Order {
+                id: res_data_json["orderId"].as_str().unwrap().to_string(),
+                custom_id: res_data_json["clientOid"].as_str().unwrap().to_string(),
+                price: dec!(0),
+                amount: dec!(0),
+                deal_amount: dec!(0),
+                avg_price: dec!(0),
+                status: "REMOVE".to_string(),
+                order_type: "".to_string(),
+                trace_stack: TraceStack::default().on_special("319 bitget_spot".to_string()),
+            };
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let res_data = self.request.cancel_orders(symbol_format.clone()).await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let order_info_list: Vec<serde_json::Value> = res_data_json["successList"].as_array().unwrap().clone();
+            let result = order_info_list.iter().map(|item|
+                Order {
+                    id: item["orderId"].as_str().unwrap().to_string(),
+                    custom_id: item["clientOid"].as_str().unwrap().to_string(),
+                    price: dec!(0),
+                    amount: dec!(0),
+                    deal_amount: dec!(0),
+                    avg_price: dec!(0),
+                    status: "REMOVE".to_string(),
+                    order_type: "".to_string(),
+                    trace_stack: TraceStack::default().on_special("344 bitget_spot".to_string()),
+                }
+            ).collect();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+        todo!()
+    }
+
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+        todo!()
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> {
+        todo!()
+    }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+        todo!()
+    }
+
+    async fn command_order(&mut self, _order_command: OrderCommand, _trace_stack: TraceStack) {
+        todo!()
+    }
+}
+

+ 117 - 0
standard/src/bitget_spot_handle.rs

@@ -0,0 +1,117 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use tracing::trace;
+use exchanges::response_base::ResponseData;
+use crate::{Account, MarketOrder, Order, SpecialDepth, SpecialOrder, SpecialTicker};
+use crate::exchange::ExchangeEnum;
+use crate::handle_info::HandleSwapInfo;
+
+// 处理账号信息
+pub fn handle_account_info(res_data: ResponseData, symbol: String) -> Account {
+    let symbol_array: Vec<&str> = symbol.split("_").collect();
+    let res_data_str = res_data.data;
+    let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&res_data_str).unwrap();
+    let account_info = res_data_json.iter().find(|&item| item["coin"].as_str().unwrap() == symbol_array[1]).unwrap();
+    format_account_info(account_info.clone())
+}
+
+pub fn format_account_info(data: serde_json::Value) -> Account {
+    let available_balance = Decimal::from_str(data["available"].as_str().unwrap()).unwrap();
+    let frozen_balance = Decimal::from_str(data["frozen"].as_str().unwrap()).unwrap();
+    let balance = available_balance + frozen_balance;
+    Account {
+        balance,
+        available_balance,
+        frozen_balance,
+        stocks: dec!(0),
+        available_stocks: dec!(0),
+        frozen_stocks: dec!(0),
+    }
+}
+
+// 处理order信息
+pub fn handle_order(res_data: ResponseData, amount_size: Decimal) -> SpecialOrder {
+    let res_data_str = res_data.data;
+    let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+    let mut order_info = Vec::new();
+    for item in res_data_json.iter() {
+        order_info.push(format_order_item(item.clone(), amount_size));
+    }
+    trace!(?order_info);
+    SpecialOrder {
+        name: res_data.label,
+        order: order_info,
+    }
+}
+
+// 处理订单信息
+pub fn format_order_item(order: serde_json::Value, amount_size: Decimal) -> Order {
+    let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
+    let size = Decimal::from_str(order["size"].as_str().unwrap()).unwrap();
+    let status = order["status"].as_str().unwrap_or("");
+    let acc_base_volume = Decimal::from_str(order["accBaseVolume"].as_str().unwrap()).unwrap();
+
+    let avg_price = Decimal::from_str(order["priceAvg"].as_str().unwrap()).unwrap();
+
+    let amount = size * amount_size;
+    let deal_amount = acc_base_volume * amount_size;
+    let custom_status = if ["filled", "cancelled"].contains(&status) {
+        "REMOVE".to_string()
+    } else if ["init", "live", "new", "partially_filled"].contains(&status) {
+        "NEW".to_string()
+    } else {
+        "NULL".to_string()
+    };
+    Order {
+        id: order["orderId"].as_str().unwrap().to_string(),
+        custom_id: order["clientOid"].as_str().unwrap().to_string(),
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["ordType"].as_str().unwrap().to_string(),
+        trace_stack: Default::default(),
+    }
+}
+
+// 处理特殊深度数据
+pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+    HandleSwapInfo::handle_special_depth(ExchangeEnum::OkxSwap, res_data)
+}
+
+// 格式化深度信息
+pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}
+
+// 处理特殊Ticker信息
+pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
+    let res_data_str = res_data.data;
+    let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+    format_special_ticker(res_data_json[0].clone(), res_data.label)
+}
+
+pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
+    let bp = Decimal::from_str(data["bidPr"].as_str().unwrap()).unwrap();
+    let bq = Decimal::from_str(data["bidSz"].as_str().unwrap()).unwrap();
+    let ap = Decimal::from_str(data["askPr"].as_str().unwrap()).unwrap();
+    let aq = Decimal::from_str(data["askSz"].as_str().unwrap()).unwrap();
+    let mp = (bp + ap) * dec!(0.5);
+
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp };
+    let depth_info = vec![bp, bq, ap, aq];
+    SpecialDepth {
+        name: label,
+        depth: depth_info,
+        ticker: ticker_info,
+    }
+}

+ 15 - 4
standard/src/exchange.rs

@@ -1,12 +1,14 @@
 use std::collections::{BTreeMap};
 use std::io::Error;
 use tokio::sync::mpsc::Sender;
+use crate::{Order, Platform};
 use crate::binance_swap::BinanceSwap;
 use crate::binance_spot::BinanceSpot;
 use crate::gate_spot::GateSpot;
 use crate::gate_swap::GateSwap;
 use crate::kucoin_swap::KucoinSwap;
-use crate::{Order, Platform};
+use crate::bitget_spot::BitgetSpot;
+use crate::okx_swap::OkxSwap;
 
 /// 交易所交易模式枚举
 /// - `BinanceSwap`: Binance交易所期货;
@@ -21,6 +23,8 @@ pub enum ExchangeEnum {
     GateSwap,
     GateSpot,
     KucoinSwap,
+    OkxSwap,
+    BitgetSpot,
 }
 
 /// Exchange结构体
@@ -68,9 +72,6 @@ impl Exchange {
             ExchangeEnum::BinanceSwap => {
                 Box::new(BinanceSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
-            ExchangeEnum::BinanceSpot => {
-                Box::new(BinanceSpot::new(symbol, is_colo, params))
-            }
             ExchangeEnum::GateSwap => {
                 Box::new(GateSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
@@ -80,6 +81,16 @@ impl Exchange {
             ExchangeEnum::KucoinSwap => {
                 Box::new(KucoinSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
+            ExchangeEnum::OkxSwap => {
+                Box::new(OkxSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+            ExchangeEnum::BitgetSpot => {
+                Box::new(BitgetSpot::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
+            _ => {
+                // BinanceSpot
+                Box::new(BinanceSpot::new(symbol, is_colo, params))
+            }
         }
     }
 }

+ 9 - 1
standard/src/handle_info.rs

@@ -6,7 +6,7 @@ use tracing::error;
 use exchanges::response_base::ResponseData;
 use global::public_params;
 use crate::exchange::ExchangeEnum;
-use crate::{Account, binance_handle, gate_handle, kucoin_handle, MarketOrder, Position, SpecialDepth, SpecialOrder, SpecialTicker};
+use crate::{Account, binance_handle, bitget_spot_handle, gate_handle, kucoin_handle, MarketOrder, okx_handle, Position, SpecialDepth, SpecialOrder, SpecialTicker};
 
 #[allow(dead_code)]
 pub struct HandleSwapInfo;
@@ -115,6 +115,14 @@ impl HandleSwapInfo {
                 depth_asks = kucoin_handle::format_depth_items(res_data_json["asks"].clone());
                 depth_bids = kucoin_handle::format_depth_items(res_data_json["bids"].clone());
             }
+            ExchangeEnum::OkxSwap => {
+                depth_asks = okx_handle::format_depth_items(res_data_json[0]["asks"].clone());
+                depth_bids = okx_handle::format_depth_items(res_data_json[0]["bids"].clone());
+            }
+            ExchangeEnum::BitgetSpot => {
+                depth_asks = bitget_spot_handle::format_depth_items(res_data_json[0]["asks"].clone());
+                depth_bids = bitget_spot_handle::format_depth_items(res_data_json[0]["bids"].clone());
+            }
             _ => {
                 error!("参数错误!handle_special_depth: {:?}",exchange);
                 panic!("参数错误!handle_special_depth: {:?}", exchange);

+ 4 - 4
standard/src/lib.rs

@@ -23,10 +23,10 @@ mod gate_spot;
 pub mod gate_handle;
 mod kucoin_swap;
 pub mod kucoin_handle;
-// mod okx_swap;
-// pub mod okx_handle;
-// mod bitget_spot;
-// pub mod bitget_spot_handle;
+mod okx_swap;
+pub mod okx_handle;
+mod bitget_spot;
+pub mod bitget_spot_handle;
 
 /// 持仓模式枚举
 /// - `Both`:单持仓方向

+ 155 - 0
standard/src/okx_handle.rs

@@ -0,0 +1,155 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use tracing::trace;
+use exchanges::response_base::ResponseData;
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
+use crate::exchange::ExchangeEnum;
+use crate::handle_info::HandleSwapInfo;
+
+// 处理账号信息
+pub fn handle_account_info(res_data: ResponseData, _symbol: String) -> Account {
+    let res_data_str = res_data.data;
+    let res_data_json: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
+    let account_info = res_data_json[0]["details"].clone();
+    let details = account_info[0].clone();
+    format_account_info(details)
+}
+
+pub fn format_account_info(data: serde_json::Value) -> Account {
+    Account {
+        balance: Decimal::from_str(data["cashBal"].as_str().unwrap()).unwrap(),
+        available_balance: Decimal::from_str(data["availBal"].as_str().unwrap()).unwrap(),
+        frozen_balance: Decimal::from_str(data["fixedBal"].as_str().unwrap()).unwrap(),
+        stocks: dec!(0),
+        available_stocks: dec!(0),
+        frozen_stocks: dec!(0),
+    }
+}
+
+// 处理order信息
+pub fn handle_order(res_data: ResponseData, amount_size: Decimal) -> SpecialOrder {
+    let res_data_str = res_data.data;
+    let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
+    trace!(?res_data_json);
+    let mut order_info = Vec::new();
+    for item in res_data_json.iter() {
+        order_info.push(format_order_item(item.clone(), amount_size));
+    }
+    trace!(?order_info);
+    SpecialOrder {
+        name: res_data.label,
+        order: order_info,
+    }
+}
+
+// 处理订单信息
+pub fn format_order_item(order: serde_json::Value, amount_size: Decimal) -> Order {
+    let price = Decimal::from_str(order["px"].as_str().unwrap()).unwrap();
+    let size = Decimal::from_str(order["sz"].as_str().unwrap()).unwrap();
+    let status = order["state"].as_str().unwrap_or("");
+    let filled_size = Decimal::from_str(order["accFillSz"].as_str().unwrap()).unwrap();
+
+    let avg_price = Decimal::from_str(order["avgPx"].as_str().unwrap()).unwrap();
+
+    let amount = size * amount_size;
+    let deal_amount = filled_size * amount_size;
+    let custom_status = if ["canceled", "filled", "mmp_canceled"].contains(&status) {
+        "REMOVE".to_string()
+    } else if ["live", "partially_filled"].contains(&status) {
+        "NEW".to_string()
+    } else {
+        "NULL".to_string()
+    };
+    Order {
+        id: order["ordId"].as_str().unwrap().to_string(),
+        custom_id: order["clOrdId"].as_str().unwrap().to_string(),
+        price,
+        amount,
+        deal_amount,
+        avg_price,
+        status: custom_status,
+        order_type: order["ordType"].as_str().unwrap().to_string(),
+        trace_stack: Default::default(),
+    }
+}
+
+// 处理特殊深度数据
+pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
+    HandleSwapInfo::handle_special_depth(ExchangeEnum::OkxSwap, res_data)
+}
+
+// 格式化深度信息
+pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
+    let mut depth_items: Vec<MarketOrder> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(MarketOrder {
+            price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}
+
+// 处理特殊Ticker信息
+pub fn handle_special_ticker(res_data: ResponseData) -> SpecialDepth {
+    let res_data_str = res_data.data;
+    let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
+    format_special_ticker(res_data_json[0].clone(), res_data.label)
+}
+
+pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
+    let bids = data["bids"][0].as_array().unwrap();
+    let asks = data["asks"][0].as_array().unwrap();
+    let bp = Decimal::from_str(bids[0].as_str().unwrap()).unwrap();
+    let bq = Decimal::from_str(bids[1].as_str().unwrap()).unwrap();
+    let ap = Decimal::from_str(asks[0].as_str().unwrap()).unwrap();
+    let aq = Decimal::from_str(asks[1].as_str().unwrap()).unwrap();
+    let mp = (bp + ap) * dec!(0.5);
+
+    let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp };
+    let depth_info = vec![bp, bq, ap, aq];
+    SpecialDepth {
+        name: label,
+        depth: depth_info,
+        ticker: ticker_info,
+    }
+}
+
+// 处理position信息
+pub fn handle_position(res_data: ResponseData, amount_size: Decimal) -> Vec<Position> {
+    let res_data_str = res_data.data;
+    let res_data_json: serde_json::Value = serde_json::from_str(&res_data_str).unwrap();
+    let position_data = res_data_json[0]["posData"].clone();
+    trace!(?position_data);
+    let result = format_position_item(position_data, amount_size);
+    return vec![result];
+}
+
+pub fn format_position_item(position: serde_json::Value, amount_size: Decimal) -> Position {
+    let symbol = position["instId"].as_str().unwrap_or("");
+    let real_leverage = dec!(-1);
+    let currency = position["ccy"].as_str().unwrap_or("");
+    let coin: Vec<&str> = symbol.split("-").collect();
+    let avg_entry_price = Decimal::from_str(&position["avgPx"].to_string()).unwrap();
+    let unrealised_pnl = Decimal::from_str(&position["unrealisedPnl"].to_string()).unwrap();
+    let pos_margin = Decimal::from_str(&position["posSide"].to_string()).unwrap();
+
+    let current_qty = Decimal::from_str(&position["pos"].to_string()).unwrap();
+    let amount = current_qty * amount_size;
+    let position_mode = match amount {
+        amount if amount > dec!(0) => PositionModeEnum::Long,
+        amount if amount < dec!(0) => PositionModeEnum::Short,
+        _ => { PositionModeEnum::Both }
+    };
+    Position {
+        symbol: format!("{}_{}", coin[0], currency),
+        margin_level: real_leverage,
+        amount,
+        frozen_amount: dec!(0),
+        price: avg_entry_price,
+        profit: unrealised_pnl,
+        position_mode,
+        margin: pos_margin,
+    }
+}

+ 123 - 0
standard/src/okx_swap.rs

@@ -0,0 +1,123 @@
+use std::collections::{BTreeMap};
+use std::io::{Error, ErrorKind};
+use tokio::sync::mpsc::Sender;
+use async_trait::async_trait;
+use rust_decimal::Decimal;
+use exchanges::okx_swap_rest::OkxSwapRest;
+use global::trace_stack::TraceStack;
+use crate::exchange::ExchangeEnum;
+use crate::{Account, Market, Order, OrderCommand, Platform, Position, Ticker};
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct OkxSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: OkxSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl OkxSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> OkxSwap {
+        let market = Market::new();
+        let mut kucoin_swap = OkxSwap {
+            exchange: ExchangeEnum::OkxSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: OkxSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+        // kucoin_swap.market = OkxSwap::get_market(&mut kucoin_swap).await.unwrap_or(kucoin_swap.market);
+        return kucoin_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for OkxSwap {
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+
+    fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::OkxSwap }
+
+    fn get_self_symbol(&self) -> String { self.symbol.clone() }
+
+    fn get_self_is_colo(&self) -> bool { self.is_colo }
+
+    fn get_self_params(&self) -> BTreeMap<String, String> { self.params.clone() }
+
+    fn get_self_market(&self) -> Market { self.market.clone() }
+
+    fn get_request_delays(&self) -> Vec<i64> { self.request.get_delays() }
+
+    fn get_request_avg_delay(&self) -> Decimal { self.request.get_avg_delay() }
+
+    fn get_request_max_delay(&self) -> i64 { self.request.get_max_delay() }
+
+    async fn get_server_time(&mut self) -> Result<String, Error> {
+        let res_data = self.request.get_server_time().await;
+        if res_data.code == "200" {
+            let res_data_str = &res_data.data;
+            let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
+            let result = res_data_json[0]["ts"].as_str().unwrap().to_string();
+            Ok(result)
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        todo!()
+    }
+
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        todo!()
+    }
+
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        todo!()
+    }
+
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        todo!()
+    }
+
+    async fn get_ticker_symbol(&mut self, _symbol: String) -> Result<Ticker, Error> { todo!() }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        todo!()
+    }
+
+    async fn get_market_symbol(&mut self, _symbol: String) -> Result<Market, Error> { todo!() }
+
+    async fn get_order_detail(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { todo!() }
+
+    async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> { todo!() }
+
+    async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { todo!() }
+
+    async fn take_order_symbol(&mut self, _symbol: String, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { todo!() }
+
+    async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { todo!() }
+
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        todo!()
+    }
+
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { todo!() }
+
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+        todo!()
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { todo!() }
+
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { todo!() }
+
+    async fn command_order(&mut self, _order_command: OrderCommand, _trace_stack: TraceStack) { todo!() }
+}

+ 143 - 49
standard/tests/exchange_test.rs

@@ -4,15 +4,17 @@ use std::io::Error;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
 use std::time::Duration;
+use rust_decimal_macros::dec;
 use tokio::sync::mpsc::{channel, Receiver, Sender};
 use tokio::try_join;
 use tracing::{error, trace};
-use exchanges::binance_spot_ws::{BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
+// use exchanges::binance_spot_ws::{BinanceSpotSubscribeType, BinanceSpotWs, BinanceSpotWsType};
+use exchanges::okx_swap_ws::{OkxSubscribeType, OkxSwapWs, OkxWsType};
 // use exchanges::kucoin_swap_ws::{KucoinSubscribeType, KucoinSwapWs, KucoinWsType};
 use exchanges::response_base::ResponseData;
 use standard::exchange::{Exchange, ExchangeEnum};
 // use standard::{binance_handle, kucoin_handle, Order, Platform, utils};
-use standard::{binance_handle, Order, Platform, utils};
+use standard::{okx_handle, Order, Platform, utils};
 
 // 创建实体
 #[allow(dead_code)]
@@ -63,61 +65,77 @@ pub async fn test_new_exchange(exchange: ExchangeEnum, symbol: &str) -> Box<dyn
             params.insert("pass_key".to_string(), pass_key);
             Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
         }
+        ExchangeEnum::OkxSwap => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = env::var("okx_access_key").unwrap_or("".to_string());
+            let secret_key = env::var("okx_secret_key").unwrap_or("".to_string());
+            let pass_key = env::var("okx_passphrase").unwrap_or("".to_string());
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            params.insert("pass_key".to_string(), pass_key);
+            Exchange::new(exchange, symbol.to_string(), false, params, order_sender, error_sender).await
+        }
+        _ => {
+            error!("该交易所不支持!test_new_exchange:{:?}",exchange);
+            panic!("该交易所不支持!test_new_exchange:{:?}", exchange)
+        }
     }
 }
 
 #[allow(dead_code)]
-pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subscriber_type: T, mold: &str) where Vec<BinanceSpotSubscribeType>: From<T> {
+pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subscriber_type: T, mold: &str) where Vec<OkxSubscribeType>: From<T> {
     utils::proxy_handle();
 
     match exchange {
         ExchangeEnum::BinanceSpot => {
-            let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
-            trace!(symbol_format);
-            let name = format!("binance_usdt_swap@{}", symbol.to_string().to_lowercase());
-            let bool_v1 = Arc::new(AtomicBool::new(true));
-
-            let (res_sender, mut res_receiver): (Sender<ResponseData>, Receiver<ResponseData>) = channel(1024);
-            let mut params: BTreeMap<String, String> = BTreeMap::new();
-            let access_key = env::var("binance_access_key").unwrap_or("".to_string());
-            let secret_key = env::var("binance_secret_key").unwrap_or("".to_string());
-            params.insert("access_key".to_string(), access_key);
-            params.insert("secret_key".to_string(), secret_key);
-            let mut exchange_wss;
-            exchange_wss = BinanceSpotWs::new_label(name, false, params, BinanceSpotWsType::PublicAndPrivate, res_sender);
-            exchange_wss.set_subscribe(subscriber_type.into());
-
-            let t1 = tokio::spawn(async move {
-                exchange_wss.custom_subscribe(bool_v1, vec![symbol_format]).await;
-            });
-            let mold_arc = Arc::new(mold.to_string());
-            let t2 = tokio::spawn(async move {
-                let mold_clone = Arc::clone(&mold_arc);
-                loop {
-                    tokio::time::sleep(Duration::from_millis(1)).await;
-                    if let Ok(received) = res_receiver.try_recv() {
-                        match mold_clone.as_str() {
-                            "depth" => {
-                                if received.data != "" {
-                                    let result = binance_handle::handle_special_depth(received);
-                                    trace!(?result)
-                                }
-                            }
-                            "ticker" => {
-                                if received.data != "" {
-                                    let result = binance_handle::handle_special_ticker(received);
-                                    trace!(?result)
-                                }
-                            }
-                            _ => {
-                                error!("没有该命令!mode={}", mold_clone);
-                                panic!("没有该命令!mode={}", mold_clone)
-                            }
-                        }
-                    }
-                }
-            });
-            try_join!(t1, t2).unwrap();
+            // let symbol_format = utils::format_symbol(symbol.to_string(), "").to_uppercase();
+            // trace!(symbol_format);
+            // let name = format!("binance_usdt_swap@{}", symbol.to_string().to_lowercase());
+            // let bool_v1 = Arc::new(AtomicBool::new(true));
+            //
+            // let (res_sender, mut res_receiver): (Sender<ResponseData>, Receiver<ResponseData>) = channel(1024);
+            // let mut params: BTreeMap<String, String> = BTreeMap::new();
+            // let access_key = env::var("binance_access_key").unwrap_or("".to_string());
+            // let secret_key = env::var("binance_secret_key").unwrap_or("".to_string());
+            // params.insert("access_key".to_string(), access_key);
+            // params.insert("secret_key".to_string(), secret_key);
+            // let mut exchange_wss;
+            // exchange_wss = BinanceSpotWs::new_label(name, false, params, BinanceSpotWsType::PublicAndPrivate, res_sender);
+            // exchange_wss.set_subscribe(subscriber_type.into());
+            //
+            // let t1 = tokio::spawn(async move {
+            //     exchange_wss.custom_subscribe(bool_v1, vec![symbol_format]).await;
+            // });
+            // let mold_arc = Arc::new(mold.to_string());
+            // let t2 = tokio::spawn(async move {
+            //     let mold_clone = Arc::clone(&mold_arc);
+            //     loop {
+            //         tokio::time::sleep(Duration::from_millis(1)).await;
+            //         if let Ok(received) = res_receiver.try_recv() {
+            //             match mold_clone.as_str() {
+            //                 "depth" => {
+            //                     if received.data != "" {
+            //                         let result = binance_handle::handle_special_depth(received);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 "ticker" => {
+            //                     if received.data != "" {
+            //                         let result = binance_handle::handle_special_ticker(received);
+            //                         trace!(?result)
+            //                     }
+            //                 }
+            //                 _ => {
+            //                     error!("没有该命令!mode={}", mold_clone);
+            //                     panic!("没有该命令!mode={}", mold_clone)
+            //                 }
+            //             }
+            //         }
+            //     }
+            // });
+            // try_join!(t1, t2).unwrap();
+            error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
+            panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)
         }
         ExchangeEnum::BinanceSwap => {
             error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
@@ -192,6 +210,82 @@ pub async fn test_new_exchange_wss<T>(exchange: ExchangeEnum, symbol: &str, subs
             // });
             // try_join!(t1, t2).unwrap();
         }
+        ExchangeEnum::OkxSwap => {
+            let symbol_format = utils::format_symbol(symbol.to_string(), "-").to_uppercase();
+            let symbol_back = utils::format_symbol(symbol.to_string(), "_");
+            trace!(symbol_format);
+            let name = format!("okx_usdt_swap@{}", symbol.to_string().to_lowercase());
+            let bool_v1 = Arc::new(AtomicBool::new(true));
+
+            let (res_sender, mut res_receiver): (Sender<ResponseData>, Receiver<ResponseData>) = channel(1024);
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = env::var("okx_access_key").unwrap_or("".to_string());
+            let secret_key = env::var("okx_secret_key").unwrap_or("".to_string());
+            let passphrase = env::var("okx_passphrase").unwrap_or("".to_string());
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            params.insert("pass_key".to_string(), passphrase);
+
+            let mut exchange_wss = if ["depth", "ticker"].contains(&mold) {
+                OkxSwapWs::new_label(name, false, params, OkxWsType::Public, res_sender)
+            } else if ["account", "orders", "position"].contains(&mold) {
+                OkxSwapWs::new_label(name, false, params, OkxWsType::Private, res_sender)
+            } else {
+                OkxSwapWs::new_label(name, false, params, OkxWsType::Business, res_sender)
+            };
+
+            exchange_wss.set_subscribe(subscriber_type.into());
+
+            let t1 = tokio::spawn(async move {
+                exchange_wss.custom_subscribe(bool_v1, vec![symbol_format]).await;
+            });
+            let mold_arc = Arc::new(mold.to_string());
+            let t2 = tokio::spawn(async move {
+                let mold_clone = Arc::clone(&mold_arc);
+                loop {
+                    tokio::time::sleep(Duration::from_millis(1)).await;
+                    if let Ok(received) = res_receiver.try_recv() {
+                        match mold_clone.as_str() {
+                            "depth" => {
+                                if received.data != "" {
+                                    let result = okx_handle::handle_special_depth(received);
+                                    trace!(?result)
+                                }
+                            }
+                            "ticker" => {
+                                if received.data != "" {
+                                    let result = okx_handle::handle_special_ticker(received);
+                                    trace!(?result)
+                                }
+                            }
+                            "account" => {
+                                if received.data != "" {
+                                    let result = okx_handle::handle_account_info(received, symbol_back.clone());
+                                    trace!(?result)
+                                }
+                            }
+                            "position" => {
+                                if received.data != "" {
+                                    let result = okx_handle::handle_position(received, dec!(10));
+                                    trace!(?result)
+                                }
+                            }
+                            "orders" => {
+                                if received.data != "" {
+                                    let result = okx_handle::handle_order(received, dec!(10));
+                                    trace!(?result)
+                                }
+                            }
+                            _ => {
+                                error!("没有该命令!mode={}", mold_clone);
+                                panic!("没有该命令!mode={}", mold_clone)
+                            }
+                        }
+                    }
+                }
+            });
+            try_join!(t1, t2).unwrap();
+        }
         _ => {
             error!("该交易所不支持!test_new_exchange_wss:{:?}",exchange);
             panic!("该交易所不支持!test_new_exchange_wss:{:?}", exchange)

+ 68 - 0
standard/tests/okx_handle_test.rs

@@ -0,0 +1,68 @@
+mod exchange_test;
+
+use tracing::{instrument};
+use exchanges::okx_swap_ws::OkxSubscribeType;
+use standard::exchange::ExchangeEnum;
+use crate::exchange_test::test_new_exchange_wss;
+
+const SYMBOL: &str = "ARB_USDT";
+
+// 测试订阅深度订阅
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_depth() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PuBooks50L2tbt
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "depth").await;
+}
+
+// 测试订阅Ticker信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_ticker() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PuBooks5
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "ticker").await;
+}
+
+// 测试订阅Account信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_account() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PrAccount("USDT".to_string())
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "account").await;
+}
+
+// 测试订阅Position信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_position() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PrBalanceAndPosition
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "position").await;
+}
+
+// 测试订阅Orders信息
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+#[instrument(level = "TRACE")]
+async fn test_get_wss_orders() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_subscribe_type = vec![
+        OkxSubscribeType::PrOrders
+    ];
+    test_new_exchange_wss(ExchangeEnum::OkxSwap, SYMBOL, okx_subscribe_type, "orders").await;
+}

+ 117 - 0
standard/tests/okx_swap_test.rs

@@ -0,0 +1,117 @@
+use tracing::{instrument, trace};
+use standard::exchange::ExchangeEnum;
+use standard::Platform;
+use crate::exchange_test::test_new_exchange;
+
+mod exchange_test;
+const SYMBOL: &str = "BLZ_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_exchange = okx_swap_exchange.get_self_exchange();
+    trace!(?okx_get_self_exchange);
+}
+
+// 测试获取交易对信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_symbol() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_symbol = okx_swap_exchange.get_self_symbol();
+    trace!(?okx_get_self_symbol);
+}
+
+// 测试获取是否使用高速通道
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_is_colo() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_is_colo = okx_swap_exchange.get_self_is_colo();
+    trace!(?okx_get_self_is_colo);
+}
+
+// 测试获取登录params信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_params() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_params = okx_swap_exchange.get_self_params();
+    trace!("okx_get_self_params={:?}",okx_get_self_params);
+}
+
+// 测试获取Market信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_market() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_self_market = okx_swap_exchange.get_self_market();
+    trace!(?okx_get_self_market);
+}
+
+// 测试获取请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_delays() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_request_delays = okx_swap_exchange.get_request_delays();
+    trace!(?okx_get_request_delays);
+}
+
+// 测试获取请求平均时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_avg_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_request_avg_delay = okx_swap_exchange.get_request_avg_delay();
+    trace!(?okx_get_request_avg_delay);
+}
+
+// 测试获取最大请求时间信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_request_max_delay() {
+    global::log_utils::init_log_with_trace();
+
+    let okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_request_max_delay = okx_swap_exchange.get_request_max_delay();
+    trace!(?okx_get_request_max_delay);
+}
+
+// 测试获取服务器时间
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_server_time() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_server_time = okx_swap_exchange.get_server_time().await;
+    trace!(?okx_get_server_time);
+}
+
+// 测试获取账号信息
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_account() {
+    global::log_utils::init_log_with_trace();
+
+    let mut okx_swap_exchange: Box<dyn Platform> = test_new_exchange(ExchangeEnum::OkxSwap, SYMBOL).await;
+    let okx_get_account = okx_swap_exchange.get_account().await;
+    trace!(?okx_get_account);
+}