Browse Source

添加cointr交易所数据收集

DESKTOP-NE65RNK\Citrus_limon 1 năm trước cách đây
mục cha
commit
8c2cbbc24c

+ 122 - 0
src/cointr_usdt_swap_data_listener.rs

@@ -0,0 +1,122 @@
+use std::collections::{BTreeMap, HashMap};
+use std::str::FromStr;
+use std::sync::{Arc};
+use std::sync::atomic::AtomicBool;
+use lazy_static::lazy_static;
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use tokio::sync::{Mutex};
+use exchanges::cointr_swap_rest::CointrSwapRest;
+use exchanges::cointr_swap_ws::{CointrSwapSubscribeType, CointrSwapWs, CointrSwapWsType};
+use exchanges::response_base::ResponseData;
+use serde_json::{json};
+use standard::exchange::ExchangeEnum;
+use standard::exchange_struct_handler::ExchangeStructHandler;
+use standard::utils::symbol_out_mapper;
+use crate::listener_tools::{RecordMap, TradeMap, update_record, update_trade};
+
+const EXCHANGE_NAME: &str = "cointr_usdt_swap";
+
+lazy_static! {
+    // static ref DEPTH_MAP: Mutex<DepthMap> = Mutex::new(HashMap::new());
+    static ref TRADES_MAP: Mutex<TradeMap> = Mutex::new(HashMap::new());
+    static ref RECORD_MAP: Mutex<RecordMap> = Mutex::new(HashMap::new());
+    static ref MUL_MAP: Mutex<HashMap<String, Decimal>> = Mutex::new(HashMap::new());
+}
+
+pub async fn run_listener(is_shutdown_arc: Arc<AtomicBool>) {
+    let name = "cointr_usdt_swap_listener";
+    // 订阅所有币种
+    let login = BTreeMap::new();
+    let mut cointr_rest = CointrSwapRest::new(false, login);
+    let params = json!({});
+    let response = cointr_rest.get_market(params).await;
+    let mut symbols = vec![];
+    if response.code == 200 {
+        let symbol_infos = response.data["data"].as_array().unwrap();
+        let mut mul_map = MUL_MAP.lock().await;
+        for symbol_info in symbol_infos {
+            if symbol_info["quoteCcy"].as_str().unwrap() != "USDT" { continue; }
+            // quanto_multiplier是ct_val
+            let symbol = symbol_info["instId"].as_str().unwrap().to_string();
+            let loc_symbol = format!("{}_{}", symbol_info["baseCcy"].as_str().unwrap(), symbol_info["quoteCcy"].as_str().unwrap());
+            let mul = Decimal::from_str(symbol_info["ctVal"].as_str().unwrap()).unwrap();
+            mul_map.insert(symbol_out_mapper(ExchangeEnum::CointrSwap, &loc_symbol), mul);
+
+            symbols.push(symbol)
+        }
+    }
+    for chunk in symbols.chunks(1) {
+        let ws_name = name.to_string();
+        let (write_tx, write_rx) = futures_channel::mpsc::unbounded();
+        let write_tx_am = Arc::new(Mutex::new(write_tx));
+        let symbols_chunk = chunk.iter().cloned().collect::<Vec<String>>();
+        let is_shutdown_clone = Arc::clone(&is_shutdown_arc);
+
+        tokio::spawn(async move {
+            let mut ws = CointrSwapWs::new_with_tag(ws_name, false, None, CointrSwapWsType::PublicAndPrivate).clone();
+            ws.set_subscribe(vec![
+                CointrSwapSubscribeType::PuFuturesTrades,
+                CointrSwapSubscribeType::PuFuturesRecords,
+                // CointrSwapSubscribeType::PuFuturesDepth
+            ]);
+
+            // 建立链接
+            ws.set_symbols(symbols_chunk);
+            ws.ws_connect_async(is_shutdown_clone, data_listener, &write_tx_am, write_rx).await.expect("链接失败(内部一个心跳线程应该已经关闭了)");
+        });
+    }
+}
+
+// 读取数据
+pub async fn data_listener(response: ResponseData) {
+    if response.code != 200 {
+        return;
+    }
+
+    match response.channel.as_str() {
+        // 深度数据
+        "futures.order_book" => {
+            // let depth = ExchangeStructHandler::order_book_handle(ExchangeEnum::CointrSwap, &response);
+            //
+            // update_depth(&depth).await;
+        }
+        // 订单流数据
+        "futures.trades" => {
+            let mut trades = ExchangeStructHandler::trades_handle(ExchangeEnum::CointrSwap, &response);
+            let mul_map = MUL_MAP.lock().await;
+
+            for trade in trades.iter_mut() {
+                // 真实交易量处理,因为cointr的量都是张数
+                let mul = mul_map[trade.symbol.as_str()];
+                let mut real_size = trade.size * mul * trade.price;
+                real_size.rescale(2);
+                trade.size = real_size;
+
+                // 更新到本地数据库
+                let trades_map = TRADES_MAP.lock().await;
+                update_trade(trade, trades_map, EXCHANGE_NAME).await;
+            }
+        }
+        // k线数据
+        "futures.candlesticks" => {
+            let mut records = ExchangeStructHandler::records_handle(ExchangeEnum::CointrSwap, &response);
+            let mul_map = MUL_MAP.lock().await;
+            for record in records.iter_mut() {
+                // 真实交易量处理,因为cointr的量都是张数
+                let mul = mul_map[record.symbol.as_str()];
+                let mid_price = (record.high + record.low) * dec!(0.5);
+                let mut real_volume = record.volume * mul * mid_price;
+                real_volume.rescale(2);
+                record.volume = real_volume;
+
+                // 更新到本地数据库
+                let record_map = RECORD_MAP.lock().await;
+                update_record(record, record_map, EXCHANGE_NAME).await;
+            }
+        }
+        _ => {
+            // info!("48 未知的数据类型: {:?}", response)
+        }
+    }
+}

+ 2 - 0
src/main.rs

@@ -15,6 +15,7 @@ mod kucoin_usdt_swap_data_listener;
 mod coinsph_usdt_swap_data_listener;
 mod phemex_usdt_swap_data_listener;
 mod woo_usdt_swap_data_listener;
+mod cointr_usdt_swap_data_listener;
 
 
 use std::sync::Arc;
@@ -54,6 +55,7 @@ async fn main() {
     htx_usdt_swap_data_listener::run_listener(running.clone()).await;
     phemex_usdt_swap_data_listener::run_listener(running.clone()).await;
     woo_usdt_swap_data_listener::run_listener(running.clone()).await;
+    cointr_usdt_swap_data_listener::run_listener(running.clone()).await;
     // panic错误捕获,panic级别的错误直接退出
     // let panic_running = running.clone();
     std::panic::set_hook(Box::new(move |panic_info| {

+ 2 - 1
src/server.rs

@@ -180,7 +180,8 @@ async fn get_exchanges() -> impl Responder {
         "coinex_usdt_swap",
         "htx_usdt_swap",
         "phemex_usdt_swap",
-        "woo_usdt_swap"
+        "woo_usdt_swap",
+        "cointr_usdt_swap",
     ];
     let response_data = json!(exchanges);
 

+ 268 - 0
standard/src/cointr_swap.rs

@@ -0,0 +1,268 @@
+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 serde_json::{json, Value};
+use tracing::{error, info};
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, utils};
+use exchanges::cointr_swap_rest::CointrSwapRest;
+use rust_decimal::prelude::FromPrimitive;
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct CointrSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: CointrSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl CointrSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> CointrSwap {
+        let market = Market::new();
+        let mut cointr_swap = CointrSwap {
+            exchange: ExchangeEnum::CointrSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: CointrSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+
+        // 修改持仓模式
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let mode_result = cointr_swap.set_dual_mode(symbol_array[1], true).await;
+        match mode_result {
+            Ok(ok) => {
+                info!("Cointr:设置持仓模式成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("Cointr:设置持仓模式失败!mode_result={}", error)
+            }
+        }
+        // 获取市场信息
+        cointr_swap.market = CointrSwap::get_market(&mut cointr_swap).await.unwrap_or(cointr_swap.market);
+        return cointr_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for CointrSwap {
+    // 克隆方法
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+    // 获取交易所模式
+    fn get_self_exchange(&self) -> ExchangeEnum {
+        ExchangeEnum::CointrSwap
+    }
+    // 获取交易对
+    fn get_self_symbol(&self) -> String { self.symbol.clone() }
+    // 获取是否使用高速通道
+    fn get_self_is_colo(&self) -> bool {
+        self.is_colo
+    }
+    // 获取params信息
+    fn get_self_params(&self) -> BTreeMap<String, String> {
+        self.params.clone()
+    }
+    // 获取market信息
+    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> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+    // 获取账号信息
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 获取持仓信息
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+    // 获取所有持仓
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+    // 获取市场行情
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_ticker_symbol(&mut self, _symbol: String) -> Result<Ticker, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_market(&mut self) -> Result<Market, Error> {
+        let symbol_format = utils::format_symbol(self.symbol.clone(), "");
+        let params = json!({});
+        let res_data = self.request.get_market(params).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data["data"].as_array().unwrap();
+            let market_info = res_data_json.iter().find(|item| item["instId"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("cointr_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let symbol = value["instId"].as_str().unwrap().to_string();
+                    let base_asset = value["baseCcy"].as_str().unwrap().to_string();
+                    let quote_asset = value["quoteCcy"].as_str().unwrap().to_string();
+
+                    let tick_size = Decimal::from_str(value["tickSz"].as_str().unwrap()).unwrap();
+                    let step: Vec<&str> = value["steps"].as_str().unwrap().split(",").collect();
+                    let amount_size = tick_size * Decimal::from_str(step[0]).unwrap();
+                    let price_precision = Decimal::from_str(value["pxPrecision"].as_str().unwrap()).unwrap();
+                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+                    let min_qty = Decimal::from_str(value["minSz"].as_str().unwrap()).unwrap();
+                    let max_qty = Decimal::from_str(value["maxSz"].as_str().unwrap()).unwrap();
+                    let min_notional = min_qty;
+                    let max_notional = max_qty;
+                    let ct_val = Decimal::from_str(value["ctVal"].as_str().unwrap()).unwrap();
+
+                    let result = Market {
+                        symbol,
+                        base_asset,
+                        quote_asset,
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional,
+                        max_notional,
+                        ct_val,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
+        let symbol_format = format!("PERP_{}", symbol.clone().to_uppercase());
+        let params = json!({});
+        let res_data = self.request.get_market(params).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data["rows"].as_array().unwrap();
+            let market_info = res_data_json.iter().find(|item| item["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("cointr_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let symbol = value["symbol"].as_str().unwrap().to_string().replace("PERP_", "");
+                    let symbol_array: Vec<&str> = symbol.split("_").collect();
+                    let base_asset = symbol_array[0].to_string();
+                    let quote_asset = symbol_array[1].to_string();
+
+                    let tick_size = Decimal::from_str(value["quote_min"].as_str().unwrap()).unwrap();
+                    let amount_size = Decimal::from_str(&value["base_min"].as_str().unwrap()).unwrap();
+                    let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
+                    let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
+                    let min_qty = Decimal::from_str(value["quote_min"].as_str().unwrap()).unwrap();
+                    let max_qty = Decimal::from_str(value["quote_max"].as_str().unwrap()).unwrap();
+                    let min_notional = Decimal::from_str(value["min_notional"].as_str().unwrap()).unwrap();
+                    let max_notional = max_qty;
+                    let ct_val = Decimal::ONE;
+
+                    let result = Market {
+                        symbol,
+                        base_asset,
+                        quote_asset,
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional,
+                        max_notional,
+                        ct_val,
+                    };
+                    Ok(result)
+                }
+            }
+        } else {
+            Err(Error::new(ErrorKind::Other, res_data.to_string()))
+        }
+    }
+
+    // 获取订单详情
+    async fn get_order_detail(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+    // 获取订单列表
+    async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+    // 下单接口
+    async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn take_order_symbol(&mut self, _symbol: String, _ct_val: Decimal, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 撤销订单
+    async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+    // 批量撤销订单
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn take_stop_loss_order(&mut self, _stop_price: Decimal, _price: Decimal, _side: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 设置持仓模式
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 更新双持仓模式下杠杆
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "cointr:该交易所方法未实现".to_string())) }
+
+    // 交易账户互转
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "cointr_swap:该交易所方法未实现".to_string()))
+    }
+}

+ 53 - 0
standard/src/cointr_swap_handle.rs

@@ -0,0 +1,53 @@
+use std::str::FromStr;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::Value;
+use exchanges::response_base::ResponseData;
+use crate::{OrderBook, Trade, Record};
+
+pub fn handle_records(value: &Value) -> Vec<Record> {
+    let mut records = vec![];
+    let records_list = value["data"].as_array().unwrap();
+
+    for record_value in records_list {
+        records.push(Record {
+            time: Decimal::from_i64(record_value[0].as_i64().unwrap() * 1000).unwrap(),
+            open: Decimal::from_str(record_value[1].as_str().unwrap()).unwrap(),
+            high: Decimal::from_str(record_value[2].as_str().unwrap()).unwrap(),
+            low: Decimal::from_str(record_value[3].as_str().unwrap()).unwrap(),
+            close: Decimal::from_str(record_value[4].as_str().unwrap()).unwrap(),
+            volume: Decimal::from_str(record_value[5].as_str().unwrap()).unwrap(),
+            symbol: value["instId"].as_str().unwrap().replace("USDT", "_USDT"),
+        });
+    }
+    records
+}
+
+pub fn format_depth_items(value: &Value) -> Vec<OrderBook> {
+    let mut depth_items: Vec<OrderBook> = vec![];
+    for value in value.as_array().unwrap() {
+        depth_items.push(OrderBook {
+            price: Decimal::from_str(value[0].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(value[1].as_str().unwrap()).unwrap(),
+        })
+    }
+    return depth_items;
+}
+
+pub fn format_trade_items(res_data: &ResponseData) -> Vec<Trade> {
+    let result = res_data.data["data"].as_array().unwrap();
+    let mut trades = vec![];
+    for item in result {
+        let side = item["side"] == "BUY";
+        let size = Decimal::from_str(item["sz"].as_str().unwrap()).unwrap();
+        trades.push(Trade {
+            id: item["tradeId"].to_string(),
+            time: Decimal::from_i64(item["mTime"].as_i64().unwrap()).unwrap(),
+            size: if side { size } else { -size },
+            price: Decimal::from_str(item["px"].as_str().unwrap()).unwrap(),
+            symbol: res_data.data["instId"].as_str().unwrap().replace("USDT", "_USDT"),
+        })
+    }
+
+    return trades;
+}

+ 6 - 1
standard/src/exchange.rs

@@ -16,6 +16,7 @@ use crate::bitmart_swap::BitMartSwap;
 use crate::coinsph_swap::CoinsphSwap;
 use crate::phemex_swap::PhemexSwap;
 use crate::woo_swap::WooSwap;
+use crate::cointr_swap::CointrSwap;
 
 
 /// 交易所交易模式枚举
@@ -43,7 +44,8 @@ pub enum ExchangeEnum {
     BitMartSwap,
     CoinsphSwap,
     PhemexSwap,
-    WooSwap
+    WooSwap,
+    CointrSwap
 }
 
 /// Exchange结构体
@@ -139,6 +141,9 @@ impl Exchange {
             ExchangeEnum::WooSwap => {
                 Box::new(WooSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
+            ExchangeEnum::CointrSwap => {
+                Box::new(CointrSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
         }
     }
 }

+ 14 - 2
standard/src/exchange_struct_handler.rs

@@ -4,7 +4,7 @@ use rust_decimal::prelude::FromPrimitive;
 use tracing::{error};
 use exchanges::response_base::ResponseData;
 use crate::exchange::ExchangeEnum;
-use crate::{binance_swap_handle, gate_swap_handle, bybit_swap_handle, bitget_swap_handle, coinex_swap_handle, htx_swap_handle, bingx_swap_handle, mexc_swap_handle, okx_swap_handle, bitmart_swap_handle, kucoin_swap_handle, coinsph_swap_handle, phemex_swap_handle, woo_swap_handle};
+use crate::{binance_swap_handle, gate_swap_handle, bybit_swap_handle, bitget_swap_handle, coinex_swap_handle, htx_swap_handle, bingx_swap_handle, mexc_swap_handle, okx_swap_handle, bitmart_swap_handle, kucoin_swap_handle, coinsph_swap_handle, phemex_swap_handle, woo_swap_handle, cointr_swap_handle};
 use crate::{Record, Ticker, Trade, Depth};
 use crate::{Account, OrderBook, Position, SpecialOrder};
 
@@ -67,7 +67,13 @@ impl ExchangeStructHandler {
                 depth_asks = woo_swap_handle::format_depth_items(&res_data.data["asks"].clone());
                 depth_bids = woo_swap_handle::format_depth_items(&res_data.data["bids"].clone());
                 t = Decimal::from_i64(res_data.data["ts"].as_i64().unwrap()).unwrap();
-                symbol = res_data.data["data"]["symbol"].as_str().unwrap().replace("PERP_","")
+                symbol = res_data.data["data"]["symbol"].as_str().unwrap().replace("PERP_", "")
+            }
+            ExchangeEnum::CointrSwap => {
+                depth_asks = cointr_swap_handle::format_depth_items(&res_data.data["asks"].clone());
+                depth_bids = cointr_swap_handle::format_depth_items(&res_data.data["bids"].clone());
+                t = Decimal::from_i64(res_data.data["pTime"].as_i64().unwrap()).unwrap();
+                symbol = res_data.data["instId"].as_str().unwrap().replace("USDT", "_USDT")
             }
             _ => {
                 error!("未找到该交易所!order_book_handle: {:?}", exchange);
@@ -124,6 +130,9 @@ impl ExchangeStructHandler {
             ExchangeEnum::WooSwap => {
                 woo_swap_handle::format_trade_items(&res_data)
             }
+            ExchangeEnum::CointrSwap => {
+                cointr_swap_handle::format_trade_items(&res_data)
+            }
             _ => {
                 error!("未找到该交易所!trades_handle: {:?}", exchange);
                 panic!("未找到该交易所!trades_handle: {:?}", exchange);
@@ -178,6 +187,9 @@ impl ExchangeStructHandler {
             ExchangeEnum::WooSwap => {
                 woo_swap_handle::handle_records(&res_data.data)
             }
+            ExchangeEnum::CointrSwap => {
+                cointr_swap_handle::handle_records(&res_data.data)
+            }
             _ => {
                 error!("未找到该交易所!records_handle: {:?}", exchange);
                 panic!("未找到该交易所!records_handle: {:?}", exchange);

+ 2 - 0
standard/src/lib.rs

@@ -49,6 +49,8 @@ mod phemex_swap;
 mod phemex_swap_handle;
 mod woo_swap;
 mod woo_swap_handle;
+mod cointr_swap;
+mod cointr_swap_handle;
 
 /// 持仓模式枚举
 /// - `Both`:单持仓方向