use std::str::FromStr; use rust_decimal::Decimal; use rust_decimal::prelude::FromPrimitive; use rust_decimal_macros::dec; use serde_json::Value; use tokio::time::Instant; use tracing::{error, info}; use tracing_subscriber::fmt::format; use exchanges::response_base::ResponseData; use global::trace_stack::TraceStack; use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker}; // 处理账号信息 pub fn handle_account_info(res_data: &ResponseData, symbol: &String) -> Account { let res_data_json = res_data.data["balance_list"].as_array().unwrap(); format_account_info(res_data_json, symbol) } pub fn format_account_info(data: &Vec, symbol: &String) -> Account { let symbol_upper = symbol.to_uppercase(); let symbol_array: Vec<&str> = symbol_upper.split("_").collect(); let balance_info = data.iter().find(|&item| item["ccy"].as_str().unwrap().contains(symbol_array[1])); match balance_info { None => { error!("Coinex:格式化账号信息错误!\nformat_account_info: data={:?}", data); panic!("Coinex:格式化账号信息错误!\nformat_account_info: data={:?}", data) } Some(value) => { let frozen_balance= Decimal::from_str(&value["frozen"].as_str().unwrap()).unwrap(); let available_balance = Decimal::from_str(&value["available"].as_str().unwrap()).unwrap(); let margin = Decimal::from_str(&value["margin"].as_str().unwrap()).unwrap(); let profit_unreal = Decimal::from_str(&value["unrealized_pnl"].as_str().unwrap()).unwrap(); let balance = frozen_balance + available_balance + margin + profit_unreal; Account { coin: symbol_array[1].to_string(), balance, available_balance: Decimal::ZERO, frozen_balance: Decimal::ZERO, stocks: Decimal::ZERO, available_stocks: Decimal::ZERO, frozen_stocks: Decimal::ZERO, } } } } // 处理position信息 pub fn handle_position(res_data: &ResponseData, ct_val: &Decimal) -> Vec { let res_data_json = &res_data.data["position"]; let position = format_position_item(res_data_json, ct_val); vec![position] } pub fn format_position_item(position: &Value, ct_val: &Decimal) -> Position { let mut position_mode = match position["side"].as_str().unwrap_or("") { "long" => PositionModeEnum::Long, "short" => PositionModeEnum::Short, _ => { error!("Coinex:格式化持仓模式错误!\nformat_position_item:position={:?}", position); panic!("Coinex:格式化持仓模式错误!\nformat_position_item:position={:?}", position) } }; let size = Decimal::from_str(&position["open_interest"].as_str().unwrap()).unwrap(); let amount = size * ct_val; Position { symbol: position["market"].as_str().unwrap().to_string(), margin_level: Decimal::from_str(&position["leverage"].as_str().unwrap()).unwrap(), amount, frozen_amount: Decimal::ZERO, price: Decimal::from_str(&position["avg_entry_price"].as_str().unwrap()).unwrap(), profit: Decimal::from_str(&position["unrealized_pnl"].as_str().unwrap()).unwrap(), position_mode, margin: Decimal::from_str(&position["ath_margin_size"].as_str().unwrap()).unwrap(), } } // 处理order信息 pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder { let status = res_data.data["event"].as_str().unwrap(); let res_data_json = &res_data.data["order"]; let order_info = format_order_item(res_data_json, ct_val, status); SpecialOrder { name: res_data.label, order: vec![order_info], } } pub fn format_order_item(order: &Value, ct_val: Decimal, status: &str) -> Order { let text = order["client_id"].as_str().unwrap_or(""); let size = Decimal::from_str(order["amount"].as_str().unwrap()).unwrap(); let left = Decimal::from_str(order["unfilled_amount"].as_str().unwrap()).unwrap(); // 成交量 let filled_amount = Decimal::from_str(order["filled_amount"].as_str().unwrap_or("0")).unwrap(); // 成交额 let filled_value = Decimal::from_str(order["filled_value"].as_str().unwrap()).unwrap(); // 成交均价 let mut avg_price = Decimal::ZERO; if filled_amount != Decimal::ZERO{ avg_price = filled_value/filled_amount; } let amount = size * ct_val; let deal_amount = (size - left) * ct_val; let custom_status = if status == "finish" { "REMOVE".to_string() } else if status == "put" { "NEW".to_string() } else { "NULL".to_string() }; let rst_order = Order { id: order["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, status: custom_status, order_type: "limit".to_string(), trace_stack: TraceStack::new(0, Instant::now()).on_special("120 Coinex_handle".to_string()), }; return rst_order; } // 处理特殊Ticket信息 pub fn handle_ticker(res_data: &ResponseData) -> SpecialDepth { let depth = &res_data.data["depth"]; let bp = Decimal::from_str(depth["bids"][0][0].as_str().unwrap()).unwrap(); let bq = Decimal::from_str(depth["bids"][0][1].as_str().unwrap()).unwrap(); let ap = Decimal::from_str((depth["asks"][0][0].as_str().unwrap())).unwrap(); let aq = Decimal::from_str(depth["asks"][0][1].as_str().unwrap()).unwrap(); let mp = (bp + ap) * dec!(0.5); let t = Decimal::from_i64(depth.get("checksum").unwrap().as_i64().unwrap_or(0i64)).unwrap(); let create_at = depth.get("updated_at").unwrap().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).label.clone(), depth: depth_info, ticker: ticker_info, t, create_at, } } pub fn format_depth_items(value: &Value) -> Vec { if value.is_null() { return vec![]; } let mut depth_items: Vec = vec![]; for value in value.as_array().unwrap() { let values = value.as_array().unwrap(); depth_items.push(MarketOrder { price: Decimal::from_str(values[0].as_str().unwrap()).unwrap(), amount: Decimal::from_str(values[1].as_str().unwrap()).unwrap(), }) } return depth_items; }