Bladeren bron

添加bingx数据收集

DESKTOP-NE65RNK\Citrus_limon 1 jaar geleden
bovenliggende
commit
cc8a66b8b8

+ 122 - 0
src/bingx_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 tracing::info;
+use exchanges::bingx_swap_rest::BingxSwapRest;
+use exchanges::bingx_swap_ws::{BingxSwapSubscribeType, BingxSwapWs, BingxSwapWsType};
+use exchanges::response_base::ResponseData;
+use serde_json::json;
+use standard::exchange::ExchangeEnum;
+use standard::exchange_struct_handler::ExchangeStructHandler;
+use crate::listener_tools::{RecordMap, TradeMap, update_record, update_trade};
+
+const EXCHANGE_NAME: &str = "bingx_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 = "bingx_usdt_swap_listener";
+    // 订阅所有币种
+    let login = BTreeMap::new();
+    let mut bingx_rest = BingxSwapRest::new(false, login);
+    let params = json!({});
+    let response = bingx_rest.get_market(params).await;
+    let mut symbols = vec![];
+    if response.code == 200 {
+        let symbol_infos = response.data.as_array().unwrap();
+        let mut mul_map = MUL_MAP.lock().await;
+        for symbol_info in symbol_infos {
+            // quanto_multiplier是ct_val
+            let symbol = symbol_info["symbol"].as_str().unwrap().replace("-", "_");
+            let mul = Decimal::ONE;
+            mul_map.insert(symbol.clone(), mul);
+
+            symbols.push(symbol)
+        }
+    }
+
+    for chunk in symbols.chunks(20) {
+        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 = BingxSwapWs::new_with_tag(ws_name, false, None, BingxSwapWsType::PublicAndPrivate);
+            ws.set_subscribe(vec![
+                BingxSwapSubscribeType::PuFuturesTrades,
+                BingxSwapSubscribeType::PuFuturesRecords,
+                // BingxSwapSubscribeType::PuFuturesOrderBook
+            ]);
+
+            // 建立链接
+            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::BingxSwap, &response);
+            //
+            // update_depth(&depth).await;
+        }
+        // 订单流数据
+        "futures.trades" => {
+            let mut trades = ExchangeStructHandler::trades_handle(ExchangeEnum::BingxSwap, &response);
+            let mul_map = MUL_MAP.lock().await;
+
+            for trade in trades.iter_mut() {
+                // 真实交易量处理,因为bingx的量都是张数
+                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::BingxSwap, &response);
+
+            let mul_map = MUL_MAP.lock().await;
+            for record in records.iter_mut() {
+                // 真实交易量处理,因为bingx的量都是张数
+                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)
+        }
+    }
+}

+ 7 - 5
src/main.rs

@@ -7,6 +7,7 @@ mod gate_usdt_swap_data_listener;
 mod binance_usdt_swap_data_listener;
 mod coinex_usdt_swap_data_listener;
 mod htx_usdt_swap_data_listener;
+mod bingx_usdt_swap_data_listener;
 
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};
@@ -31,11 +32,12 @@ async fn main() {
     // ctrl c退出检查程序
     control_c::exit_handler(running.clone());
     // 启动各交易所的数据监听器
-    binance_usdt_swap_data_listener::run_listener(running.clone()).await;
-    gate_usdt_swap_data_listener::run_listener(running.clone()).await;
-    bitget_usdt_swap_data_listener::run_listener(running.clone()).await;
-    coinex_usdt_swap_data_listener::run_listener(running.clone()).await;
-    htx_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // binance_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // gate_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // bitget_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // coinex_usdt_swap_data_listener::run_listener(running.clone()).await;
+    // htx_usdt_swap_data_listener::run_listener(running.clone()).await;
+    bingx_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

@@ -172,7 +172,8 @@ async fn get_exchanges() -> impl Responder {
         "bitget_usdt_swap",
         "binance_usdt_swap",
         "coinex_usdt_swap",
-        "htx_usdt_swap"
+        "htx_usdt_swap",
+        "bingx_usdt_swap"
     ];
     let response_data = json!(exchanges);
 

+ 319 - 0
standard/src/bingx_swap.rs

@@ -0,0 +1,319 @@
+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, MathematicalOps};
+use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
+use serde_json::{json, Value};
+use tracing::{error, info};
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, PositionModeEnum, utils};
+use exchanges::bingx_swap_rest::BingxSwapRest;
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct BingxSwap {
+    exchange: ExchangeEnum,
+    symbol: String,
+    is_colo: bool,
+    params: BTreeMap<String, String>,
+    request: BingxSwapRest,
+    market: Market,
+    order_sender: Sender<Order>,
+    error_sender: Sender<Error>,
+}
+
+impl BingxSwap {
+    pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BingxSwap {
+        let market = Market::new();
+        let mut bingx_swap = BingxSwap {
+            exchange: ExchangeEnum::BingxSwap,
+            symbol: symbol.to_uppercase(),
+            is_colo,
+            params: params.clone(),
+            request: BingxSwapRest::new(is_colo, params.clone()),
+            market,
+            order_sender,
+            error_sender,
+        };
+
+        // 修改持仓模式
+        let symbol_array: Vec<&str> = symbol.split("_").collect();
+        let mode_result = bingx_swap.set_dual_mode(symbol_array[1], true).await;
+        match mode_result {
+            Ok(ok) => {
+                info!("Bingx:设置持仓模式成功!{:?}", ok);
+            }
+            Err(error) => {
+                error!("Bingx:设置持仓模式失败!mode_result={}", error)
+            }
+        }
+        // 获取市场信息
+        bingx_swap.market = BingxSwap::get_market(&mut bingx_swap).await.unwrap_or(bingx_swap.market);
+        return bingx_swap;
+    }
+}
+
+#[async_trait]
+impl Platform for BingxSwap {
+    // 克隆方法
+    fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
+    // 获取交易所模式
+    fn get_self_exchange(&self) -> ExchangeEnum {
+        ExchangeEnum::BingxSwap
+    }
+    // 获取交易对
+    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, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+    // 获取账号信息
+    async fn get_account(&mut self) -> Result<Account, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 获取持仓信息
+    async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+    // 获取所有持仓
+    async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+    // 获取市场行情
+    async fn get_ticker(&mut self) -> Result<Ticker, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn get_ticker_symbol(&mut self, _symbol: String) -> Result<Ticker, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_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.as_array().unwrap();
+            let market_info = res_data_json.iter().find(|item| item["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("bingx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let name = value["symbol"].as_str().unwrap();
+                    let name_array: Vec<&str> = name.split("-").collect();
+                    let price_precision = Decimal::from_str(&value["pricePrecision"].to_string()).unwrap();
+                    let tick_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * price_precision);
+                    let amount_precision = Decimal::from_str(&value["quantityPrecision"].to_string()).unwrap();
+                    let amount_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * amount_precision);
+                    let min_qty = Decimal::from_str(&value["tradeMinUSDT"].to_string()).unwrap();
+                    let max_qty = Decimal::NEGATIVE_ONE;
+                    let ct_val = Decimal::ONE;
+
+                    let result = Market {
+                        symbol: name_array.join("_"),
+                        base_asset: name_array[0].to_string(),
+                        quote_asset: name_array[1].to_string(),
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional: min_qty,
+                        max_notional: max_qty,
+                        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 = utils::format_symbol(symbol.clone(), "-");
+        let params = json!({
+            "symbol": symbol_format.clone()
+        });
+        let res_data = self.request.get_market(params).await;
+        if res_data.code == 200 {
+            let res_data_json = res_data.data.as_array().unwrap();
+            let market_info = res_data_json.iter().find(|item| item["symbol"].as_str().unwrap() == symbol_format);
+            match market_info {
+                None => {
+                    error!("bingx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
+                    Err(Error::new(ErrorKind::Other, res_data.to_string()))
+                }
+                Some(value) => {
+                    let name = value["symbol"].as_str().unwrap();
+                    let name_array: Vec<&str> = name.split("-").collect();
+                    let price_precision = Decimal::from_str(&value["pricePrecision"].to_string()).unwrap();
+                    let tick_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * price_precision);
+                    let amount_precision = Decimal::from_str(&value["quantityPrecision"].to_string()).unwrap();
+                    let amount_size = Decimal::TEN.powd(Decimal::NEGATIVE_ONE * amount_precision);
+                    let min_qty = Decimal::from_str(&value["tradeMinUSDT"].to_string()).unwrap();
+                    let max_qty = Decimal::NEGATIVE_ONE;
+                    let ct_val = Decimal::ONE;
+
+                    let result = Market {
+                        symbol: name_array.join("_"),
+                        base_asset: name_array[0].to_string(),
+                        quote_asset: name_array[1].to_string(),
+                        tick_size,
+                        amount_size,
+                        price_precision,
+                        amount_precision,
+                        min_qty,
+                        max_qty,
+                        min_notional: min_qty,
+                        max_notional: max_qty,
+                        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, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+    // 获取订单列表
+    async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_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, "bingx_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, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 撤销订单
+    async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+    // 批量撤销订单
+    async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_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, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 设置持仓模式
+    async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    // 更新双持仓模式下杠杆
+    async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+
+    async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "bingx:该交易所方法未实现".to_string())) }
+
+    // 交易账户互转
+    async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
+        Err(Error::new(ErrorKind::NotFound, "bingx_swap:该交易所方法未实现".to_string()))
+    }
+}
+
+pub fn format_position_item(position: &Value, ct_val: Decimal) -> Position {
+    let mut position_mode = match position["mode"].as_str().unwrap_or("") {
+        "single" => PositionModeEnum::Both,
+        "dual_long" => PositionModeEnum::Long,
+        "dual_short" => PositionModeEnum::Short,
+        _ => {
+            error!("bingx_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
+            panic!("bingx_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
+        }
+    };
+    let size = Decimal::from_str(&position["size"].to_string()).unwrap();
+    let amount = size * ct_val;
+    match position_mode {
+        PositionModeEnum::Both => {
+            position_mode = match amount {
+                amount if amount > Decimal::ZERO => PositionModeEnum::Long,
+                amount if amount < Decimal::ZERO => PositionModeEnum::Short,
+                _ => { PositionModeEnum::Both }
+            }
+        }
+        _ => {}
+    }
+    Position {
+        symbol: position["contract"].as_str().unwrap_or("").parse().unwrap(),
+        margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
+        amount,
+        frozen_amount: Decimal::ZERO,
+        price: Decimal::from_str(position["entry_price"].as_str().unwrap()).unwrap(),
+        profit: Decimal::from_str(position["unrealised_pnl"].as_str().unwrap()).unwrap(),
+        position_mode,
+        margin: Decimal::from_str(position["margin"].as_str().unwrap()).unwrap(),
+    }
+}
+
+pub fn format_order_item(order: Value, ct_val: Decimal) -> Order {
+    let status = order["status"].as_str().unwrap_or("");
+    let text = order["text"].as_str().unwrap_or("");
+    let size = Decimal::from_str(&order["size"].to_string()).unwrap();
+    let left = Decimal::from_str(&order["left"].to_string()).unwrap();
+
+    let amount = size * ct_val;
+    let deal_amount = (size - left) * ct_val;
+    let custom_status = if status == "finished" { "REMOVE".to_string() } else if status == "open" { "NEW".to_string() } else {
+        error!("bingx_swap:格式化订单状态错误!\nformat_order_item:order={:?}", order);
+        panic!("bingx_swap:格式化订单状态错误!\nformat_order_item:order={:?}", order)
+    };
+    let rst_order = Order {
+        id: order["id"].to_string(),
+        custom_id: text.replace("t-my-custom-id_", "").replace("t-", ""),
+        price: Decimal::from_str(order["price"].as_str().unwrap()).unwrap(),
+        amount,
+        deal_amount,
+        avg_price: Decimal::from_str(&order["fill_price"].as_str().unwrap()).unwrap(),
+        status: custom_status,
+        order_type: "limit".to_string(),
+    };
+    return rst_order;
+}

+ 104 - 0
standard/src/bingx_swap_handle.rs

@@ -0,0 +1,104 @@
+use std::str::FromStr;
+use chrono::Utc;
+use rust_decimal::Decimal;
+use rust_decimal::prelude::FromPrimitive;
+use serde_json::Value;
+use exchanges::response_base::ResponseData;
+use crate::{Account, OrderBook, Order, Position, SpecialOrder, Trade, Record};
+
+// 处理账号信息
+pub fn handle_account_info(res_data: &ResponseData, symbol: &String) -> Account {
+    Account::new()
+}
+
+pub fn format_account_info(_data: &Vec<Value>, _symbol: &String) -> Account {
+    Account::new()
+}
+
+// 处理position信息
+pub fn handle_position(_res_data: &ResponseData, _ct_val: &Decimal) -> Vec<Position> {
+    vec![Position::new()]
+}
+
+pub fn format_position_item(_position: &Value, _ct_val: &Decimal) -> Position {
+    Position::new()
+}
+
+// 处理order信息
+pub fn handle_order(_res_data: &ResponseData, _ct_val: Decimal) -> SpecialOrder {
+    SpecialOrder::new()
+}
+
+pub fn format_order_item(_order: Value, _ct_val: Decimal) -> Order {
+    Order::new()
+}
+// 处理特殊Ticket信息
+// pub fn handle_book_ticker(res_data: &ResponseData) -> SpecialDepth {
+//     let bp = Decimal::from_str((*res_data).data["b"].as_str().unwrap()).unwrap();
+//     let bq = Decimal::from_f64((*res_data).data["B"].as_f64().unwrap()).unwrap();
+//     let ap = Decimal::from_str((*res_data).data["a"].as_str().unwrap()).unwrap();
+//     let aq = Decimal::from_f64((*res_data).data["A"].as_f64().unwrap()).unwrap();
+//     let mp = (bp + ap) * dec!(0.5);
+//     let t = Decimal::from_u64((*res_data).data["u"].as_u64().unwrap()).unwrap();
+//     let create_at = (*res_data).data["t"].as_i64().unwrap() * 1000;
+//
+//     let ticker_info = SpecialTicker { sell: ap, buy: bp, mid_price: mp, t, create_at };
+//     let depth_info = vec![bp, bq, ap, aq];
+//
+//     SpecialDepth {
+//         name: (*res_data).tag.clone(),
+//         depth: depth_info,
+//         ticker: ticker_info,
+//         t,
+//         create_at,
+//     }
+// }
+
+pub fn handle_records(value: &Value) -> Vec<Record> {
+    let mut records = vec![];
+    let symbol = value["s"].as_str().unwrap().replace("-", "_");
+
+    for record_value in value["data"].as_array().unwrap() {
+        records.push(Record {
+            time: Decimal::from_i64(Utc::now().timestamp_millis()).unwrap(),
+            open: Decimal::from_str(record_value["o"].as_str().unwrap()).unwrap(),
+            high: Decimal::from_str(record_value["h"].as_str().unwrap()).unwrap(),
+            low: Decimal::from_str(record_value["l"].as_str().unwrap()).unwrap(),
+            close: Decimal::from_str(record_value["c"].as_str().unwrap()).unwrap(),
+            volume: Decimal::from_str(record_value["v"].as_str().unwrap()).unwrap(),
+            symbol: symbol.clone(),
+        });
+    }
+
+    return 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["p"].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_f64(value["s"].as_f64().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["m"] == true;
+        let size = Decimal::from_str(item["q"].as_str().unwrap()).unwrap();
+        trades.push(Trade {
+            id: item["T"].to_string(),
+            time: Decimal::from_i64(item["T"].as_i64().unwrap()).unwrap(),
+            size: if side { -size } else { size },
+            price: Decimal::from_str(item["p"].as_str().unwrap().to_string().as_str()).unwrap(),
+            symbol: item["s"].as_str().unwrap().to_string().replace("-", "_"),
+        })
+    }
+
+    return trades;
+}

+ 6 - 1
standard/src/exchange.rs

@@ -9,6 +9,7 @@ use crate::bybit_swap::BybitSwap;
 use crate::bitget_swap::BitgetSwap;
 use crate::coinex_swap::CoinexSwap;
 use crate::htx_swap::HtxSwap;
+use crate::bingx_swap::BingxSwap;
 
 /// 交易所交易模式枚举
 /// - `BinanceSwap`: Binance交易所期货;
@@ -29,7 +30,8 @@ pub enum ExchangeEnum {
     // BitgetSpot,
     BitgetSwap,
     BybitSwap,
-    HtxSwap
+    HtxSwap,
+    BingxSwap
 }
 
 /// Exchange结构体
@@ -107,6 +109,9 @@ impl Exchange {
             ExchangeEnum::HtxSwap => {
                 Box::new(HtxSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
             }
+            ExchangeEnum::BingxSwap => {
+                Box::new(BingxSwap::new(symbol, is_colo, params, order_sender, error_sender).await)
+            }
         }
     }
 }

+ 12 - 1
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, kucoin_handle, htx_swap_handle};
+use crate::{binance_swap_handle, gate_swap_handle, bybit_swap_handle, bitget_swap_handle, coinex_swap_handle, kucoin_handle, htx_swap_handle, bingx_swap_handle};
 use crate::{Record, Ticker, Trade, Depth};
 use crate::{Account, OrderBook, Position, SpecialOrder};
 
@@ -48,6 +48,11 @@ impl ExchangeStructHandler {
                 depth_bids = bybit_swap_handle::format_depth_items(res_data.data["b"].clone());
                 t = Decimal::from_i64(res_data.reach_time).unwrap();
             }
+            ExchangeEnum::BingxSwap => {
+                depth_asks = bingx_swap_handle::format_depth_items(&res_data.data["asks"].clone());
+                depth_bids = bingx_swap_handle::format_depth_items(&res_data.data["bids"].clone());
+                t = Decimal::from_i64(res_data.reach_time).unwrap();
+            }
             _ => {
                 error!("未找到该交易所!order_book_handle: {:?}", exchange);
                 panic!("未找到该交易所!order_book_handle: {:?}", exchange);
@@ -79,6 +84,9 @@ impl ExchangeStructHandler {
             ExchangeEnum::HtxSwap => {
                 htx_swap_handle::format_trade_items(&res_data)
             },
+            ExchangeEnum::BingxSwap => {
+                bingx_swap_handle::format_trade_items(&res_data)
+            },
             _ => {
                 error!("未找到该交易所!trades_handle: {:?}", exchange);
                 panic!("未找到该交易所!trades_handle: {:?}", exchange);
@@ -109,6 +117,9 @@ impl ExchangeStructHandler {
             ExchangeEnum::HtxSwap => {
                 htx_swap_handle::handle_records(&res_data.data)
             }
+            ExchangeEnum::BingxSwap => {
+                bingx_swap_handle::handle_records(&res_data.data)
+            }
             _ => {
                 error!("未找到该交易所!records_handle: {:?}", exchange);
                 panic!("未找到该交易所!records_handle: {:?}", exchange);

+ 2 - 0
standard/src/lib.rs

@@ -37,6 +37,8 @@ mod coinex_swap;
 mod coinex_swap_handle;
 mod htx_swap_handle;
 mod htx_swap;
+mod bingx_swap_handle;
+mod bingx_swap;
 
 /// 持仓模式枚举
 /// - `Both`:单持仓方向