|
|
@@ -6,8 +6,8 @@ use std::str::FromStr;
|
|
|
use async_trait::async_trait;
|
|
|
use rust_decimal::Decimal;
|
|
|
use rust_decimal_macros::dec;
|
|
|
-use tracing::error;
|
|
|
-use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand};
|
|
|
+use tokio::sync::mpsc::Sender;
|
|
|
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, utils, PositionModeEnum};
|
|
|
use exchanges::binance_swap_rest::BinanceSwapRest;
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
@@ -18,17 +18,26 @@ pub struct BinanceSwap {
|
|
|
is_colo: bool,
|
|
|
params: BTreeMap<String, String>,
|
|
|
request: BinanceSwapRest,
|
|
|
+ market: Market,
|
|
|
+ order_sender: Sender<Order>,
|
|
|
+ error_sender: Sender<Error>,
|
|
|
}
|
|
|
|
|
|
impl BinanceSwap {
|
|
|
- pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>) -> BinanceSwap {
|
|
|
- BinanceSwap {
|
|
|
+ pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> BinanceSwap {
|
|
|
+ let market = Market::new();
|
|
|
+ let mut binance_swap = BinanceSwap {
|
|
|
exchange: ExchangeEnum::BinanceSwap,
|
|
|
symbol: symbol.to_uppercase(),
|
|
|
is_colo,
|
|
|
params: params.clone(),
|
|
|
request: BinanceSwapRest::new(is_colo, params.clone()),
|
|
|
- }
|
|
|
+ market,
|
|
|
+ order_sender,
|
|
|
+ error_sender,
|
|
|
+ };
|
|
|
+ binance_swap.market = BinanceSwap::get_market(&mut binance_swap).await.unwrap_or(binance_swap.market);
|
|
|
+ return binance_swap;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -51,16 +60,25 @@ impl Platform for BinanceSwap {
|
|
|
self.params.clone()
|
|
|
}
|
|
|
// 获取market信息
|
|
|
- fn get_self_market(&self) -> Market { todo!() }
|
|
|
+ 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> {
|
|
|
- todo!()
|
|
|
+ let res_data = self.request.get_server_time().await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_str = &res_data.data;
|
|
|
+ let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
|
|
|
+ let result = res_data_json["serverTime"].to_string();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.message))
|
|
|
+ }
|
|
|
}
|
|
|
// 获取账号信息
|
|
|
async fn get_account(&mut self) -> Result<Account, Error> {
|
|
|
@@ -72,7 +90,6 @@ impl Platform for BinanceSwap {
|
|
|
let balance_info = res_data_json.iter().find(|item| item["asset"].as_str().unwrap().to_string() == symbol_array[1].to_string());
|
|
|
match balance_info {
|
|
|
None => {
|
|
|
- error!("格式化Binance账号信息错误!");
|
|
|
panic!("格式化Binance账号信息错误!\nget_account: balance_info={:?}", balance_info)
|
|
|
}
|
|
|
Some(value) => {
|
|
|
@@ -96,33 +113,128 @@ impl Platform for BinanceSwap {
|
|
|
}
|
|
|
// 获取仓位信息
|
|
|
async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
- todo!()
|
|
|
+ let symbol_format = utils::format_symbol(self.symbol.clone(), "");
|
|
|
+ let amount_size = self.market.amount_size;
|
|
|
+ let res_data = self.request.get_position_risk(symbol_format).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_str = &res_data.data;
|
|
|
+ let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+ let result = res_data_json.iter().map(|item| { format_position_item(item, amount_size) }).collect();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.message))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
- todo!()
|
|
|
+ let amount_size = self.market.amount_size;
|
|
|
+ let res_data = self.request.get_position_risk("".to_string()).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_str = &res_data.data;
|
|
|
+ let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+ let result = res_data_json.iter().map(|item| { format_position_item(item, amount_size) }).collect();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.message))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 获取市场行情
|
|
|
async fn get_ticker(&mut self) -> Result<Ticker, Error> {
|
|
|
- todo!()
|
|
|
+ let symbol_format = utils::format_symbol(self.symbol.clone(), "");
|
|
|
+ let res_data = self.request.get_book_ticker(symbol_format).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_str = &res_data.data;
|
|
|
+ let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
|
|
|
+ let result = Ticker {
|
|
|
+ time: res_data_json["time"].as_i64().unwrap(),
|
|
|
+ high: Decimal::from_str(res_data_json["askPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ low: Decimal::from_str(res_data_json["bidPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ sell: Decimal::from_str(res_data_json["askPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ buy: Decimal::from_str(res_data_json["bidPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ last: dec!(-1),
|
|
|
+ volume: dec!(-1),
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.message))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- async fn get_market(&mut self) -> Result<Market, Error> { todo!() }
|
|
|
+ async fn get_market(&mut self) -> Result<Market, Error> {
|
|
|
+ let symbol_format = utils::format_symbol(self.symbol.clone(), "");
|
|
|
+ let res_data = self.request.get_exchange_info().await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_str = &res_data.data;
|
|
|
+ let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
|
|
|
+ let symbols: Vec<serde_json::Value> = res_data_json["symbols"].as_array().unwrap().clone();
|
|
|
+ let market_info = symbols.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format);
|
|
|
+ match market_info {
|
|
|
+ None => {
|
|
|
+ panic!("Binance:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
|
|
|
+ }
|
|
|
+ Some(value) => {
|
|
|
+ let base_asset = value["baseAsset"].as_str().unwrap_or("").to_string();
|
|
|
+ let quote_asset = value["quoteAsset"].as_str().unwrap_or("").to_string();
|
|
|
+
|
|
|
+ let filter_array = value["filters"].as_array().unwrap().clone();
|
|
|
+ let price_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "PRICE_FILTER").unwrap();
|
|
|
+ let lot_size_filter = filter_array.iter().find(|&item| item["filterType"].as_str().unwrap() == "LOT_SIZE").unwrap();
|
|
|
+
|
|
|
+ let result = Market {
|
|
|
+ symbol: format!("{}_{}", base_asset, quote_asset),
|
|
|
+ base_asset,
|
|
|
+ quote_asset,
|
|
|
+ tick_size: Decimal::from_str(&price_filter["tickSize"].as_str().unwrap()).unwrap(),
|
|
|
+ amount_size: dec!(1),
|
|
|
+ price_precision: Decimal::from_str(&value["pricePrecision"].to_string()).unwrap(),
|
|
|
+ amount_precision: Decimal::from_str(&value["quantityPrecision"].to_string()).unwrap(),
|
|
|
+ min_qty: Decimal::from_str(lot_size_filter["minQty"].as_str().unwrap()).unwrap(),
|
|
|
+ max_qty: Decimal::from_str(lot_size_filter["maxQty"].as_str().unwrap()).unwrap(),
|
|
|
+ min_notional: Decimal::from_str(price_filter["minPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ max_notional: Decimal::from_str(price_filter["maxPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ ct_val: Default::default(),
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.message))
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- async fn get_order_detail(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { todo!() }
|
|
|
+ async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
|
|
|
+ let symbol_format = utils::format_symbol(self.symbol.clone(), "");
|
|
|
+ let res_data = self.request.get_order(symbol_format, order_id.parse().unwrap_or(-1), custom_id.to_string()).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_str = &res_data.data;
|
|
|
+ let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
|
|
|
|
|
|
- async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
|
|
|
- todo!()
|
|
|
+ let status = res_data_json["status"].as_str().unwrap();
|
|
|
+ let custom_status = if ["CANCELED", "EXPIRED", "FILLED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else { panic!("Binance:格式化订单状态错误!\nget_order_detail:status={:?}", status) };
|
|
|
+ let result = Order {
|
|
|
+ id: res_data_json["orderId"].to_string(),
|
|
|
+ custom_id: res_data_json["clientOrderId"].as_str().unwrap().parse().unwrap(),
|
|
|
+ price: Decimal::from_str(res_data_json["price"].as_str().unwrap()).unwrap(),
|
|
|
+ amount: Decimal::from_str(res_data_json["origQty"].as_str().unwrap()).unwrap(),
|
|
|
+ deal_amount: Decimal::from_str(res_data_json["executedQty"].as_str().unwrap()).unwrap(),
|
|
|
+ avg_price: Decimal::from_str(res_data_json["avgPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ status: custom_status,
|
|
|
+ order_type: res_data_json["type"].as_str().unwrap().parse().unwrap(),
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.message))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> { todo!() }
|
|
|
+
|
|
|
async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { todo!() }
|
|
|
|
|
|
async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { todo!() }
|
|
|
|
|
|
- async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
- todo!()
|
|
|
- }
|
|
|
+ async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { todo!() }
|
|
|
|
|
|
async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { todo!() }
|
|
|
|
|
|
@@ -132,3 +244,24 @@ impl Platform for BinanceSwap {
|
|
|
|
|
|
async fn command_order(&mut self, _order_command: OrderCommand) { todo!() }
|
|
|
}
|
|
|
+
|
|
|
+pub fn format_position_item(position: &serde_json::Value, amount_size: Decimal) -> Position {
|
|
|
+ let position_mode = match position["positionSide"].as_str().unwrap_or("") {
|
|
|
+ "BOTH" => PositionModeEnum::Both,
|
|
|
+ "LONG" => PositionModeEnum::Long,
|
|
|
+ "SHORT" => PositionModeEnum::Short,
|
|
|
+ _ => panic!("Binance:格式化持仓模式错误!\nformat_position_item:position_side={:?}", position["positionSide"])
|
|
|
+ };
|
|
|
+ let size = Decimal::from_str(position["positionAmt"].as_str().unwrap()).unwrap();
|
|
|
+ let amount = size * amount_size;
|
|
|
+ Position {
|
|
|
+ symbol: position["symbol"].as_str().unwrap_or("").parse().unwrap(),
|
|
|
+ margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
|
|
|
+ amount,
|
|
|
+ frozen_amount: dec!(0),
|
|
|
+ price: Decimal::from_str(position["entryPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ profit: Decimal::from_str(position["unRealizedProfit"].as_str().unwrap()).unwrap(),
|
|
|
+ position_mode,
|
|
|
+ margin: Decimal::from_str(position["isolatedMargin"].as_str().unwrap()).unwrap(),
|
|
|
+ }
|
|
|
+}
|