|
|
@@ -1,771 +1,771 @@
|
|
|
-use std::collections::{BTreeMap};
|
|
|
-use std::io::{Error, ErrorKind};
|
|
|
-use std::str::FromStr;
|
|
|
-use tokio::sync::mpsc::Sender;
|
|
|
-use async_trait::async_trait;
|
|
|
-use futures::stream::FuturesUnordered;
|
|
|
-use futures::TryStreamExt;
|
|
|
-use rust_decimal::Decimal;
|
|
|
-use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
|
|
|
-use serde_json::{Value};
|
|
|
-use tokio::spawn;
|
|
|
-use tokio::time::Instant;
|
|
|
-use tracing::{error, info, trace};
|
|
|
-use exchanges::coinex_swap_rest::CoinexSwapRest;
|
|
|
-use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, PositionModeEnum, utils};
|
|
|
-use global::trace_stack::TraceStack;
|
|
|
-use crate::utils::get_tick_size;
|
|
|
-
|
|
|
-#[allow(dead_code)]
|
|
|
-#[derive(Clone)]
|
|
|
-pub struct CoinexSwap {
|
|
|
- exchange: ExchangeEnum,
|
|
|
- symbol: String,
|
|
|
- is_colo: bool,
|
|
|
- params: BTreeMap<String, String>,
|
|
|
- request: CoinexSwapRest,
|
|
|
- market: Market,
|
|
|
- order_sender: Sender<Order>,
|
|
|
- error_sender: Sender<Error>,
|
|
|
-}
|
|
|
-
|
|
|
-impl CoinexSwap {
|
|
|
- pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> CoinexSwap {
|
|
|
- let market = Market::new();
|
|
|
- let mut coinex_swap = CoinexSwap {
|
|
|
- exchange: ExchangeEnum::CoinexSwap,
|
|
|
- symbol: symbol.to_uppercase(),
|
|
|
- is_colo,
|
|
|
- params: params.clone(),
|
|
|
- request: CoinexSwapRest::new(params.clone()),
|
|
|
- market,
|
|
|
- order_sender,
|
|
|
- error_sender,
|
|
|
- };
|
|
|
-
|
|
|
- // 修改持仓模式
|
|
|
- let symbol_array: Vec<&str> = symbol.split("_").collect();
|
|
|
- let mode_result = coinex_swap.set_dual_mode(symbol_array[1], true).await;
|
|
|
- match mode_result {
|
|
|
- Ok(_) => {
|
|
|
- trace!("Coinex:设置持仓模式成功!")
|
|
|
- }
|
|
|
- Err(error) => {
|
|
|
- error!("Coinex:设置持仓模式失败!mode_result={}", error)
|
|
|
- }
|
|
|
- }
|
|
|
- // 获取市场信息
|
|
|
- coinex_swap.market = CoinexSwap::get_market(&mut coinex_swap).await.unwrap_or(coinex_swap.market);
|
|
|
- // 设置持仓杠杆
|
|
|
- let lever_rate_result = coinex_swap.set_dual_leverage("10").await;
|
|
|
- match lever_rate_result {
|
|
|
- Ok(ok) => {
|
|
|
- info!("Coinex:设置持仓杠杆成功!{:?}", ok);
|
|
|
- }
|
|
|
- Err(error) => {
|
|
|
- error!("Coinex:设置持仓杠杆失败!{:?}", error)
|
|
|
- }
|
|
|
- }
|
|
|
- return coinex_swap;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#[async_trait]
|
|
|
-impl Platform for CoinexSwap {
|
|
|
- // 克隆方法
|
|
|
- fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
|
|
|
- // 获取交易所模式
|
|
|
- fn get_self_exchange(&self) -> ExchangeEnum {
|
|
|
- ExchangeEnum::CoinexSwap
|
|
|
- }
|
|
|
- // 获取交易对
|
|
|
- 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: Value = res_data.data;
|
|
|
- let result = res_data_json["timestamp"].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 coin = symbol_array[1].to_string().to_uppercase();
|
|
|
- let res_data = self.request.get_account().await;
|
|
|
- if res_data.code == 200 {
|
|
|
- if res_data.data.is_array() {
|
|
|
- let res_data_array = res_data.data.as_array().unwrap();
|
|
|
- for res_data_json in res_data_array.iter() {
|
|
|
- if res_data_json["ccy"].as_str().unwrap() == coin {
|
|
|
- let frozen_balance= Decimal::from_str(res_data_json["frozen"].as_str().unwrap()).unwrap();
|
|
|
- let available_balance = Decimal::from_str(res_data_json["available"].as_str().unwrap()).unwrap();
|
|
|
- let balance = frozen_balance + available_balance;
|
|
|
- let result = Account {
|
|
|
- coin: symbol_array[1].to_string(),
|
|
|
- balance,
|
|
|
- available_balance,
|
|
|
- frozen_balance,
|
|
|
- stocks: Decimal::ZERO,
|
|
|
- available_stocks: Decimal::ZERO,
|
|
|
- frozen_stocks: Decimal::ZERO,
|
|
|
- };
|
|
|
- return Ok(result);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- 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, "coinex_swap:该方法暂未实现".to_string()))
|
|
|
- }
|
|
|
-
|
|
|
- // 获取持仓信息
|
|
|
- async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
- let symbol: String = self.symbol.replace("_", "").to_uppercase();
|
|
|
- let ct_val = self.market.ct_val;
|
|
|
- let res_data = self.request.get_position(symbol).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_user_position().await;
|
|
|
- if res_data.code == 200 {
|
|
|
- info!("{}", res_data.data.to_string());
|
|
|
- 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: String = self.symbol.replace("_", "");
|
|
|
- let res_data = self.request.get_ticker(symbol.clone()).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_json = res_data.data.as_array().unwrap();
|
|
|
- let ticker_info = res_data_json.iter().find(|item| item["market"].as_str().unwrap() == symbol);
|
|
|
- match ticker_info {
|
|
|
- None => {
|
|
|
- error!("coinex_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- Some(value) => {
|
|
|
- let result = Ticker {
|
|
|
- time: chrono::Utc::now().timestamp_millis(),
|
|
|
- high: Decimal::from_str(value["high"].as_str().unwrap()).unwrap(),
|
|
|
- low: Decimal::from_str(value["low"].as_str().unwrap()).unwrap(),
|
|
|
- sell: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
- buy: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
- last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
- volume: Decimal::from_str(value["volume"].as_str().unwrap()).unwrap(),
|
|
|
- };
|
|
|
- Ok(result)
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- async fn get_ticker_symbol(&mut self, symbol_param: String) -> Result<Ticker, Error> {
|
|
|
- let symbol: String = symbol_param.replace("_", "").to_uppercase();
|
|
|
- let res_data = self.request.get_ticker(symbol.clone()).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_json = res_data.data.as_array().unwrap();
|
|
|
- let ticker_info = res_data_json.iter().find(|item| item["contract"].as_str().unwrap() == symbol);
|
|
|
- match ticker_info {
|
|
|
- None => {
|
|
|
- error!("coinex_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- Some(value) => {
|
|
|
- let result = Ticker {
|
|
|
- time: chrono::Utc::now().timestamp_millis(),
|
|
|
- high: Decimal::from_str(value["high"].as_str().unwrap()).unwrap(),
|
|
|
- low: Decimal::from_str(value["low"].as_str().unwrap()).unwrap(),
|
|
|
- sell: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
- buy: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
- last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
- volume: Decimal::from_str(value["volume"].as_str().unwrap()).unwrap(),
|
|
|
- };
|
|
|
- Ok(result)
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- async fn get_market(&mut self) -> Result<Market, Error> {
|
|
|
- let symbol_array: Vec<&str> = self.symbol.split("_").collect();
|
|
|
- let symbol = format!("{}{}", symbol_array[0], symbol_array[1]);
|
|
|
- let res_data = self.request.get_market_details(symbol.clone()).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_json = res_data.data.as_array().unwrap();
|
|
|
- let market_info = res_data_json.iter().find(|item| item["market"].as_str().unwrap() == symbol);
|
|
|
- match market_info {
|
|
|
- None => {
|
|
|
- error!("coinex_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- Some(value) => {
|
|
|
- // 报价精度字符串
|
|
|
- let price_precision_i64 = value["quote_ccy_precision"].as_i64().unwrap();
|
|
|
- // 价格最小变动数值
|
|
|
- let tick_size = get_tick_size(price_precision_i64.to_u32().unwrap());
|
|
|
- // 报价精度
|
|
|
- let price_precision = Decimal::from_i64(price_precision_i64).unwrap();
|
|
|
- // 最小数量
|
|
|
- let min_qty = Decimal::from_str(value["min_amount"].as_str().unwrap()).unwrap();
|
|
|
- // 数量没有最大值
|
|
|
- let max_qty = Decimal::MAX;
|
|
|
- // 没有张数
|
|
|
- let ct_val = Decimal::ONE;
|
|
|
-
|
|
|
- let amount_size = min_qty * ct_val;
|
|
|
- let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
|
|
|
- let min_notional = min_qty * ct_val;
|
|
|
- let max_notional = max_qty * ct_val;
|
|
|
-
|
|
|
- let result = Market {
|
|
|
- symbol: self.symbol.clone(),
|
|
|
- base_asset: symbol_array[0].to_string(),
|
|
|
- quote_asset: symbol_array[1].to_string(),
|
|
|
- tick_size,
|
|
|
- amount_size,
|
|
|
- price_precision,
|
|
|
- amount_precision,
|
|
|
- min_qty,
|
|
|
- max_qty,
|
|
|
- min_notional,
|
|
|
- max_notional,
|
|
|
- ct_val,
|
|
|
- };
|
|
|
- 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_upper = symbol.to_uppercase();
|
|
|
- let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
|
|
|
- let symbol = format!("{}{}", symbol_array[0], symbol_array[1]);
|
|
|
- let res_data = self.request.get_market_details(symbol.clone()).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_json = res_data.data.as_array().unwrap();
|
|
|
- let market_info = res_data_json.iter().find(|item| item["name"].as_str().unwrap() == symbol.clone());
|
|
|
- match market_info {
|
|
|
- None => {
|
|
|
- error!("coinex_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- Some(value) => {
|
|
|
- let tick_size = Decimal::from_str(value["quote_ccy_precision"].as_str().unwrap()).unwrap();
|
|
|
- let min_qty = Decimal::from_str(&value["min_amount"].to_string()).unwrap();
|
|
|
- // 数量没有最大值
|
|
|
- let max_qty = Decimal::MAX;
|
|
|
- // 没有张数
|
|
|
- let ct_val = Decimal::ONE;
|
|
|
-
|
|
|
- let amount_size = min_qty * ct_val;
|
|
|
- let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
|
|
|
- let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
|
|
|
- let min_notional = min_qty * ct_val;
|
|
|
- let max_notional = max_qty * ct_val;
|
|
|
-
|
|
|
- let result = Market {
|
|
|
- symbol: self.symbol.clone(),
|
|
|
- base_asset: symbol_array[0].to_string(),
|
|
|
- quote_asset: symbol_array[1].to_string(),
|
|
|
- tick_size,
|
|
|
- amount_size,
|
|
|
- price_precision,
|
|
|
- amount_precision,
|
|
|
- min_qty,
|
|
|
- max_qty,
|
|
|
- min_notional,
|
|
|
- max_notional,
|
|
|
- ct_val,
|
|
|
- };
|
|
|
- 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 = self.symbol.replace("_", "").to_uppercase();
|
|
|
- let ct_val = self.market.ct_val;
|
|
|
- let res_data;
|
|
|
- let status ;
|
|
|
- if order_id != "" {
|
|
|
- status = "";
|
|
|
- res_data = self.request.get_order_details(order_id.to_string(), symbol).await;
|
|
|
- } else if custom_id != "" {
|
|
|
- // 通过客户端id查询 只有未完成的订单才能查询出来
|
|
|
- res_data = self.request.get_pending_order(custom_id.to_string()).await;
|
|
|
- status = "open";
|
|
|
- } else {
|
|
|
- return Err(Error::new(ErrorKind::Other, format!("订单id和客户端id都为空,查询失败!order_id :{}, custom_id: {}", order_id, custom_id)));
|
|
|
- }
|
|
|
-
|
|
|
- if res_data.code == 200 {
|
|
|
- // info!("order_detail {}", res_data.data);
|
|
|
- if res_data.data.is_array() {
|
|
|
- let res_data_json = res_data.data.as_array().unwrap();
|
|
|
- if res_data_json.len() == 0 { // 已取消或已成交
|
|
|
- return Ok(Order{
|
|
|
- id: order_id.to_string(),
|
|
|
- custom_id: custom_id.to_string(),
|
|
|
- price: Default::default(),
|
|
|
- amount: Default::default(),
|
|
|
- deal_amount: Default::default(),
|
|
|
- avg_price: Default::default(),
|
|
|
- status: "NULL".to_string(),
|
|
|
- order_type: "".to_string(),
|
|
|
- trace_stack: TraceStack::new(0, Instant::now()).on_special("358 coinex_swap".to_string()),
|
|
|
- })
|
|
|
- } else { // 待成交
|
|
|
- let mut result = format_order_item(res_data_json[0].clone(), ct_val, status);
|
|
|
- result.custom_id = custom_id.to_string();
|
|
|
- result.id = order_id.to_string();
|
|
|
- Ok(result)
|
|
|
- }
|
|
|
- } else {
|
|
|
- let mut result = format_order_item(res_data.data, ct_val, status);
|
|
|
- result.custom_id = custom_id.to_string();
|
|
|
- result.id = order_id.to_string();
|
|
|
- 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 = self.symbol.replace("_", "").to_uppercase();
|
|
|
- let ct_val = self.market.ct_val;
|
|
|
- let status_order;
|
|
|
- let res_data;
|
|
|
- if status == "pending" {
|
|
|
- res_data = self.request.get_pending_orders().await;
|
|
|
- status_order = "open";
|
|
|
- } else if status == "finish" {
|
|
|
- res_data = self.request.get_finished_orders().await;
|
|
|
- status_order = "filled";
|
|
|
- }else{
|
|
|
- return Err(Error::new(ErrorKind::Other, status));
|
|
|
- }
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_json = res_data.data.as_array().unwrap();
|
|
|
- if res_data_json.len() ==0 {
|
|
|
- return Ok(vec![])
|
|
|
- }
|
|
|
- let order_info: Vec<_> = res_data_json.iter().filter(|item| item["market"].as_str().unwrap_or("") == symbol).collect();
|
|
|
- let result = order_info.iter().map(|&item| format_order_item(item.clone(), ct_val, status_order)).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> {
|
|
|
- let symbol = self.symbol.replace("_", "").to_uppercase();
|
|
|
- let ct_val = self.market.ct_val;
|
|
|
- let order_side;
|
|
|
- let position_side;
|
|
|
- let size = utils::truncate_decimal(amount, self.market.amount_precision.to_u32().unwrap());
|
|
|
- if size <= Decimal::ZERO {
|
|
|
- error!("下单数量异常 amount {} amount_precision {} size {}", amount, self.market.amount_precision.to_u32().unwrap(), size);
|
|
|
- return Err(Error::new(ErrorKind::Other, format!("下单数量错误 amount:{}", amount)));
|
|
|
- }
|
|
|
- match origin_side {
|
|
|
- "kd" => {
|
|
|
- position_side = "long";
|
|
|
- order_side = "buy";
|
|
|
- }
|
|
|
- "pd" => {
|
|
|
- position_side = "long";
|
|
|
- order_side = "sell";
|
|
|
- }
|
|
|
- "kk" => {
|
|
|
- position_side = "short";
|
|
|
- order_side = "sell";
|
|
|
- }
|
|
|
- "pk" => {
|
|
|
- position_side = "short";
|
|
|
- order_side = "buy";
|
|
|
- }
|
|
|
- _ => {
|
|
|
- error!("下单参数错误");
|
|
|
- position_side = "error";
|
|
|
- order_side = "error";
|
|
|
- }
|
|
|
- };
|
|
|
- let res_data = self.request.order(symbol, position_side.to_string(), order_side.to_string(), size, price, custom_id.to_string()).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_json: Value = res_data.data;
|
|
|
- // info!("take_order {}", res_data_json);
|
|
|
- let result = format_order_item(res_data_json, ct_val, "open");
|
|
|
- Ok(result)
|
|
|
- } else {
|
|
|
- // error!("take_order error {}", res_data.data);
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- async fn take_order_symbol(&mut self, symbol_y: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
|
|
|
- let symbol = symbol_y.replace("_", "").to_uppercase();
|
|
|
- let order_side;
|
|
|
- let position_side;
|
|
|
- let size = (amount / ct_val).floor();
|
|
|
- match origin_side {
|
|
|
- "kd" => {
|
|
|
- position_side = "long";
|
|
|
- order_side = "buy";
|
|
|
- }
|
|
|
- "pd" => {
|
|
|
- position_side = "long";
|
|
|
- order_side = "sell";
|
|
|
- }
|
|
|
- "kk" => {
|
|
|
- position_side = "short";
|
|
|
- order_side = "sell";
|
|
|
- }
|
|
|
- "pk" => {
|
|
|
- position_side = "short";
|
|
|
- order_side = "buy";
|
|
|
- }
|
|
|
- _ => {
|
|
|
- error!("下单参数错误");
|
|
|
- position_side = "error";
|
|
|
- order_side = "error";
|
|
|
- }
|
|
|
- };
|
|
|
- let res_data = self.request.order(symbol, position_side.to_string(), order_side.to_string(), size, price, custom_id.to_string()).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_json: Value = res_data.data;
|
|
|
- let result = format_order_item(res_data_json, ct_val, "open");
|
|
|
- Ok(result)
|
|
|
- } else {
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 撤销订单
|
|
|
- async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
|
|
|
- let symbol = self.symbol.replace("_", "").to_uppercase();
|
|
|
-
|
|
|
- let ct_val = self.market.ct_val;
|
|
|
- let res_data = self.request.cancel_order(symbol, order_id, custom_id).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_json: Value = res_data.data;
|
|
|
- let mut result;
|
|
|
- if res_data_json.is_null(){
|
|
|
- result = Order{
|
|
|
- id: custom_id.to_string(),
|
|
|
- custom_id: order_id.to_string(),
|
|
|
- price: Default::default(),
|
|
|
- amount: Default::default(),
|
|
|
- deal_amount: Default::default(),
|
|
|
- avg_price: Default::default(),
|
|
|
- status: "NULL".to_string(),
|
|
|
- order_type: "".to_string(),
|
|
|
- trace_stack: TraceStack::new(0, Instant::now()).on_special("513 coinex_swap".to_string())
|
|
|
- };
|
|
|
- } else {
|
|
|
- result = format_order_item(res_data_json, ct_val, "canceled");
|
|
|
- result.custom_id = custom_id.to_string();
|
|
|
- result.id = order_id.to_string();
|
|
|
- }
|
|
|
- Ok(result)
|
|
|
- } else {
|
|
|
- let message = format!("撤单HTTP请求失败 order_id: {}, custom_id: {}, res_data: {:?}", order_id, custom_id, res_data);
|
|
|
- Err(Error::new(ErrorKind::Other, message))
|
|
|
- }
|
|
|
- }
|
|
|
- // 批量撤销订单
|
|
|
- async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
- let symbol = self.symbol.replace("_", "").to_uppercase();
|
|
|
- let res_data = self.request.cancel_order_all(symbol).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- info!("{}", res_data.data.to_string());
|
|
|
- let result = vec![];
|
|
|
- Ok(result)
|
|
|
- } else {
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
- let orders_res_data = self.request.get_pending_orders().await;
|
|
|
- if orders_res_data.code == 200 {
|
|
|
- let result = vec![];
|
|
|
- let orders_res_data_json = orders_res_data.data.as_array().unwrap();
|
|
|
- for order in orders_res_data_json {
|
|
|
- let cancel_res_data = self.request.cancel_order_all( order["market"].as_str().unwrap().to_string()).await;
|
|
|
- if cancel_res_data.code != 200 {
|
|
|
- return Err(Error::new(ErrorKind::Other, cancel_res_data.to_string()));
|
|
|
- }
|
|
|
- }
|
|
|
- Ok(result)
|
|
|
- } else {
|
|
|
- Err(Error::new(ErrorKind::Other, orders_res_data.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, "coin_ex:该交易所暂未实现自动订单下单".to_string()))
|
|
|
- }
|
|
|
-
|
|
|
- async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
|
|
|
- Err(Error::new(ErrorKind::NotFound, "coin_ex:该交易所暂未实现取消自动订单".to_string()))
|
|
|
- }
|
|
|
-
|
|
|
- // 设置持仓模式
|
|
|
- async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
|
|
|
- Err(Error::new(ErrorKind::NotFound, "coin_ex:该交易所只允许单向持仓,无法设置持仓模式".to_string()))
|
|
|
- }
|
|
|
-
|
|
|
- // 更新杠杆
|
|
|
- async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
|
|
|
- let leverage_int = leverage.parse::<i32>().unwrap();
|
|
|
- let symbol = self.symbol.replace("_", "").to_uppercase();
|
|
|
- let res_data = self.request.setting_dual_leverage(symbol, leverage_int).await;
|
|
|
- if res_data.code == 200 {
|
|
|
- let res_data_str = &res_data.data;
|
|
|
- let result = res_data_str.clone();
|
|
|
- Ok(result.to_string())
|
|
|
- } else {
|
|
|
- Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "Coinex:该交易所方法未实现".to_string())) }
|
|
|
-
|
|
|
- async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
|
|
|
- Err(Error::new(ErrorKind::NotFound, "Coinex wallet_transfers:该交易所方法未实现".to_string()))
|
|
|
- }
|
|
|
-
|
|
|
- // 指令下单
|
|
|
- async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack) {
|
|
|
- let mut handles = vec![];
|
|
|
-
|
|
|
- // 下单指令
|
|
|
- for item in order_command.limits_open.keys() {
|
|
|
- let mut ts = trace_stack.clone();
|
|
|
-
|
|
|
- let amount = Decimal::from_str(&*order_command.limits_open[item].get(0).unwrap().clone()).unwrap();
|
|
|
- let side = order_command.limits_open[item].get(1).unwrap().clone();
|
|
|
- let price = Decimal::from_str(&*order_command.limits_open[item].get(2).unwrap().clone()).unwrap();
|
|
|
- let cid = order_command.limits_open[item].get(3).unwrap().clone();
|
|
|
-
|
|
|
- // order_name: [数量,方向,价格,c_id]
|
|
|
- let mut self_clone = self.clone();
|
|
|
- let handle = spawn(async move {
|
|
|
- // info!("数量 {},方向 {},价格 {},c_id {}", amount, side, price, cid);
|
|
|
- ts.on_before_send();
|
|
|
- let result = self_clone.take_order(&cid, &side, price, amount).await;
|
|
|
- ts.on_after_send();
|
|
|
-
|
|
|
- match result {
|
|
|
- Ok(mut result) => {
|
|
|
- result.trace_stack = ts;
|
|
|
- // info!("数量 {},方向 {},价格 {},c_id {}", amount, side, price, cid);
|
|
|
- self_clone.order_sender.send(result).await.unwrap();
|
|
|
- }
|
|
|
- Err(error) => {
|
|
|
- let mut err_order = Order::new();
|
|
|
- err_order.custom_id = cid.clone();
|
|
|
- err_order.status = "REMOVE".to_string();
|
|
|
- // info!("coinex下单error 数量: {},方向: {},价格: {},c_id: {}, err: {}", amount, side, price, cid, error);
|
|
|
- self_clone.order_sender.send(err_order).await.unwrap();
|
|
|
- self_clone.error_sender.send(error).await.unwrap();
|
|
|
- // 触发限频
|
|
|
- // if error_info.to_string().contains("213:Please don't try too frequently") {
|
|
|
- // Err(Error::new(ErrorKind::Other, "触发限频, 请调整下单频率"))
|
|
|
- // }
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- handles.push(handle)
|
|
|
- }
|
|
|
- let futures = FuturesUnordered::from_iter(handles);
|
|
|
- // 等待所有任务完成
|
|
|
- let _: Result<Vec<_>, _> = futures.try_collect().await;
|
|
|
-
|
|
|
- // 撤销订单
|
|
|
- let mut cancel_handlers = vec![];
|
|
|
- for item in order_command.cancel.keys() {
|
|
|
- let order_id = order_command.cancel[item].get(1).unwrap().clone();
|
|
|
- let custom_id = order_command.cancel[item].get(0).unwrap().clone();
|
|
|
-
|
|
|
- let mut self_clone = self.clone();
|
|
|
- let handle = spawn(async move {
|
|
|
- let result = self_clone.cancel_order(&order_id, &custom_id).await;
|
|
|
- match result {
|
|
|
- Ok(order) => {
|
|
|
- if order.status == "REMOVE" {
|
|
|
- // 由于有时撤单成功ws不推送,所以加入rest
|
|
|
- self_clone.order_sender.send(order).await.unwrap();
|
|
|
- }
|
|
|
- }
|
|
|
- Err(error) => {
|
|
|
- // info!("撤单失败:{:?}", error.to_string());
|
|
|
- // 取消失败去查订单。
|
|
|
- let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
- match query_rst {
|
|
|
- Ok(order) => {
|
|
|
- // info!("查单 订单详情:{:?}", order);
|
|
|
- self_clone.order_sender.send(order).await.unwrap();
|
|
|
- }
|
|
|
- Err(_err) => {
|
|
|
- // warn!("撤单失败,而且查单也失败了,Coinex_io_swap,oid={}, cid={} err={:?}", order_id.clone(), custom_id.clone(), err);
|
|
|
- // panic!("撤单失败,而且查单也失败了,Coinex_io_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
|
|
|
- }
|
|
|
- }
|
|
|
- self_clone.error_sender.send(error).await.unwrap();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- cancel_handlers.push(handle)
|
|
|
- }
|
|
|
- let futures = FuturesUnordered::from_iter(cancel_handlers);
|
|
|
- // 等待所有任务完成
|
|
|
- let _: Result<Vec<_>, _> = futures.try_collect().await;
|
|
|
-
|
|
|
- // 检查订单指令
|
|
|
- let mut check_handlers = vec![];
|
|
|
- for item in order_command.check.keys() {
|
|
|
- let order_id = order_command.check[item].get(1).unwrap().clone();
|
|
|
- let custom_id = order_command.check[item].get(0).unwrap().clone();
|
|
|
-
|
|
|
- let mut self_clone = self.clone();
|
|
|
- let handle = spawn(async move {
|
|
|
- let result = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
- match result {
|
|
|
- Ok(result) => {
|
|
|
- self_clone.order_sender.send(result).await.unwrap();
|
|
|
- }
|
|
|
- Err(error) => {
|
|
|
- self_clone.error_sender.send(error).await.unwrap();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- check_handlers.push(handle)
|
|
|
- }
|
|
|
-
|
|
|
- let futures = FuturesUnordered::from_iter(check_handlers);
|
|
|
- // 等待所有任务完成
|
|
|
- let _: Result<Vec<_>, _> = futures.try_collect().await;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-pub fn format_position_item(position: &Value, ct_val: Decimal) -> Position {
|
|
|
- let position_mode = match position["side"].as_str().unwrap_or("") {
|
|
|
- "long" => PositionModeEnum::Long,
|
|
|
- "short" => PositionModeEnum::Short,
|
|
|
- _ => {
|
|
|
- error!("coinex_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
|
|
|
- panic!("coinex_swap:格式化持仓模式错误!\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_or("").parse().unwrap(),
|
|
|
- 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(),
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-pub fn format_order_item(order: Value, ct_val: Decimal, status: &str) -> Order {
|
|
|
- // info!("order_format {}", order);
|
|
|
- let size;
|
|
|
- match order["amount"].as_str() {
|
|
|
- Some(val) => {
|
|
|
- size = Decimal::from_str(val).unwrap()
|
|
|
- },
|
|
|
- None => {
|
|
|
- error!("coinex_swap:格式化订单大小错误!\nformat_order_item:order={:?} status={}", order, status);
|
|
|
- panic!("coinex_swap:格式化订单大小失败,退出程序!");
|
|
|
- }
|
|
|
- }
|
|
|
- // info!("order {}", order);
|
|
|
- // 通过客户端id查询订单 只能查出未完成订单(没有状态字段)
|
|
|
- let status = order["status"].as_str().unwrap_or(status);
|
|
|
- let text = order["client_id"].as_str().unwrap_or("");
|
|
|
-
|
|
|
- let deal_amount = Decimal::from_str(&order["filled_amount"].as_str().unwrap()).unwrap();
|
|
|
- let filled_value = Decimal::from_str(&order["filled_value"].as_str().unwrap()).unwrap();
|
|
|
-
|
|
|
- let amount = size * ct_val;
|
|
|
- let mut avg_price = Decimal::ZERO;
|
|
|
- if deal_amount != Decimal::ZERO{
|
|
|
- avg_price = filled_value/deal_amount;
|
|
|
- }
|
|
|
- let custom_status = if status == "filled" || status == "canceled" {
|
|
|
- "REMOVE".to_string()
|
|
|
- } else if status == "open" || status == "part_filled" || status == "part_canceled" {
|
|
|
- "NEW".to_string()
|
|
|
- } else {
|
|
|
- error!("coinex_swap:格式化订单状态错误!\nformat_order_item:order={:?}", order);
|
|
|
- "NULL".to_string()
|
|
|
- };
|
|
|
- let rst_order = Order {
|
|
|
- id: order["order_id"].to_string(),
|
|
|
- custom_id: text.to_string(),
|
|
|
- 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("765 trace_stack".to_string()),
|
|
|
- };
|
|
|
- return rst_order;
|
|
|
-}
|
|
|
+// use std::collections::{BTreeMap};
|
|
|
+// use std::io::{Error, ErrorKind};
|
|
|
+// use std::str::FromStr;
|
|
|
+// use tokio::sync::mpsc::Sender;
|
|
|
+// use async_trait::async_trait;
|
|
|
+// use futures::stream::FuturesUnordered;
|
|
|
+// use futures::TryStreamExt;
|
|
|
+// use rust_decimal::Decimal;
|
|
|
+// use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
|
|
|
+// use serde_json::{Value};
|
|
|
+// use tokio::spawn;
|
|
|
+// use tokio::time::Instant;
|
|
|
+// use tracing::{error, info, trace};
|
|
|
+// use exchanges::coinex_swap_rest::CoinexSwapRest;
|
|
|
+// use crate::{Platform, ExchangeEnum, Account, Position, Ticker, Market, Order, OrderCommand, PositionModeEnum, utils};
|
|
|
+// use global::trace_stack::TraceStack;
|
|
|
+// use crate::utils::get_tick_size;
|
|
|
+//
|
|
|
+// #[allow(dead_code)]
|
|
|
+// #[derive(Clone)]
|
|
|
+// pub struct CoinexSwap {
|
|
|
+// exchange: ExchangeEnum,
|
|
|
+// symbol: String,
|
|
|
+// is_colo: bool,
|
|
|
+// params: BTreeMap<String, String>,
|
|
|
+// request: CoinexSwapRest,
|
|
|
+// market: Market,
|
|
|
+// order_sender: Sender<Order>,
|
|
|
+// error_sender: Sender<Error>,
|
|
|
+// }
|
|
|
+//
|
|
|
+// impl CoinexSwap {
|
|
|
+// pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> CoinexSwap {
|
|
|
+// let market = Market::new();
|
|
|
+// let mut coinex_swap = CoinexSwap {
|
|
|
+// exchange: ExchangeEnum::CoinexSwap,
|
|
|
+// symbol: symbol.to_uppercase(),
|
|
|
+// is_colo,
|
|
|
+// params: params.clone(),
|
|
|
+// request: CoinexSwapRest::new(params.clone()),
|
|
|
+// market,
|
|
|
+// order_sender,
|
|
|
+// error_sender,
|
|
|
+// };
|
|
|
+//
|
|
|
+// // 修改持仓模式
|
|
|
+// let symbol_array: Vec<&str> = symbol.split("_").collect();
|
|
|
+// let mode_result = coinex_swap.set_dual_mode(symbol_array[1], true).await;
|
|
|
+// match mode_result {
|
|
|
+// Ok(_) => {
|
|
|
+// trace!("Coinex:设置持仓模式成功!")
|
|
|
+// }
|
|
|
+// Err(error) => {
|
|
|
+// error!("Coinex:设置持仓模式失败!mode_result={}", error)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// // 获取市场信息
|
|
|
+// coinex_swap.market = CoinexSwap::get_market(&mut coinex_swap).await.unwrap_or(coinex_swap.market);
|
|
|
+// // 设置持仓杠杆
|
|
|
+// let lever_rate_result = coinex_swap.set_dual_leverage("10").await;
|
|
|
+// match lever_rate_result {
|
|
|
+// Ok(ok) => {
|
|
|
+// info!("Coinex:设置持仓杠杆成功!{:?}", ok);
|
|
|
+// }
|
|
|
+// Err(error) => {
|
|
|
+// error!("Coinex:设置持仓杠杆失败!{:?}", error)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// return coinex_swap;
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// #[async_trait]
|
|
|
+// impl Platform for CoinexSwap {
|
|
|
+// // 克隆方法
|
|
|
+// fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
|
|
|
+// // 获取交易所模式
|
|
|
+// fn get_self_exchange(&self) -> ExchangeEnum {
|
|
|
+// ExchangeEnum::CoinexSwap
|
|
|
+// }
|
|
|
+// // 获取交易对
|
|
|
+// 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: Value = res_data.data;
|
|
|
+// let result = res_data_json["timestamp"].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 coin = symbol_array[1].to_string().to_uppercase();
|
|
|
+// let res_data = self.request.get_account().await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// if res_data.data.is_array() {
|
|
|
+// let res_data_array = res_data.data.as_array().unwrap();
|
|
|
+// for res_data_json in res_data_array.iter() {
|
|
|
+// if res_data_json["ccy"].as_str().unwrap() == coin {
|
|
|
+// let frozen_balance= Decimal::from_str(res_data_json["frozen"].as_str().unwrap()).unwrap();
|
|
|
+// let available_balance = Decimal::from_str(res_data_json["available"].as_str().unwrap()).unwrap();
|
|
|
+// let balance = frozen_balance + available_balance;
|
|
|
+// let result = Account {
|
|
|
+// coin: symbol_array[1].to_string(),
|
|
|
+// balance,
|
|
|
+// available_balance,
|
|
|
+// frozen_balance,
|
|
|
+// stocks: Decimal::ZERO,
|
|
|
+// available_stocks: Decimal::ZERO,
|
|
|
+// frozen_stocks: Decimal::ZERO,
|
|
|
+// };
|
|
|
+// return Ok(result);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// 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, "coinex_swap:该方法暂未实现".to_string()))
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 获取持仓信息
|
|
|
+// async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
+// let symbol: String = self.symbol.replace("_", "").to_uppercase();
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let res_data = self.request.get_position(symbol).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_user_position().await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// info!("{}", res_data.data.to_string());
|
|
|
+// 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: String = self.symbol.replace("_", "");
|
|
|
+// let res_data = self.request.get_ticker(symbol.clone()).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+// let ticker_info = res_data_json.iter().find(|item| item["market"].as_str().unwrap() == symbol);
|
|
|
+// match ticker_info {
|
|
|
+// None => {
|
|
|
+// error!("coinex_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// let result = Ticker {
|
|
|
+// time: chrono::Utc::now().timestamp_millis(),
|
|
|
+// high: Decimal::from_str(value["high"].as_str().unwrap()).unwrap(),
|
|
|
+// low: Decimal::from_str(value["low"].as_str().unwrap()).unwrap(),
|
|
|
+// sell: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
+// buy: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
+// last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
+// volume: Decimal::from_str(value["volume"].as_str().unwrap()).unwrap(),
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_ticker_symbol(&mut self, symbol_param: String) -> Result<Ticker, Error> {
|
|
|
+// let symbol: String = symbol_param.replace("_", "").to_uppercase();
|
|
|
+// let res_data = self.request.get_ticker(symbol.clone()).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+// let ticker_info = res_data_json.iter().find(|item| item["contract"].as_str().unwrap() == symbol);
|
|
|
+// match ticker_info {
|
|
|
+// None => {
|
|
|
+// error!("coinex_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data);
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// let result = Ticker {
|
|
|
+// time: chrono::Utc::now().timestamp_millis(),
|
|
|
+// high: Decimal::from_str(value["high"].as_str().unwrap()).unwrap(),
|
|
|
+// low: Decimal::from_str(value["low"].as_str().unwrap()).unwrap(),
|
|
|
+// sell: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
+// buy: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
+// last: Decimal::from_str(value["last"].as_str().unwrap()).unwrap(),
|
|
|
+// volume: Decimal::from_str(value["volume"].as_str().unwrap()).unwrap(),
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_market(&mut self) -> Result<Market, Error> {
|
|
|
+// let symbol_array: Vec<&str> = self.symbol.split("_").collect();
|
|
|
+// let symbol = format!("{}{}", symbol_array[0], symbol_array[1]);
|
|
|
+// let res_data = self.request.get_market_details(symbol.clone()).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+// let market_info = res_data_json.iter().find(|item| item["market"].as_str().unwrap() == symbol);
|
|
|
+// match market_info {
|
|
|
+// None => {
|
|
|
+// error!("coinex_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// // 报价精度字符串
|
|
|
+// let price_precision_i64 = value["quote_ccy_precision"].as_i64().unwrap();
|
|
|
+// // 价格最小变动数值
|
|
|
+// let tick_size = get_tick_size(price_precision_i64.to_u32().unwrap());
|
|
|
+// // 报价精度
|
|
|
+// let price_precision = Decimal::from_i64(price_precision_i64).unwrap();
|
|
|
+// // 最小数量
|
|
|
+// let min_qty = Decimal::from_str(value["min_amount"].as_str().unwrap()).unwrap();
|
|
|
+// // 数量没有最大值
|
|
|
+// let max_qty = Decimal::MAX;
|
|
|
+// // 没有张数
|
|
|
+// let ct_val = Decimal::ONE;
|
|
|
+//
|
|
|
+// let amount_size = min_qty * ct_val;
|
|
|
+// let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
|
|
|
+// let min_notional = min_qty * ct_val;
|
|
|
+// let max_notional = max_qty * ct_val;
|
|
|
+//
|
|
|
+// let result = Market {
|
|
|
+// symbol: self.symbol.clone(),
|
|
|
+// base_asset: symbol_array[0].to_string(),
|
|
|
+// quote_asset: symbol_array[1].to_string(),
|
|
|
+// tick_size,
|
|
|
+// amount_size,
|
|
|
+// price_precision,
|
|
|
+// amount_precision,
|
|
|
+// min_qty,
|
|
|
+// max_qty,
|
|
|
+// min_notional,
|
|
|
+// max_notional,
|
|
|
+// ct_val,
|
|
|
+// };
|
|
|
+// 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_upper = symbol.to_uppercase();
|
|
|
+// let symbol_array: Vec<&str> = symbol_upper.split("_").collect();
|
|
|
+// let symbol = format!("{}{}", symbol_array[0], symbol_array[1]);
|
|
|
+// let res_data = self.request.get_market_details(symbol.clone()).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+// let market_info = res_data_json.iter().find(|item| item["name"].as_str().unwrap() == symbol.clone());
|
|
|
+// match market_info {
|
|
|
+// None => {
|
|
|
+// error!("coinex_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// let tick_size = Decimal::from_str(value["quote_ccy_precision"].as_str().unwrap()).unwrap();
|
|
|
+// let min_qty = Decimal::from_str(&value["min_amount"].to_string()).unwrap();
|
|
|
+// // 数量没有最大值
|
|
|
+// let max_qty = Decimal::MAX;
|
|
|
+// // 没有张数
|
|
|
+// let ct_val = Decimal::ONE;
|
|
|
+//
|
|
|
+// let amount_size = min_qty * ct_val;
|
|
|
+// let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
|
|
|
+// let amount_precision = Decimal::from_u32(amount_size.scale()).unwrap();
|
|
|
+// let min_notional = min_qty * ct_val;
|
|
|
+// let max_notional = max_qty * ct_val;
|
|
|
+//
|
|
|
+// let result = Market {
|
|
|
+// symbol: self.symbol.clone(),
|
|
|
+// base_asset: symbol_array[0].to_string(),
|
|
|
+// quote_asset: symbol_array[1].to_string(),
|
|
|
+// tick_size,
|
|
|
+// amount_size,
|
|
|
+// price_precision,
|
|
|
+// amount_precision,
|
|
|
+// min_qty,
|
|
|
+// max_qty,
|
|
|
+// min_notional,
|
|
|
+// max_notional,
|
|
|
+// ct_val,
|
|
|
+// };
|
|
|
+// 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 = self.symbol.replace("_", "").to_uppercase();
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let res_data;
|
|
|
+// let status ;
|
|
|
+// if order_id != "" {
|
|
|
+// status = "";
|
|
|
+// res_data = self.request.get_order_details(order_id.to_string(), symbol).await;
|
|
|
+// } else if custom_id != "" {
|
|
|
+// // 通过客户端id查询 只有未完成的订单才能查询出来
|
|
|
+// res_data = self.request.get_pending_order(custom_id.to_string()).await;
|
|
|
+// status = "open";
|
|
|
+// } else {
|
|
|
+// return Err(Error::new(ErrorKind::Other, format!("订单id和客户端id都为空,查询失败!order_id :{}, custom_id: {}", order_id, custom_id)));
|
|
|
+// }
|
|
|
+//
|
|
|
+// if res_data.code == 200 {
|
|
|
+// // info!("order_detail {}", res_data.data);
|
|
|
+// if res_data.data.is_array() {
|
|
|
+// let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+// if res_data_json.len() == 0 { // 已取消或已成交
|
|
|
+// return Ok(Order{
|
|
|
+// id: order_id.to_string(),
|
|
|
+// custom_id: custom_id.to_string(),
|
|
|
+// price: Default::default(),
|
|
|
+// amount: Default::default(),
|
|
|
+// deal_amount: Default::default(),
|
|
|
+// avg_price: Default::default(),
|
|
|
+// status: "NULL".to_string(),
|
|
|
+// order_type: "".to_string(),
|
|
|
+// trace_stack: TraceStack::new(0, Instant::now()).on_special("358 coinex_swap".to_string()),
|
|
|
+// })
|
|
|
+// } else { // 待成交
|
|
|
+// let mut result = format_order_item(res_data_json[0].clone(), ct_val, status);
|
|
|
+// result.custom_id = custom_id.to_string();
|
|
|
+// result.id = order_id.to_string();
|
|
|
+// Ok(result)
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// let mut result = format_order_item(res_data.data, ct_val, status);
|
|
|
+// result.custom_id = custom_id.to_string();
|
|
|
+// result.id = order_id.to_string();
|
|
|
+// 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 = self.symbol.replace("_", "").to_uppercase();
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let status_order;
|
|
|
+// let res_data;
|
|
|
+// if status == "pending" {
|
|
|
+// res_data = self.request.get_pending_orders().await;
|
|
|
+// status_order = "open";
|
|
|
+// } else if status == "finish" {
|
|
|
+// res_data = self.request.get_finished_orders().await;
|
|
|
+// status_order = "filled";
|
|
|
+// }else{
|
|
|
+// return Err(Error::new(ErrorKind::Other, status));
|
|
|
+// }
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+// if res_data_json.len() ==0 {
|
|
|
+// return Ok(vec![])
|
|
|
+// }
|
|
|
+// let order_info: Vec<_> = res_data_json.iter().filter(|item| item["market"].as_str().unwrap_or("") == symbol).collect();
|
|
|
+// let result = order_info.iter().map(|&item| format_order_item(item.clone(), ct_val, status_order)).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> {
|
|
|
+// let symbol = self.symbol.replace("_", "").to_uppercase();
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let order_side;
|
|
|
+// let position_side;
|
|
|
+// let size = utils::truncate_decimal(amount, self.market.amount_precision.to_u32().unwrap());
|
|
|
+// if size <= Decimal::ZERO {
|
|
|
+// error!("下单数量异常 amount {} amount_precision {} size {}", amount, self.market.amount_precision.to_u32().unwrap(), size);
|
|
|
+// return Err(Error::new(ErrorKind::Other, format!("下单数量错误 amount:{}", amount)));
|
|
|
+// }
|
|
|
+// match origin_side {
|
|
|
+// "kd" => {
|
|
|
+// position_side = "long";
|
|
|
+// order_side = "buy";
|
|
|
+// }
|
|
|
+// "pd" => {
|
|
|
+// position_side = "long";
|
|
|
+// order_side = "sell";
|
|
|
+// }
|
|
|
+// "kk" => {
|
|
|
+// position_side = "short";
|
|
|
+// order_side = "sell";
|
|
|
+// }
|
|
|
+// "pk" => {
|
|
|
+// position_side = "short";
|
|
|
+// order_side = "buy";
|
|
|
+// }
|
|
|
+// _ => {
|
|
|
+// error!("下单参数错误");
|
|
|
+// position_side = "error";
|
|
|
+// order_side = "error";
|
|
|
+// }
|
|
|
+// };
|
|
|
+// let res_data = self.request.order(symbol, position_side.to_string(), order_side.to_string(), size, price, custom_id.to_string()).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_json: Value = res_data.data;
|
|
|
+// // info!("take_order {}", res_data_json);
|
|
|
+// let result = format_order_item(res_data_json, ct_val, "open");
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// // error!("take_order error {}", res_data.data);
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn take_order_symbol(&mut self, symbol_y: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
|
|
|
+// let symbol = symbol_y.replace("_", "").to_uppercase();
|
|
|
+// let order_side;
|
|
|
+// let position_side;
|
|
|
+// let size = (amount / ct_val).floor();
|
|
|
+// match origin_side {
|
|
|
+// "kd" => {
|
|
|
+// position_side = "long";
|
|
|
+// order_side = "buy";
|
|
|
+// }
|
|
|
+// "pd" => {
|
|
|
+// position_side = "long";
|
|
|
+// order_side = "sell";
|
|
|
+// }
|
|
|
+// "kk" => {
|
|
|
+// position_side = "short";
|
|
|
+// order_side = "sell";
|
|
|
+// }
|
|
|
+// "pk" => {
|
|
|
+// position_side = "short";
|
|
|
+// order_side = "buy";
|
|
|
+// }
|
|
|
+// _ => {
|
|
|
+// error!("下单参数错误");
|
|
|
+// position_side = "error";
|
|
|
+// order_side = "error";
|
|
|
+// }
|
|
|
+// };
|
|
|
+// let res_data = self.request.order(symbol, position_side.to_string(), order_side.to_string(), size, price, custom_id.to_string()).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_json: Value = res_data.data;
|
|
|
+// let result = format_order_item(res_data_json, ct_val, "open");
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 撤销订单
|
|
|
+// async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
|
|
|
+// let symbol = self.symbol.replace("_", "").to_uppercase();
|
|
|
+//
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let res_data = self.request.cancel_order(symbol, order_id, custom_id).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_json: Value = res_data.data;
|
|
|
+// let mut result;
|
|
|
+// if res_data_json.is_null(){
|
|
|
+// result = Order{
|
|
|
+// id: custom_id.to_string(),
|
|
|
+// custom_id: order_id.to_string(),
|
|
|
+// price: Default::default(),
|
|
|
+// amount: Default::default(),
|
|
|
+// deal_amount: Default::default(),
|
|
|
+// avg_price: Default::default(),
|
|
|
+// status: "NULL".to_string(),
|
|
|
+// order_type: "".to_string(),
|
|
|
+// trace_stack: TraceStack::new(0, Instant::now()).on_special("513 coinex_swap".to_string())
|
|
|
+// };
|
|
|
+// } else {
|
|
|
+// result = format_order_item(res_data_json, ct_val, "canceled");
|
|
|
+// result.custom_id = custom_id.to_string();
|
|
|
+// result.id = order_id.to_string();
|
|
|
+// }
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// let message = format!("撤单HTTP请求失败 order_id: {}, custom_id: {}, res_data: {:?}", order_id, custom_id, res_data);
|
|
|
+// Err(Error::new(ErrorKind::Other, message))
|
|
|
+// }
|
|
|
+// }
|
|
|
+// // 批量撤销订单
|
|
|
+// async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
+// let symbol = self.symbol.replace("_", "").to_uppercase();
|
|
|
+// let res_data = self.request.cancel_order_all(symbol).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// info!("{}", res_data.data.to_string());
|
|
|
+// let result = vec![];
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
+// let orders_res_data = self.request.get_pending_orders().await;
|
|
|
+// if orders_res_data.code == 200 {
|
|
|
+// let result = vec![];
|
|
|
+// let orders_res_data_json = orders_res_data.data.as_array().unwrap();
|
|
|
+// for order in orders_res_data_json {
|
|
|
+// let cancel_res_data = self.request.cancel_order_all( order["market"].as_str().unwrap().to_string()).await;
|
|
|
+// if cancel_res_data.code != 200 {
|
|
|
+// return Err(Error::new(ErrorKind::Other, cancel_res_data.to_string()));
|
|
|
+// }
|
|
|
+// }
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, orders_res_data.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, "coin_ex:该交易所暂未实现自动订单下单".to_string()))
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
|
|
|
+// Err(Error::new(ErrorKind::NotFound, "coin_ex:该交易所暂未实现取消自动订单".to_string()))
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 设置持仓模式
|
|
|
+// async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
|
|
|
+// Err(Error::new(ErrorKind::NotFound, "coin_ex:该交易所只允许单向持仓,无法设置持仓模式".to_string()))
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 更新杠杆
|
|
|
+// async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
|
|
|
+// let leverage_int = leverage.parse::<i32>().unwrap();
|
|
|
+// let symbol = self.symbol.replace("_", "").to_uppercase();
|
|
|
+// let res_data = self.request.setting_dual_leverage(symbol, leverage_int).await;
|
|
|
+// if res_data.code == 200 {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let result = res_data_str.clone();
|
|
|
+// Ok(result.to_string())
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "Coinex:该交易所方法未实现".to_string())) }
|
|
|
+//
|
|
|
+// async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
|
|
|
+// Err(Error::new(ErrorKind::NotFound, "Coinex wallet_transfers:该交易所方法未实现".to_string()))
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 指令下单
|
|
|
+// async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack) {
|
|
|
+// let mut handles = vec![];
|
|
|
+//
|
|
|
+// // 下单指令
|
|
|
+// for item in order_command.limits_open.keys() {
|
|
|
+// let mut ts = trace_stack.clone();
|
|
|
+//
|
|
|
+// let amount = Decimal::from_str(&*order_command.limits_open[item].get(0).unwrap().clone()).unwrap();
|
|
|
+// let side = order_command.limits_open[item].get(1).unwrap().clone();
|
|
|
+// let price = Decimal::from_str(&*order_command.limits_open[item].get(2).unwrap().clone()).unwrap();
|
|
|
+// let cid = order_command.limits_open[item].get(3).unwrap().clone();
|
|
|
+//
|
|
|
+// // order_name: [数量,方向,价格,c_id]
|
|
|
+// let mut self_clone = self.clone();
|
|
|
+// let handle = spawn(async move {
|
|
|
+// // info!("数量 {},方向 {},价格 {},c_id {}", amount, side, price, cid);
|
|
|
+// ts.on_before_send();
|
|
|
+// let result = self_clone.take_order(&cid, &side, price, amount).await;
|
|
|
+// ts.on_after_send();
|
|
|
+//
|
|
|
+// match result {
|
|
|
+// Ok(mut result) => {
|
|
|
+// result.trace_stack = ts;
|
|
|
+// // info!("数量 {},方向 {},价格 {},c_id {}", amount, side, price, cid);
|
|
|
+// self_clone.order_sender.send(result).await.unwrap();
|
|
|
+// }
|
|
|
+// Err(error) => {
|
|
|
+// let mut err_order = Order::new();
|
|
|
+// err_order.custom_id = cid.clone();
|
|
|
+// err_order.status = "REMOVE".to_string();
|
|
|
+// // info!("coinex下单error 数量: {},方向: {},价格: {},c_id: {}, err: {}", amount, side, price, cid, error);
|
|
|
+// self_clone.order_sender.send(err_order).await.unwrap();
|
|
|
+// self_clone.error_sender.send(error).await.unwrap();
|
|
|
+// // 触发限频
|
|
|
+// // if error_info.to_string().contains("213:Please don't try too frequently") {
|
|
|
+// // Err(Error::new(ErrorKind::Other, "触发限频, 请调整下单频率"))
|
|
|
+// // }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// });
|
|
|
+// handles.push(handle)
|
|
|
+// }
|
|
|
+// let futures = FuturesUnordered::from_iter(handles);
|
|
|
+// // 等待所有任务完成
|
|
|
+// let _: Result<Vec<_>, _> = futures.try_collect().await;
|
|
|
+//
|
|
|
+// // 撤销订单
|
|
|
+// let mut cancel_handlers = vec![];
|
|
|
+// for item in order_command.cancel.keys() {
|
|
|
+// let order_id = order_command.cancel[item].get(1).unwrap().clone();
|
|
|
+// let custom_id = order_command.cancel[item].get(0).unwrap().clone();
|
|
|
+//
|
|
|
+// let mut self_clone = self.clone();
|
|
|
+// let handle = spawn(async move {
|
|
|
+// let result = self_clone.cancel_order(&order_id, &custom_id).await;
|
|
|
+// match result {
|
|
|
+// Ok(order) => {
|
|
|
+// if order.status == "REMOVE" {
|
|
|
+// // 由于有时撤单成功ws不推送,所以加入rest
|
|
|
+// self_clone.order_sender.send(order).await.unwrap();
|
|
|
+// }
|
|
|
+// }
|
|
|
+// Err(error) => {
|
|
|
+// // info!("撤单失败:{:?}", error.to_string());
|
|
|
+// // 取消失败去查订单。
|
|
|
+// let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
+// match query_rst {
|
|
|
+// Ok(order) => {
|
|
|
+// // info!("查单 订单详情:{:?}", order);
|
|
|
+// self_clone.order_sender.send(order).await.unwrap();
|
|
|
+// }
|
|
|
+// Err(_err) => {
|
|
|
+// // warn!("撤单失败,而且查单也失败了,Coinex_io_swap,oid={}, cid={} err={:?}", order_id.clone(), custom_id.clone(), err);
|
|
|
+// // panic!("撤单失败,而且查单也失败了,Coinex_io_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
|
|
|
+// }
|
|
|
+// }
|
|
|
+// self_clone.error_sender.send(error).await.unwrap();
|
|
|
+// }
|
|
|
+// }
|
|
|
+// });
|
|
|
+// cancel_handlers.push(handle)
|
|
|
+// }
|
|
|
+// let futures = FuturesUnordered::from_iter(cancel_handlers);
|
|
|
+// // 等待所有任务完成
|
|
|
+// let _: Result<Vec<_>, _> = futures.try_collect().await;
|
|
|
+//
|
|
|
+// // 检查订单指令
|
|
|
+// let mut check_handlers = vec![];
|
|
|
+// for item in order_command.check.keys() {
|
|
|
+// let order_id = order_command.check[item].get(1).unwrap().clone();
|
|
|
+// let custom_id = order_command.check[item].get(0).unwrap().clone();
|
|
|
+//
|
|
|
+// let mut self_clone = self.clone();
|
|
|
+// let handle = spawn(async move {
|
|
|
+// let result = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
+// match result {
|
|
|
+// Ok(result) => {
|
|
|
+// self_clone.order_sender.send(result).await.unwrap();
|
|
|
+// }
|
|
|
+// Err(error) => {
|
|
|
+// self_clone.error_sender.send(error).await.unwrap();
|
|
|
+// }
|
|
|
+// }
|
|
|
+// });
|
|
|
+// check_handlers.push(handle)
|
|
|
+// }
|
|
|
+//
|
|
|
+// let futures = FuturesUnordered::from_iter(check_handlers);
|
|
|
+// // 等待所有任务完成
|
|
|
+// let _: Result<Vec<_>, _> = futures.try_collect().await;
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// pub fn format_position_item(position: &Value, ct_val: Decimal) -> Position {
|
|
|
+// let position_mode = match position["side"].as_str().unwrap_or("") {
|
|
|
+// "long" => PositionModeEnum::Long,
|
|
|
+// "short" => PositionModeEnum::Short,
|
|
|
+// _ => {
|
|
|
+// error!("coinex_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
|
|
|
+// panic!("coinex_swap:格式化持仓模式错误!\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_or("").parse().unwrap(),
|
|
|
+// 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(),
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// pub fn format_order_item(order: Value, ct_val: Decimal, status: &str) -> Order {
|
|
|
+// // info!("order_format {}", order);
|
|
|
+// let size;
|
|
|
+// match order["amount"].as_str() {
|
|
|
+// Some(val) => {
|
|
|
+// size = Decimal::from_str(val).unwrap()
|
|
|
+// },
|
|
|
+// None => {
|
|
|
+// error!("coinex_swap:格式化订单大小错误!\nformat_order_item:order={:?} status={}", order, status);
|
|
|
+// panic!("coinex_swap:格式化订单大小失败,退出程序!");
|
|
|
+// }
|
|
|
+// }
|
|
|
+// // info!("order {}", order);
|
|
|
+// // 通过客户端id查询订单 只能查出未完成订单(没有状态字段)
|
|
|
+// let status = order["status"].as_str().unwrap_or(status);
|
|
|
+// let text = order["client_id"].as_str().unwrap_or("");
|
|
|
+//
|
|
|
+// let deal_amount = Decimal::from_str(&order["filled_amount"].as_str().unwrap()).unwrap();
|
|
|
+// let filled_value = Decimal::from_str(&order["filled_value"].as_str().unwrap()).unwrap();
|
|
|
+//
|
|
|
+// let amount = size * ct_val;
|
|
|
+// let mut avg_price = Decimal::ZERO;
|
|
|
+// if deal_amount != Decimal::ZERO{
|
|
|
+// avg_price = filled_value/deal_amount;
|
|
|
+// }
|
|
|
+// let custom_status = if status == "filled" || status == "canceled" {
|
|
|
+// "REMOVE".to_string()
|
|
|
+// } else if status == "open" || status == "part_filled" || status == "part_canceled" {
|
|
|
+// "NEW".to_string()
|
|
|
+// } else {
|
|
|
+// error!("coinex_swap:格式化订单状态错误!\nformat_order_item:order={:?}", order);
|
|
|
+// "NULL".to_string()
|
|
|
+// };
|
|
|
+// let rst_order = Order {
|
|
|
+// id: order["order_id"].to_string(),
|
|
|
+// custom_id: text.to_string(),
|
|
|
+// 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("765 trace_stack".to_string()),
|
|
|
+// };
|
|
|
+// return rst_order;
|
|
|
+// }
|