|
|
@@ -1,8 +1,11 @@
|
|
|
use std::str::FromStr;
|
|
|
use rust_decimal::Decimal;
|
|
|
use rust_decimal_macros::dec;
|
|
|
+use serde_json::{Value, from_str, from_value};
|
|
|
+use tracing::{debug, error};
|
|
|
use exchanges::response_base::ResponseData;
|
|
|
-use crate::{MarketOrder, SpecialDepth, SpecialTicker};
|
|
|
+use global::trace_stack::TraceStack;
|
|
|
+use crate::{Account, MarketOrder, Order, Position, PositionModeEnum, SpecialDepth, SpecialOrder, SpecialTicker};
|
|
|
use crate::exchange::ExchangeEnum;
|
|
|
use crate::handle_info::HandleSwapInfo;
|
|
|
|
|
|
@@ -10,11 +13,11 @@ use crate::handle_info::HandleSwapInfo;
|
|
|
// 处理特殊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();
|
|
|
+ let res_data_json: Value = from_str(&*res_data_str).unwrap();
|
|
|
format_special_ticker(res_data_json, res_data.label)
|
|
|
}
|
|
|
|
|
|
-pub fn format_special_ticker(data: serde_json::Value, label: String) -> SpecialDepth {
|
|
|
+pub fn format_special_ticker(data: Value, label: String) -> SpecialDepth {
|
|
|
let bp = Decimal::from_str(data["b"].as_str().unwrap()).unwrap();
|
|
|
let bq = Decimal::from_str(data["B"].as_str().unwrap()).unwrap();
|
|
|
let ap = Decimal::from_str(data["a"].as_str().unwrap()).unwrap();
|
|
|
@@ -40,7 +43,7 @@ pub fn handle_special_depth(res_data: ResponseData) -> SpecialDepth {
|
|
|
}
|
|
|
|
|
|
// 格式化深度信息
|
|
|
-pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
|
|
|
+pub fn format_depth_items(value: Value) -> Vec<MarketOrder> {
|
|
|
let mut depth_items: Vec<MarketOrder> = vec![];
|
|
|
for value in value.as_array().unwrap() {
|
|
|
depth_items.push(MarketOrder {
|
|
|
@@ -49,4 +52,119 @@ pub fn format_depth_items(value: serde_json::Value) -> Vec<MarketOrder> {
|
|
|
})
|
|
|
}
|
|
|
return depth_items;
|
|
|
+}
|
|
|
+
|
|
|
+pub fn handle_account_position_info(res_data: ResponseData, symbol: String, ct_val: Decimal) -> (Account, Vec<Position>) {
|
|
|
+ let res_data_str = res_data.data;
|
|
|
+ let res_data_json: Value = from_str(&res_data_str).unwrap();
|
|
|
+ let b: Vec<Value> = from_value(res_data_json["B"].clone()).unwrap();
|
|
|
+ let account = format_account_info(b, symbol);
|
|
|
+ let mut positions: Vec<Position> = Vec::new();
|
|
|
+ if res_data_json.get("P").is_some() {
|
|
|
+ let position_arr: Vec<Value> = from_value(res_data_json["P"].clone()).unwrap();
|
|
|
+ positions = position_arr.iter().map(|item| { format_position_item(item, ct_val) }).collect();
|
|
|
+ }
|
|
|
+ (account, positions)
|
|
|
+}
|
|
|
+
|
|
|
+pub fn format_account_info(data: Vec<Value>, symbol: String) -> Account {
|
|
|
+ if data.is_empty(){
|
|
|
+ error!("Binance:格式化usdt余额信息错误! 格式化数据为空");
|
|
|
+ }
|
|
|
+ let upper_str = symbol.to_uppercase();
|
|
|
+ let symbol_array: Vec<&str> = upper_str.split("_").collect();
|
|
|
+ let balance_info = data.iter().find(|&item| item["a"].as_str().unwrap() == symbol_array[1]);
|
|
|
+ match balance_info {
|
|
|
+ None => {
|
|
|
+ error!("Binance:格式化usdt余额信息错误!\nformat_account_info: data={:?}", balance_info);
|
|
|
+ panic!("Binance:格式化usdt余额信息错误!\nformat_account_info: data={:?}", balance_info)
|
|
|
+ }
|
|
|
+ Some(value) => {
|
|
|
+ let balance = Decimal::from_str(&value["wb"].as_str().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,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn format_position_item(position: &Value, ct_val: Decimal) -> Position {
|
|
|
+ let mut position_mode = match position["ps"].as_str().unwrap_or("") {
|
|
|
+ "BOTH" => PositionModeEnum::Both,
|
|
|
+ "LONG" => PositionModeEnum::Long,
|
|
|
+ "SHORT" => PositionModeEnum::Short,
|
|
|
+ _ => {
|
|
|
+ error!("Binance:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
|
|
|
+ panic!("Binance:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let symbol_mapper = position["s"].as_str().unwrap().to_string();
|
|
|
+ let currency = "USDT";
|
|
|
+ let coin = &symbol_mapper[..symbol_mapper.find(currency).unwrap_or(0)];
|
|
|
+ let size = Decimal::from_str(&position["pa"].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: format!{"{}_{}", coin, currency},
|
|
|
+ margin_level: Decimal::from_str(&position["leverage"].as_f64().unwrap().to_string()).unwrap(),
|
|
|
+ amount,
|
|
|
+ frozen_amount: Decimal::ZERO,
|
|
|
+ price: Decimal::from_str(&position["ep"].as_f64().unwrap().to_string()).unwrap(),
|
|
|
+ profit: Decimal::from_str(&position["up"].as_f64().unwrap().to_string()).unwrap(),
|
|
|
+ position_mode,
|
|
|
+ margin: Decimal::from_str(&position["iw"].as_f64().unwrap().to_string()).unwrap(),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// 处理order信息
|
|
|
+pub fn handle_order(res_data: ResponseData, ct_val: Decimal) -> SpecialOrder {
|
|
|
+ let res_data_str = res_data.data;
|
|
|
+ let order: Value = from_str(&*res_data_str).unwrap();
|
|
|
+ debug!("format-order-start, binance_handle");
|
|
|
+ debug!(?order);
|
|
|
+ let status = order["X"].as_str().unwrap_or("");
|
|
|
+ let text = order["c"].as_str().unwrap_or("");
|
|
|
+ let size = Decimal::from_str(order["q"].as_str().unwrap()).unwrap();
|
|
|
+ let right = Decimal::from_str(order["z"].as_str().unwrap()).unwrap();
|
|
|
+ let price = Decimal::from_str(order["p"].as_str().unwrap()).unwrap();
|
|
|
+ let amount = size * ct_val;
|
|
|
+ let avg_price = Decimal::from_str(order["ap"].as_str().unwrap()).unwrap();
|
|
|
+ let deal_amount = right * ct_val;
|
|
|
+ let custom_status = if ["CANCELED", "FILLED", "EXPIRED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else {
|
|
|
+ "NULL".to_string()
|
|
|
+ };
|
|
|
+ let rst_order = Order {
|
|
|
+ id: format!("{}", order["i"].as_str().unwrap()),
|
|
|
+ custom_id: text.replace("t-my-custom-id_", "").replace("t-", ""),
|
|
|
+ price,
|
|
|
+ amount,
|
|
|
+ deal_amount,
|
|
|
+ avg_price,
|
|
|
+ status: custom_status,
|
|
|
+ order_type: "limit".to_string(),
|
|
|
+ trace_stack: TraceStack::default().on_special("301 binance_handle".to_string()),
|
|
|
+ };
|
|
|
+
|
|
|
+ debug!(?rst_order);
|
|
|
+ debug!("format-order-end, binance_handle");
|
|
|
+ SpecialOrder {
|
|
|
+ name: res_data.label,
|
|
|
+ order: vec![rst_order],
|
|
|
+ }
|
|
|
}
|