use std::str::FromStr; use rust_decimal::Decimal; use rust_decimal_macros::dec; use serde::{Deserialize, Serialize}; use tracing::trace; use exchanges::response_base::ResponseData; use global::trace_stack::TraceStack; use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker}; use crate::exchange::ExchangeEnum; use crate::handle_info::HandleSwapInfo; use crate::okx_swap::SwapPosition; #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] struct SwapBalanceAndPositionSubscribe { pos_data: Vec, } #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] struct SwapBalanceAndPositionPosDataSubscribe { pos_id: String, trade_id: String, inst_id: String, inst_type: String, mgn_mode: String, pos_side: String, pos: Decimal, ccy: String, pos_ccy: String, avg_px: Decimal, u_time: String, } #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] struct SwapPositionSubscribe { arg: SwapPositionSubscribeArg, data: Vec, } #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] struct SwapPositionSubscribeArg { channel: String, uid: String, inst_type: String, } // 处理账号信息 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, symbol) } pub fn format_account_info(data: serde_json::Value, symbol: String) -> Account { let symbol_upper = symbol.to_uppercase(); let symbol_array: Vec<&str> = symbol_upper.split("_").collect(); Account { coin: symbol_array[1].to_string(), 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: Decimal::ZERO, available_stocks: Decimal::ZERO, frozen_stocks: Decimal::ZERO, } } // 处理order信息 pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder { let res_data_str = res_data.data; let res_data_json: Vec = 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(), ct_val)); } trace!(?order_info); SpecialOrder { name: res_data.label, order: order_info, } } // 处理订单信息 pub fn format_order_item(order: serde_json::Value, ct_val: 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 * ct_val; let deal_amount = filled_size * ct_val; 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: TraceStack::default().on_special("77 okx_handle".to_string()), } } // 处理特殊深度数据 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 { let mut depth_items: Vec = 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 t = Decimal::from_str(&data["seqId"].to_string()).unwrap(); let create_at = data["ts"].as_str().unwrap().parse::().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: label, depth: depth_info, ticker: ticker_info, t, create_at, } } // 处理position信息 pub fn handle_position(res_data: ResponseData, ct_val: Decimal) -> Vec { let res_data_str = res_data.data; let data_list: SwapBalanceAndPositionSubscribe = serde_json::from_str(&res_data_str).unwrap(); let position_data = data_list.pos_data; position_data.iter().map(|item| format_position_item(item, ct_val)).collect() } pub fn format_position_item(value: &SwapBalanceAndPositionPosDataSubscribe, ct_val: Decimal) -> Position { let position_mode = match value.pos_side.as_str() { "long" => { PositionModeEnum::Long } "short" => { PositionModeEnum::Short } _ => { PositionModeEnum::Both } }; Position { symbol: value.inst_id.replace("-SWAP", ""), margin_level: Decimal::ZERO, amount: value.pos * ct_val, frozen_amount: Decimal::ZERO, price: value.avg_px, profit: Decimal::ZERO, position_mode, margin: Decimal::ZERO, } }