|
|
@@ -0,0 +1,387 @@
|
|
|
+use std::collections::BTreeMap;
|
|
|
+use std::io::{Error, ErrorKind};
|
|
|
+use std::result::Result;
|
|
|
+use std::str::FromStr;
|
|
|
+use async_trait::async_trait;
|
|
|
+use rust_decimal::Decimal;
|
|
|
+use rust_decimal::prelude::FromPrimitive;
|
|
|
+use rust_decimal_macros::dec;
|
|
|
+use serde_json::Value;
|
|
|
+use tokio::sync::mpsc::Sender;
|
|
|
+use tracing::{error};
|
|
|
+use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, utils, PositionModeEnum};
|
|
|
+use exchanges::binance_swap_rest::BinanceSwapRest;
|
|
|
+
|
|
|
+#[allow(dead_code)]
|
|
|
+#[derive(Clone)]
|
|
|
+pub struct BinanceSwap {
|
|
|
+ exchange: ExchangeEnum,
|
|
|
+ symbol: String,
|
|
|
+ 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>, 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;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[async_trait]
|
|
|
+impl Platform for BinanceSwap {
|
|
|
+ // 克隆方法
|
|
|
+ fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
|
|
|
+ // 获取交易所模式
|
|
|
+ fn get_self_exchange(&self) -> ExchangeEnum {
|
|
|
+ ExchangeEnum::BinanceSwap
|
|
|
+ }
|
|
|
+ // 获取交易对
|
|
|
+ fn get_self_symbol(&self) -> String { self.symbol.clone() }
|
|
|
+ // 获取是否使用高速通道
|
|
|
+ fn get_self_is_colo(&self) -> bool {
|
|
|
+ self.is_colo
|
|
|
+ }
|
|
|
+ // 获取params信息
|
|
|
+ fn get_self_params(&self) -> BTreeMap<String, String> {
|
|
|
+ self.params.clone()
|
|
|
+ }
|
|
|
+ // 获取market信息
|
|
|
+ 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> {
|
|
|
+ let res_data = self.request.get_server_time().await;
|
|
|
+ if res_data.code == 200 {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let result = res_data_json["serverTime"].to_string();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 获取账号信息
|
|
|
+ async fn get_account(&mut self) -> Result<Account, Error> {
|
|
|
+ let symbol_array: Vec<&str> = self.symbol.split("_").collect();
|
|
|
+ let res_data = self.request.get_account().await;
|
|
|
+ if res_data.code == 200 {
|
|
|
+ let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+ 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_swap:格式化账号信息错误!\nget_account: res_data={:?}", res_data);
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ Some(value) => {
|
|
|
+ let balance = Decimal::from_str(value["balance"].as_str().unwrap()).unwrap();
|
|
|
+ let available_balance = Decimal::from_str(value["availableBalance"].as_str().unwrap()).unwrap();
|
|
|
+ let frozen_balance = balance - available_balance;
|
|
|
+ let result = Account {
|
|
|
+ coin: value["asset"].as_str().unwrap().to_string(),
|
|
|
+ balance,
|
|
|
+ available_balance,
|
|
|
+ frozen_balance,
|
|
|
+ stocks: Decimal::ZERO,
|
|
|
+ available_stocks: Decimal::ZERO,
|
|
|
+ frozen_stocks: Decimal::ZERO,
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
|
|
|
+ Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取仓位信息
|
|
|
+ async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
+ let symbol_format = utils::format_symbol(self.symbol.clone(), "");
|
|
|
+ let ct_val = self.market.ct_val;
|
|
|
+ let res_data = self.request.get_position_risk(symbol_format).await;
|
|
|
+ if res_data.code == 200 {
|
|
|
+ let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+ let result = res_data_json.iter().map(|item| { format_position_item(item, ct_val) }).collect();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
+ let res_data = self.request.get_position_risk("".to_string()).await;
|
|
|
+ if res_data.code == 200 {
|
|
|
+ let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+ let result = res_data_json.iter().map(|item| { format_position_item(item, Decimal::ONE) }).collect();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取市场行情
|
|
|
+ async fn get_ticker(&mut self) -> Result<Ticker, Error> {
|
|
|
+ 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_json: serde_json::Value = res_data.data;
|
|
|
+ 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.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
|
|
|
+ let symbol_format = utils::format_symbol(symbol.clone(), "");
|
|
|
+ let res_data = self.request.get_book_ticker(symbol_format).await;
|
|
|
+ if res_data.code == 200 {
|
|
|
+ let res_data_json: serde_json::Value = res_data.data;
|
|
|
+ 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.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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_json = res_data.data;
|
|
|
+ let symbols = res_data_json["symbols"].as_array().unwrap();
|
|
|
+ let market_info = symbols.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format);
|
|
|
+ match market_info {
|
|
|
+ None => {
|
|
|
+ error!("binance_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_json);
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data_json.to_string()))
|
|
|
+ }
|
|
|
+ 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: Decimal::from_str(lot_size_filter["stepSize"].as_str().unwrap()).unwrap(),
|
|
|
+ price_precision: Decimal::from_f64(value["pricePrecision"].as_f64().unwrap()).unwrap(),
|
|
|
+ amount_precision: Decimal::from_f64(value["quantityPrecision"].as_f64().unwrap()).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: Decimal::ONE,
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
|
|
|
+ let symbol_format = utils::format_symbol(symbol.clone(), "");
|
|
|
+ let res_data = self.request.get_exchange_info().await;
|
|
|
+ if res_data.code == 200 {
|
|
|
+ let res_data_json: serde_json::Value = res_data.data;
|
|
|
+ let symbols = res_data_json["symbols"].as_array().unwrap();
|
|
|
+ let market_info = symbols.iter().find(|&item| item["symbol"].as_str().unwrap() == symbol_format);
|
|
|
+ match market_info {
|
|
|
+ None => {
|
|
|
+ error!("binance_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_json);
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data_json.to_string()))
|
|
|
+ }
|
|
|
+ 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: Decimal::from_str(lot_size_filter["stepSize"].as_str().unwrap()).unwrap(),
|
|
|
+ price_precision: Decimal::from_f64(value["pricePrecision"].as_f64().unwrap()).unwrap(),
|
|
|
+ amount_precision: Decimal::from_f64(value["quantityPrecision"].as_f64().unwrap()).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: Decimal::ONE,
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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_json: serde_json::Value = res_data.data;
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ error!("binance_swap:格式化订单状态错误!\nget_order_detail:res_data={:?}", res_data_json);
|
|
|
+ panic!("binance_swap:格式化订单状态错误!\nget_order_detail:res_data={:?}", res_data_json)
|
|
|
+ };
|
|
|
+ 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.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
|
|
|
+ let symbol_format = utils::format_symbol(self.symbol.clone(), "");
|
|
|
+ let res_data = self.request.get_open_orders(symbol_format).await;
|
|
|
+ if res_data.code == 200 {
|
|
|
+ let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+ let order_info: Vec<_> = res_data_json.iter().filter(|item| item["contract"].as_str().unwrap_or("") == self.symbol).collect();
|
|
|
+ let result = order_info.iter().map(|&item| {
|
|
|
+ let status = item["status"].as_str().unwrap();
|
|
|
+ let custom_status = if ["CANCELED", "EXPIRED", "FILLED"].contains(&status) { "REMOVE".to_string() } else if status == "NEW" { "NEW".to_string() } else {
|
|
|
+ error!("binance_swap:格式化订单状态错误!\nget_order_detail:res_data={:?}", res_data);
|
|
|
+ panic!("binance_swap:格式化订单状态错误!\nget_order_detail:res_data={:?}", res_data)
|
|
|
+ };
|
|
|
+ Order {
|
|
|
+ id: item["orderId"].to_string(),
|
|
|
+ custom_id: item["clientOrderId"].as_str().unwrap().parse().unwrap(),
|
|
|
+ price: Decimal::from_str(item["price"].as_str().unwrap()).unwrap(),
|
|
|
+ amount: Decimal::from_str(item["origQty"].as_str().unwrap()).unwrap(),
|
|
|
+ deal_amount: Decimal::from_str(item["executedQty"].as_str().unwrap()).unwrap(),
|
|
|
+ avg_price: Decimal::from_str(item["avgPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ status: custom_status,
|
|
|
+ order_type: item["type"].as_str().unwrap().parse().unwrap()
|
|
|
+ }
|
|
|
+ }).collect();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn take_order(&mut self, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+
|
|
|
+ async fn take_order_symbol(&mut self, _symbol: String, _ct_val: Decimal, _custom_id: &str, _origin_side: &str, _price: Decimal, _amount: Decimal) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+
|
|
|
+ async fn cancel_order(&mut self, _order_id: &str, _custom_id: &str) -> Result<Order, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+
|
|
|
+ async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+
|
|
|
+ async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+
|
|
|
+
|
|
|
+ async fn take_stop_loss_order(&mut self, _stop_price: Decimal, _price: Decimal, _side: &str) -> Result<Value, Error> {
|
|
|
+ Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
|
|
|
+ Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+
|
|
|
+ async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+
|
|
|
+ async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+
|
|
|
+ async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "binance_swap:该交易所方法未实现".to_string())) }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn format_position_item(position: &serde_json::Value, ct_val: Decimal) -> Position {
|
|
|
+ let mut position_mode = match position["positionSide"].as_str().unwrap_or("") {
|
|
|
+ "BOTH" => PositionModeEnum::Both,
|
|
|
+ "LONG" => PositionModeEnum::Long,
|
|
|
+ "SHORT" => PositionModeEnum::Short,
|
|
|
+ _ => {
|
|
|
+ error!("binance_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
|
|
|
+ panic!("binance_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let size = Decimal::from_str(position["positionAmt"].as_str().unwrap()).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["symbol"].as_str().unwrap_or("").parse().unwrap(),
|
|
|
+ margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
|
|
|
+ amount,
|
|
|
+ frozen_amount: Decimal::ZERO,
|
|
|
+ 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(),
|
|
|
+ }
|
|
|
+}
|