use std::cmp::Ordering; use std::str::FromStr; use rust_decimal::{Decimal}; use rust_decimal::prelude::FromPrimitive; use serde_json::Value; use tokio::time::Instant; use tracing::{error}; use crate::public_params; use crate::model::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker}; use crate::response_base::ResponseData; use crate::trace_stack::TraceStack; #[derive(Debug, Clone, PartialEq, Eq)] pub struct DepthParam { pub depth_asks: Vec, pub depth_bids: Vec, pub t: Decimal, pub create_at: i64 } // 处理账号信息 pub fn handle_account_info(res_data: &ResponseData, symbol: &String) -> Account { let res_data_json = res_data.data.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["text"].as_str().unwrap().contains(&symbol_upper)); match balance_info { None => { error!("Gate:格式化账号信息错误!\nformat_account_info: data={:?}", data); panic!("Gate:格式化账号信息错误!\nformat_account_info: data={:?}", data) } Some(value) => { let balance = Decimal::from_str(&value["balance"].as_f64().unwrap().to_string()).unwrap(); 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.as_array().unwrap(); res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect() } 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!("Gate:格式化持仓模式错误!\nformat_position_item:position={:?}", position); panic!("Gate:格式化持仓模式错误!\nformat_position_item:position={:?}", position) } }; let size = Decimal::from_str(&position["size"].as_f64().unwrap().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().to_string(), margin_level: Decimal::from_str(&position["leverage"].as_f64().unwrap().to_string()).unwrap(), amount, frozen_amount: Decimal::ZERO, price: Decimal::from_str(&position["entry_price"].as_f64().unwrap().to_string()).unwrap(), profit: Decimal::from_str(&position["realised_pnl"].as_f64().unwrap().to_string()).unwrap(), position_mode, margin: Decimal::from_str(&position["margin"].as_f64().unwrap().to_string()).unwrap(), } } // 处理order信息 pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder { let res_data_json = res_data.data.as_array().unwrap(); let mut order_info = Vec::new(); for item in res_data_json.iter() { order_info.push(format_order_item(item.clone(), ct_val)); }; SpecialOrder { name: res_data.label, order: order_info, } } pub fn format_order_item(order: serde_json::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_f64(order["size"].as_f64().unwrap()).unwrap(); let left = Decimal::from_f64(order["left"].as_f64().unwrap()).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!("Gate:格式化订单状态错误!\nformat_order_item:order={:?}", order); panic!("Gate:格式化订单状态错误!\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_f64(order["price"].as_f64().unwrap()).unwrap(), amount, deal_amount, avg_price: Decimal::from_f64(order["fill_price"].as_f64().unwrap()).unwrap(), status: custom_status, order_type: "limit".to_string(), trace_stack: TraceStack::new(0, Instant::now()).on_special("120 gate_handle".to_string()), }; return rst_order; } // 处理特殊Ticket信息 pub fn handle_book_ticker(res_data: &ResponseData) -> SpecialDepth { let bp = Decimal::from_str((*res_data).data["b"].as_str().unwrap_or("0")).unwrap_or(Decimal::ZERO); 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_or("0")).unwrap_or(Decimal::ZERO); let aq = Decimal::from_f64((*res_data).data["A"].as_f64().unwrap()).unwrap(); let mp = (bp + ap) * Decimal::from_str("0.5").unwrap(); 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).label.clone(), depth: depth_info, ticker: ticker_info, t, create_at, } } pub fn format_depth_items(value: &Value) -> Vec { let mut depth_items: Vec = vec![]; for value in value.as_array().unwrap() { depth_items.push(MarketOrder { price: Decimal::from_str(value["p"].as_str().unwrap()).unwrap(), amount: Decimal::from_f64(value["s"].as_f64().unwrap()).unwrap(), }) } depth_items } // 处理深度信息 pub fn handle_special_depth(res_data: &ResponseData) -> SpecialDepth { let label = res_data.label.clone(); // 格式化 let mut format_depth = format_depth(res_data); // 运算、组装 make_special_depth(label, &mut format_depth.depth_asks, &mut format_depth.depth_bids, format_depth.t, format_depth.create_at) } pub fn format_depth(res_data: &ResponseData) -> DepthParam { let depth_asks: Vec = format_depth_items(&res_data.data["asks"]); let depth_bids: Vec = format_depth_items(&res_data.data["bids"]); // todo! 有id可以取 保证与py一致 let t: Decimal = Decimal::from_str(&res_data.data["t"].to_string()).unwrap(); let create_at: i64 = res_data.data["t"].as_i64().unwrap() * 1000; DepthParam { depth_asks, depth_bids, t, create_at } } pub fn make_special_depth(label: String, depth_asks: &mut Vec, depth_bids: &mut Vec, t: Decimal, create_at: i64) -> SpecialDepth { depth_asks.sort_by(|a, b| a.price.partial_cmp(&b.price).unwrap_or(Ordering::Equal)); depth_bids.sort_by(|a, b| b.price.partial_cmp(&a.price).unwrap_or(Ordering::Equal)); // TODO 不排序的话,有4us可以省下来。 let mp = (depth_asks[0].price + depth_bids[0].price) * Decimal::from_str("0.5").unwrap(); // 每1权重需要多少价格距离,0.001代表0.1%,每0.1%代表1权重 0.001 let step = (Decimal::new(1, 3) * mp / Decimal::from_usize(public_params::LEVEL).unwrap()).round_dp(mp.scale()); let mut ap = Vec::new(); let mut bp = Vec::new(); let mut av: Vec = Vec::new(); let mut bv: Vec = Vec::new(); for i in 0..public_params::LEVEL { let price = (depth_asks[0].price + step * Decimal::from_f64(i as f64).unwrap()).round_dp(depth_asks[0].price.scale()); ap.push(price); } for i in 0..public_params::LEVEL { let price = (depth_bids[0].price - step * Decimal::from_f64(i as f64).unwrap()).round_dp(depth_bids[0].price.scale()); bp.push(price); } let mut ap_price_tag = depth_asks[0].price + step; let mut ap_index = 0; for item in depth_asks.iter() { let price = item.price; let amount = item.amount; if av.get(ap_index).is_none() { av.push(Decimal::ZERO) }; if price < ap_price_tag { av[ap_index] += amount; } else { ap_price_tag += step; ap_index += 1; if ap_index == public_params::LEVEL { break; } av[ap_index] += amount } } let mut bp_price_tag = depth_bids[0].price - step; let mut bp_index = 0; for item in depth_bids.iter() { let price = item.price; let amount = item.amount; if bv.get(bp_index).is_none() { bv.push(Decimal::ZERO) }; if price > bp_price_tag { bv[bp_index] += amount; } else { bp_price_tag -= step; bp_index += 1; if bp_index == public_params::LEVEL { break; } bv[bp_index] += amount } } let ticker_info = SpecialTicker { sell: depth_asks[0].price, buy: depth_bids[0].price, mid_price: mp, t, create_at }; let depth_info = bp.iter().cloned().chain(bv.iter().cloned()).chain(ap.iter().cloned()).chain(av.iter().cloned()).collect(); SpecialDepth { name: label, depth: depth_info, ticker: ticker_info, t, create_at, } }