|
|
@@ -1,685 +1,668 @@
|
|
|
-// use std::collections::{BTreeMap, HashMap};
|
|
|
-// 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 rust_decimal_macros::dec;
|
|
|
-// use serde_json::json;
|
|
|
-// use tokio::time::Instant;
|
|
|
-// use tracing::{error, info};
|
|
|
-// use exchanges::kucoin_swap_rest::KucoinSwapRest;
|
|
|
-// use global::trace_stack::TraceStack;
|
|
|
-// use crate::exchange::ExchangeEnum;
|
|
|
-// use crate::{Account, kucoin_handle, Market, Order, OrderCommand, Platform, Position, Ticker, utils};
|
|
|
-//
|
|
|
-// #[allow(dead_code)]
|
|
|
-// #[derive(Clone)]
|
|
|
-// pub struct KucoinSwap {
|
|
|
-// exchange: ExchangeEnum,
|
|
|
-// symbol: String,
|
|
|
-// is_colo: bool,
|
|
|
-// params: BTreeMap<String, String>,
|
|
|
-// request: KucoinSwapRest,
|
|
|
-// market: Market,
|
|
|
-// order_sender: Sender<Order>,
|
|
|
-// error_sender: Sender<Error>,
|
|
|
-// }
|
|
|
-//
|
|
|
-// impl KucoinSwap {
|
|
|
-// pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> KucoinSwap {
|
|
|
-// let market = Market::new();
|
|
|
-// let mut kucoin_swap = KucoinSwap {
|
|
|
-// exchange: ExchangeEnum::KucoinSwap,
|
|
|
-// symbol: symbol.to_uppercase(),
|
|
|
-// is_colo,
|
|
|
-// params: params.clone(),
|
|
|
-// request: KucoinSwapRest::new(is_colo, params.clone()),
|
|
|
-// market,
|
|
|
-// order_sender,
|
|
|
-// error_sender,
|
|
|
-// };
|
|
|
-// kucoin_swap.market = KucoinSwap::get_market(&mut kucoin_swap).await.unwrap_or(kucoin_swap.market);
|
|
|
-//
|
|
|
-// // 开启自动追加保证金
|
|
|
-// let append_rst = kucoin_swap.set_auto_deposit_status(true).await;
|
|
|
-//
|
|
|
-// info!("设置自动追加保证金:{:?}", append_rst.unwrap());
|
|
|
-//
|
|
|
-// return kucoin_swap;
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// #[async_trait]
|
|
|
-// impl Platform for KucoinSwap {
|
|
|
-// // 克隆方法
|
|
|
-// fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
|
|
|
-// fn get_self_exchange(&self) -> ExchangeEnum {
|
|
|
-// ExchangeEnum::KucoinSwap
|
|
|
-// }
|
|
|
-// // 获取交易对
|
|
|
-// 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_str = &res_data.data;
|
|
|
-// let result = res_data_str.clone();
|
|
|
-// Ok(result)
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// }
|
|
|
-// // 获取账号信息
|
|
|
-// async fn get_account(&mut self) -> Result<Account, Error> {
|
|
|
-// let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_array: Vec<&str> = symbol_mapper.split("_").collect();
|
|
|
-// let res_data = self.request.get_account(symbol_array[1].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();
|
|
|
-//
|
|
|
-// let balance = Decimal::from_f64(res_data_json["marginBalance"].as_f64().unwrap()).unwrap();
|
|
|
-// let available_balance = Decimal::from_f64(res_data_json["availableBalance"].as_f64().unwrap()).unwrap();
|
|
|
-// let frozen_balance = 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,
|
|
|
-// };
|
|
|
-// 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, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
-// let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let ct_val = self.market.ct_val;
|
|
|
-// let res_data = self.request.get_position(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 = kucoin_handle::format_position_item(&res_data_json, ct_val);
|
|
|
-// Ok(vec![result])
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
-// let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_array: Vec<&str> = symbol_mapper.split("_").collect();
|
|
|
-// let res_data = self.request.get_positions(symbol_array[1].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 mut result = Vec::new();
|
|
|
-// for item in res_data_json.iter() {
|
|
|
-// result.push(kucoin_handle::format_position_item(item, Decimal::ONE))
|
|
|
-// }
|
|
|
-// Ok(result)
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn get_ticker(&mut self) -> Result<Ticker, Error> {
|
|
|
-// let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let res_data = self.request.get_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 ticker_info = res_data_json;
|
|
|
-// let time = (Decimal::from_str(&*ticker_info["ts"].to_string()).unwrap() / dec!(1000000)).floor().to_i64().unwrap();
|
|
|
-//
|
|
|
-// let result = Ticker {
|
|
|
-// time,
|
|
|
-// high: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
|
|
|
-// low: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
|
|
|
-// sell: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
|
|
|
-// buy: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
|
|
|
-// last: Decimal::from_str(ticker_info["price"].as_str().unwrap()).unwrap(),
|
|
|
-// volume: Decimal::from_str(&ticker_info["size"].to_string()).unwrap(),
|
|
|
-// };
|
|
|
-// 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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper, ""));
|
|
|
-// let res_data = self.request.get_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 ticker_info = res_data_json;
|
|
|
-// let time = (Decimal::from_str(&*ticker_info["ts"].to_string()).unwrap() / dec!(1000000)).floor().to_i64().unwrap();
|
|
|
-// let result = Ticker {
|
|
|
-// time,
|
|
|
-// high: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
|
|
|
-// low: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
|
|
|
-// sell: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
|
|
|
-// buy: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
|
|
|
-// last: Decimal::from_str(ticker_info["price"].as_str().unwrap()).unwrap(),
|
|
|
-// volume: Decimal::from_str(&ticker_info["size"].to_string()).unwrap(),
|
|
|
-// };
|
|
|
-// Ok(result)
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn get_market(&mut self) -> Result<Market, Error> {
|
|
|
-// let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let res_data = self.request.get_market_details().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 market_info = res_data_json.iter().find(|item| item["symbol"].as_str().unwrap() == symbol_format);
|
|
|
-// match market_info {
|
|
|
-// None => {
|
|
|
-// error!("kucoin_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// Some(value) => {
|
|
|
-// let base_asset = value["baseCurrency"].as_str().unwrap_or("").to_string();
|
|
|
-// let base_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, base_asset.as_str());
|
|
|
-// let quote_asset = value["quoteCurrency"].as_str().unwrap_or("").to_string();
|
|
|
-// let quote_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, quote_asset.as_str());
|
|
|
-// let tick_size = Decimal::from_f64(value["tickSize"].as_f64().unwrap()).unwrap();
|
|
|
-// let min_qty = Decimal::from_f64(value["lotSize"].as_f64().unwrap()).unwrap();
|
|
|
-// let ct_val = Decimal::from_f64(value["multiplier"].as_f64().unwrap()).unwrap();
|
|
|
-//
|
|
|
-// let amount_size = min_qty * ct_val;
|
|
|
-// let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
|
|
|
-// let amount_precision = Decimal::from_u32(ct_val.scale()).unwrap();
|
|
|
-// let min_notional = min_qty * ct_val;
|
|
|
-//
|
|
|
-// let result = Market {
|
|
|
-// symbol: format!("{}_{}", base_asset_mapper, quote_asset_mapper),
|
|
|
-// base_asset: base_asset_mapper,
|
|
|
-// quote_asset: quote_asset_mapper,
|
|
|
-// tick_size,
|
|
|
-// amount_size,
|
|
|
-// price_precision,
|
|
|
-// amount_precision,
|
|
|
-// min_qty,
|
|
|
-// max_qty: Decimal::from_f64(value["maxOrderQty"].as_f64().unwrap()).unwrap(),
|
|
|
-// min_notional,
|
|
|
-// max_notional: Decimal::from_f64(value["maxPrice"].as_f64().unwrap()).unwrap(),
|
|
|
-// 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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let res_data = self.request.get_market_details().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 market_info = res_data_json.iter().find(|item| item["symbol"].as_str().unwrap() == symbol_format);
|
|
|
-// match market_info {
|
|
|
-// None => {
|
|
|
-// error!("kucoin_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// Some(value) => {
|
|
|
-// let base_asset = value["baseCurrency"].as_str().unwrap_or("").to_string();
|
|
|
-// let base_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, base_asset.as_str());
|
|
|
-// let quote_asset = value["quoteCurrency"].as_str().unwrap_or("").to_string();
|
|
|
-// let quote_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, quote_asset.as_str());
|
|
|
-// let tick_size = Decimal::from_f64(value["tickSize"].as_f64().unwrap()).unwrap();
|
|
|
-// let min_qty = Decimal::from_f64(value["lotSize"].as_f64().unwrap()).unwrap();
|
|
|
-// let ct_val = Decimal::from_f64(value["multiplier"].as_f64().unwrap()).unwrap();
|
|
|
-//
|
|
|
-// let amount_size = min_qty * ct_val;
|
|
|
-// let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
|
|
|
-// let amount_precision = Decimal::from_u32(ct_val.scale()).unwrap();
|
|
|
-// let min_notional = min_qty * ct_val;
|
|
|
-//
|
|
|
-// let result = Market {
|
|
|
-// symbol: format!("{}_{}", base_asset_mapper, quote_asset_mapper),
|
|
|
-// base_asset: base_asset_mapper,
|
|
|
-// quote_asset: quote_asset_mapper,
|
|
|
-// tick_size,
|
|
|
-// amount_size,
|
|
|
-// price_precision,
|
|
|
-// amount_precision,
|
|
|
-// min_qty,
|
|
|
-// max_qty: Decimal::from_f64(value["maxOrderQty"].as_f64().unwrap()).unwrap(),
|
|
|
-// min_notional,
|
|
|
-// max_notional: Decimal::from_f64(value["maxPrice"].as_f64().unwrap()).unwrap(),
|
|
|
-// 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 ct_val = self.market.ct_val;
|
|
|
-// let res_data = self.request.get_orders_details(order_id.to_string(), 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();
|
|
|
-// let result = format_order_item(res_data_json, ct_val);
|
|
|
-// 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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let ct_val = self.market.ct_val;
|
|
|
-// let res_data = self.request.get_orders(status.to_string(), symbol_format.clone()).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 order_list: Vec<serde_json::Value> = res_data_json["items"].as_array().unwrap().clone();
|
|
|
-// let order_info: Vec<&serde_json::Value> = order_list.iter().filter(|item| item["symbol"].as_str().unwrap_or("") == symbol_format.clone()).collect();
|
|
|
-// let result = order_info.iter().map(|&item| format_order_item(item.clone(), ct_val)).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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let ct_val = self.market.ct_val;
|
|
|
-// let mut params = json!({
|
|
|
-// "clientOid": custom_id,
|
|
|
-// "symbol": symbol_format,
|
|
|
-// "leverage": "10",
|
|
|
-// "reduceOnly":false,
|
|
|
-// "price": price.to_string(),
|
|
|
-// });
|
|
|
-// params["type"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
|
|
|
-// let size = (amount / ct_val).floor();
|
|
|
-// params["size"] = json!(size);
|
|
|
-// match origin_side {
|
|
|
-// "kd" => {
|
|
|
-// params["side"] = json!("buy");
|
|
|
-// }
|
|
|
-// "pd" => {
|
|
|
-// params["side"] = json!("sell");
|
|
|
-// }
|
|
|
-// "kk" => {
|
|
|
-// params["side"] = json!("sell");
|
|
|
-// }
|
|
|
-// "pk" => {
|
|
|
-// params["side"] = json!("buy");
|
|
|
-// }
|
|
|
-// _ => { error!("下单参数错误"); }
|
|
|
-// };
|
|
|
-//
|
|
|
-// let res_data = self.request.swap_order(params).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 id = res_data_json["orderId"].as_str().unwrap().to_string();
|
|
|
-// let result = Order {
|
|
|
-// id,
|
|
|
-// custom_id: custom_id.to_string(),
|
|
|
-// price,
|
|
|
-// amount,
|
|
|
-// deal_amount: Decimal::ZERO,
|
|
|
-// avg_price: Decimal::ZERO,
|
|
|
-// status: "NEW".to_string(),
|
|
|
-// order_type: "".to_string(),
|
|
|
-// trace_stack: TraceStack::new(0, Instant::now()).on_special("359 kucoin_swap".to_string()),
|
|
|
-// };
|
|
|
-// Ok(result)
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.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> {
|
|
|
-// let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let mut params = json!({
|
|
|
-// "clientOid": custom_id,
|
|
|
-// "symbol": symbol_format,
|
|
|
-// "leverage": "10",
|
|
|
-// "reduceOnly":false,
|
|
|
-// "price": price.to_string(),
|
|
|
-// });
|
|
|
-// let size = (amount / ct_val).floor();
|
|
|
-// params["size"] = json!(size);
|
|
|
-// if price.eq(&Decimal::ZERO){
|
|
|
-// params["type"] = json!("market");
|
|
|
-// }
|
|
|
-// match origin_side {
|
|
|
-// "kd" => {
|
|
|
-// params["side"] = json!("buy");
|
|
|
-// }
|
|
|
-// "pd" => {
|
|
|
-// params["side"] = json!("sell");
|
|
|
-// }
|
|
|
-// "kk" => {
|
|
|
-// params["side"] = json!("sell");
|
|
|
-// }
|
|
|
-// "pk" => {
|
|
|
-// params["side"] = json!("buy");
|
|
|
-// }
|
|
|
-// _ => { error!("下单参数错误"); }
|
|
|
-// };
|
|
|
-//
|
|
|
-// let res_data = self.request.swap_order(params).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 id = res_data_json["orderId"].as_str().unwrap().to_string();
|
|
|
-// let result = Order {
|
|
|
-// id,
|
|
|
-// custom_id: custom_id.to_string(),
|
|
|
-// price,
|
|
|
-// amount,
|
|
|
-// deal_amount: Decimal::ZERO,
|
|
|
-// avg_price: Decimal::ZERO,
|
|
|
-// status: "NEW".to_string(),
|
|
|
-// order_type: "".to_string(),
|
|
|
-// trace_stack: TraceStack::new(0, Instant::now()).on_special("408 kucoin_swap".to_string()),
|
|
|
-// };
|
|
|
-// 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 res_data = self.request.cancel_order(order_id.to_string(), custom_id.to_string()).await;
|
|
|
-// if order_id == "" {
|
|
|
-// error!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id);
|
|
|
-// panic!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id)
|
|
|
-// }
|
|
|
-// 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 cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
|
|
|
-// let id = cancel_ids[0].as_str().unwrap().to_string();
|
|
|
-// let result = Order {
|
|
|
-// id,
|
|
|
-// custom_id: custom_id.to_string(),
|
|
|
-// price: Decimal::ZERO,
|
|
|
-// amount: Decimal::ZERO,
|
|
|
-// deal_amount: Decimal::ZERO,
|
|
|
-// avg_price: Decimal::ZERO,
|
|
|
-// status: "REMOVE".to_string(),
|
|
|
-// order_type: "".to_string(),
|
|
|
-// trace_stack: TraceStack::new(0, Instant::now()).on_special("436 kucoin_swap".to_string()),
|
|
|
-// };
|
|
|
-// Ok(result)
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
-// let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let res_data = self.request.cancel_orders(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 cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
|
|
|
-// let result = cancel_ids.iter().map(|item|
|
|
|
-// Order {
|
|
|
-// id: item.as_str().unwrap().to_string(),
|
|
|
-// custom_id: "".to_string(),
|
|
|
-// price: Decimal::ZERO,
|
|
|
-// amount: Decimal::ZERO,
|
|
|
-// deal_amount: Decimal::ZERO,
|
|
|
-// avg_price: Decimal::ZERO,
|
|
|
-// status: "REMOVE".to_string(),
|
|
|
-// order_type: "".to_string(),
|
|
|
-// trace_stack: TraceStack::new(0, Instant::now()).on_special("461 kucoin_swap".to_string()),
|
|
|
-// }
|
|
|
-// ).collect();
|
|
|
-// Ok(result)
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
-// let res_data = self.request.cancel_order_all().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 cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
|
|
|
-// let result = cancel_ids.iter().map(|item|
|
|
|
-// Order {
|
|
|
-// id: item.as_str().unwrap().to_string(),
|
|
|
-// custom_id: "".to_string(),
|
|
|
-// price: Decimal::ZERO,
|
|
|
-// amount: Decimal::ZERO,
|
|
|
-// deal_amount: Decimal::ZERO,
|
|
|
-// avg_price: Decimal::ZERO,
|
|
|
-// status: "REMOVE".to_string(),
|
|
|
-// order_type: "".to_string(),
|
|
|
-// trace_stack: TraceStack::new(0, Instant::now()).on_special("486 kucoin_swap".to_string()),
|
|
|
-// }
|
|
|
-// ).collect();
|
|
|
-// Ok(result)
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
|
|
|
-// Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
|
|
|
-// Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn set_auto_deposit_status(&mut self, status: bool) -> Result<String, Error> {
|
|
|
-// let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
-// let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
-// let res_data = self.request.auto_deposit_status(symbol_format, status).await;
|
|
|
-// if res_data.code == "200" {
|
|
|
-// let res_data_str = &res_data.data;
|
|
|
-// let result = res_data_str.clone();
|
|
|
-// Ok(result)
|
|
|
-// } else {
|
|
|
-// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
|
|
|
-// Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 指令下单
|
|
|
-// async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack) {
|
|
|
-// let mut handles = vec![];
|
|
|
-// // 撤销订单
|
|
|
-// let cancel = order_command.cancel;
|
|
|
-// for item in cancel.keys() {
|
|
|
-// let mut self_clone = self.clone();
|
|
|
-// let cancel_clone = cancel.clone();
|
|
|
-// let item_clone = item.clone();
|
|
|
-// let order_id = cancel_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
|
|
|
-// let custom_id = cancel_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
|
|
|
-// let result_sd = self.order_sender.clone();
|
|
|
-// let err_sd = self.error_sender.clone();
|
|
|
-// let handle = tokio::spawn(async move {
|
|
|
-// if order_id != "" {
|
|
|
-// let result = self_clone.cancel_order(&order_id, &custom_id).await;
|
|
|
-// match result {
|
|
|
-// Ok(_) => {
|
|
|
-// // result_sd.send(result).await.unwrap();
|
|
|
-// }
|
|
|
-// Err(error) => {
|
|
|
-// // 取消失败去查订单。
|
|
|
-// let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
-// match query_rst {
|
|
|
-// Ok(order) => {
|
|
|
-// result_sd.send(order).await.unwrap();
|
|
|
-// }
|
|
|
-// Err(_query_err) => {
|
|
|
-// // error!(?_query_err);
|
|
|
-// // error!("撤单失败,而且查单也失败了,kucoin_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
|
|
|
-// }
|
|
|
-// }
|
|
|
-// err_sd.send(error).await.unwrap();
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-// });
|
|
|
-// handles.push(handle)
|
|
|
-// }
|
|
|
-// // 下单指令
|
|
|
-// let mut limits = HashMap::new();
|
|
|
-// limits.extend(order_command.limits_open);
|
|
|
-// limits.extend(order_command.limits_close);
|
|
|
-// for item in limits.keys() {
|
|
|
-// let mut self_clone = self.clone();
|
|
|
-// let limits_clone = limits.clone();
|
|
|
-// let item_clone = item.clone();
|
|
|
-// let result_sd = self.order_sender.clone();
|
|
|
-// let err_sd = self.error_sender.clone();
|
|
|
-// let ts = trace_stack.clone();
|
|
|
-//
|
|
|
-// let handle = tokio::spawn(async move {
|
|
|
-// let value = limits_clone[&item_clone].clone();
|
|
|
-// // info!(?value);
|
|
|
-// // info!("{}", ts.to_string());
|
|
|
-// let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
|
|
|
-// let side = value.get(1).unwrap();
|
|
|
-// let price = Decimal::from_str(value.get(2).unwrap_or(&"0".to_string())).unwrap();
|
|
|
-// let cid = value.get(3).unwrap();
|
|
|
-//
|
|
|
-// // order_name: [数量,方向,价格,c_id]
|
|
|
-// let result = self_clone.take_order(cid, side, price, amount).await;
|
|
|
-// match result {
|
|
|
-// Ok(mut result) => {
|
|
|
-// // ts.on_after_send();
|
|
|
-// result.trace_stack = ts.clone();
|
|
|
-//
|
|
|
-// result_sd.send(result).await.unwrap();
|
|
|
-// }
|
|
|
-// Err(error) => {
|
|
|
-// let mut err_order = Order::new();
|
|
|
-// err_order.custom_id = cid.clone();
|
|
|
-// err_order.status = "REMOVE".to_string();
|
|
|
-//
|
|
|
-// result_sd.send(err_order).await.unwrap();
|
|
|
-// err_sd.send(error).await.unwrap();
|
|
|
-// }
|
|
|
-// }
|
|
|
-// });
|
|
|
-// handles.push(handle)
|
|
|
-// }
|
|
|
-// // if limits.len() > 0 {
|
|
|
-// // info!("");
|
|
|
-// // }
|
|
|
-// // 检查订单指令
|
|
|
-// let check = order_command.check;
|
|
|
-// for item in check.keys() {
|
|
|
-// let mut self_clone = self.clone();
|
|
|
-// let check_clone = check.clone();
|
|
|
-// let item_clone = item.clone();
|
|
|
-// let order_id = check_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
|
|
|
-// let custom_id = check_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
|
|
|
-// let result_sd = self.order_sender.clone();
|
|
|
-// let err_sd = self.error_sender.clone();
|
|
|
-// let handle = tokio::spawn(async move {
|
|
|
-// let result = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
-// match result {
|
|
|
-// Ok(result) => {
|
|
|
-// result_sd.send(result).await.unwrap();
|
|
|
-// }
|
|
|
-// Err(error) => {
|
|
|
-// err_sd.send(error).await.unwrap();
|
|
|
-// }
|
|
|
-// }
|
|
|
-// });
|
|
|
-// handles.push(handle)
|
|
|
-// }
|
|
|
-//
|
|
|
-// let futures = FuturesUnordered::from_iter(handles);
|
|
|
-// let _: Result<Vec<_>, _> = futures.try_collect().await;
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// pub fn format_order_item(order: serde_json::Value, ct_val: Decimal) -> Order {
|
|
|
-// let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
|
|
|
-// let size = Decimal::from_f64(order["size"].as_f64().unwrap()).unwrap();
|
|
|
-// let status = order["status"].as_str().unwrap_or("");
|
|
|
-// let filled_size = Decimal::from_f64(order["filledSize"].as_f64().unwrap()).unwrap();
|
|
|
-// let filled_value = Decimal::from_str(order["filledValue"].as_str().unwrap()).unwrap();
|
|
|
-//
|
|
|
-// let amount = size * ct_val;
|
|
|
-// let deal_amount = filled_size * ct_val;
|
|
|
-// let avg_price = if deal_amount.is_zero() { Decimal::ZERO } else { filled_value / deal_amount };
|
|
|
-// let custom_status;
|
|
|
-// if ["cancelled", "closed", "finished"].contains(&status) {
|
|
|
-// custom_status = "REMOVE".to_string();
|
|
|
-// } else if status == "open" {
|
|
|
-// custom_status = "NEW".to_string();
|
|
|
-// } else {
|
|
|
-// custom_status = "NULL".to_string();
|
|
|
-// };
|
|
|
-// Order {
|
|
|
-// id: order["id"].as_str().unwrap().to_string(),
|
|
|
-// custom_id: order["clientOid"].as_str().unwrap_or("").to_string(),
|
|
|
-// price,
|
|
|
-// amount,
|
|
|
-// deal_amount,
|
|
|
-// avg_price,
|
|
|
-// status: custom_status,
|
|
|
-// order_type: order["type"].as_str().unwrap().to_string(),
|
|
|
-// trace_stack: TraceStack::new(0, Instant::now()).on_special("655 kucoin_swap".to_string()),
|
|
|
-// }
|
|
|
-// }
|
|
|
+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 rust_decimal_macros::dec;
|
|
|
+use serde_json::{json, Value};
|
|
|
+use tokio::time::Instant;
|
|
|
+use tracing::{error, info};
|
|
|
+use exchanges::kucoin_swap_rest::KucoinSwapRest;
|
|
|
+use global::trace_stack::TraceStack;
|
|
|
+use crate::exchange::ExchangeEnum;
|
|
|
+use crate::{Account, kucoin_handle, Market, Order, OrderCommand, Platform, Position, Ticker, utils};
|
|
|
+
|
|
|
+#[allow(dead_code)]
|
|
|
+#[derive(Clone)]
|
|
|
+pub struct KucoinSwap {
|
|
|
+ exchange: ExchangeEnum,
|
|
|
+ symbol: String,
|
|
|
+ is_colo: bool,
|
|
|
+ params: BTreeMap<String, String>,
|
|
|
+ request: KucoinSwapRest,
|
|
|
+ market: Market,
|
|
|
+ order_sender: Sender<Order>,
|
|
|
+ error_sender: Sender<Error>,
|
|
|
+}
|
|
|
+
|
|
|
+impl KucoinSwap {
|
|
|
+ pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> KucoinSwap {
|
|
|
+ let market = Market::new();
|
|
|
+ let mut kucoin_swap = KucoinSwap {
|
|
|
+ exchange: ExchangeEnum::KucoinSwap,
|
|
|
+ symbol: symbol.to_uppercase(),
|
|
|
+ is_colo,
|
|
|
+ params: params.clone(),
|
|
|
+ request: KucoinSwapRest::new(is_colo, params.clone()),
|
|
|
+ market,
|
|
|
+ order_sender,
|
|
|
+ error_sender,
|
|
|
+ };
|
|
|
+ kucoin_swap.market = KucoinSwap::get_market(&mut kucoin_swap).await.unwrap_or(kucoin_swap.market);
|
|
|
+
|
|
|
+ // 开启自动追加保证金
|
|
|
+ let append_rst = kucoin_swap.set_auto_deposit_status(true).await;
|
|
|
+
|
|
|
+ info!("设置自动追加保证金:{:?}", append_rst.unwrap());
|
|
|
+
|
|
|
+ return kucoin_swap;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[async_trait]
|
|
|
+impl Platform for KucoinSwap {
|
|
|
+ // 克隆方法
|
|
|
+ fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
|
|
|
+ fn get_self_exchange(&self) -> ExchangeEnum {
|
|
|
+ ExchangeEnum::KucoinSwap
|
|
|
+ }
|
|
|
+ // 获取交易对
|
|
|
+ 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" {
|
|
|
+ Ok(res_data.data.to_string())
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 获取账号信息
|
|
|
+ async fn get_account(&mut self) -> Result<Account, Error> {
|
|
|
+ let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_array: Vec<&str> = symbol_mapper.split("_").collect();
|
|
|
+ let res_data = self.request.get_account(symbol_array[1].to_string()).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+
|
|
|
+ let balance = Decimal::from_f64(res_data_json["marginBalance"].as_f64().unwrap()).unwrap();
|
|
|
+ let available_balance = Decimal::from_f64(res_data_json["availableBalance"].as_f64().unwrap()).unwrap();
|
|
|
+ let frozen_balance = 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,
|
|
|
+ };
|
|
|
+ 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, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
+ let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let ct_val = self.market.ct_val;
|
|
|
+ let res_data = self.request.get_position(symbol_format).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let result = kucoin_handle::format_position_item(&res_data.data, &ct_val);
|
|
|
+ Ok(vec![result])
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
+ let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_array: Vec<&str> = symbol_mapper.split("_").collect();
|
|
|
+ let res_data = self.request.get_positions(symbol_array[1].to_string()).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data.as_array().unwrap();
|
|
|
+ let mut result = Vec::new();
|
|
|
+ for item in res_data_json.iter() {
|
|
|
+ result.push(kucoin_handle::format_position_item(item, &Decimal::ONE))
|
|
|
+ }
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_ticker(&mut self) -> Result<Ticker, Error> {
|
|
|
+ let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let res_data = self.request.get_ticker(symbol_format).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let ticker_info = res_data_json;
|
|
|
+ let time = (Decimal::from_str(&*ticker_info["ts"].to_string()).unwrap() / dec!(1000000)).floor().to_i64().unwrap();
|
|
|
+
|
|
|
+ let result = Ticker {
|
|
|
+ time,
|
|
|
+ high: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ low: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ sell: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ buy: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ last: Decimal::from_str(ticker_info["price"].as_str().unwrap()).unwrap(),
|
|
|
+ volume: Decimal::from_str(&ticker_info["size"].to_string()).unwrap(),
|
|
|
+ };
|
|
|
+ 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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper, ""));
|
|
|
+ let res_data = self.request.get_ticker(symbol_format).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let ticker_info = res_data_json;
|
|
|
+ let time = (Decimal::from_str(&*ticker_info["ts"].to_string()).unwrap() / dec!(1000000)).floor().to_i64().unwrap();
|
|
|
+ let result = Ticker {
|
|
|
+ time,
|
|
|
+ high: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ low: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ sell: Decimal::from_str(ticker_info["bestAskPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ buy: Decimal::from_str(ticker_info["bestBidPrice"].as_str().unwrap()).unwrap(),
|
|
|
+ last: Decimal::from_str(ticker_info["price"].as_str().unwrap()).unwrap(),
|
|
|
+ volume: Decimal::from_str(&ticker_info["size"].to_string()).unwrap(),
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn get_market(&mut self) -> Result<Market, Error> {
|
|
|
+ let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let res_data = self.request.get_market_details().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["symbol"].as_str().unwrap() == symbol_format);
|
|
|
+ match market_info {
|
|
|
+ None => {
|
|
|
+ error!("kucoin_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ Some(value) => {
|
|
|
+ let base_asset = value["baseCurrency"].as_str().unwrap_or("").to_string();
|
|
|
+ let base_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, base_asset.as_str());
|
|
|
+ let quote_asset = value["quoteCurrency"].as_str().unwrap_or("").to_string();
|
|
|
+ let quote_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, quote_asset.as_str());
|
|
|
+ let tick_size = Decimal::from_f64(value["tickSize"].as_f64().unwrap()).unwrap();
|
|
|
+ let min_qty = Decimal::from_f64(value["lotSize"].as_f64().unwrap()).unwrap();
|
|
|
+ let ct_val = Decimal::from_f64(value["multiplier"].as_f64().unwrap()).unwrap();
|
|
|
+
|
|
|
+ let amount_size = min_qty * ct_val;
|
|
|
+ let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
|
|
|
+ let amount_precision = Decimal::from_u32(ct_val.scale()).unwrap();
|
|
|
+ let min_notional = min_qty * ct_val;
|
|
|
+
|
|
|
+ let result = Market {
|
|
|
+ symbol: format!("{}_{}", base_asset_mapper, quote_asset_mapper),
|
|
|
+ base_asset: base_asset_mapper,
|
|
|
+ quote_asset: quote_asset_mapper,
|
|
|
+ tick_size,
|
|
|
+ amount_size,
|
|
|
+ price_precision,
|
|
|
+ amount_precision,
|
|
|
+ min_qty,
|
|
|
+ max_qty: Decimal::from_f64(value["maxOrderQty"].as_f64().unwrap()).unwrap(),
|
|
|
+ min_notional,
|
|
|
+ max_notional: Decimal::from_f64(value["maxPrice"].as_f64().unwrap()).unwrap(),
|
|
|
+ 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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let res_data = self.request.get_market_details().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["symbol"].as_str().unwrap() == symbol_format);
|
|
|
+ match market_info {
|
|
|
+ None => {
|
|
|
+ error!("kucoin_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data);
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ Some(value) => {
|
|
|
+ let base_asset = value["baseCurrency"].as_str().unwrap_or("").to_string();
|
|
|
+ let base_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, base_asset.as_str());
|
|
|
+ let quote_asset = value["quoteCurrency"].as_str().unwrap_or("").to_string();
|
|
|
+ let quote_asset_mapper = utils::symbol_out_mapper(ExchangeEnum::KucoinSwap, quote_asset.as_str());
|
|
|
+ let tick_size = Decimal::from_f64(value["tickSize"].as_f64().unwrap()).unwrap();
|
|
|
+ let min_qty = Decimal::from_f64(value["lotSize"].as_f64().unwrap()).unwrap();
|
|
|
+ let ct_val = Decimal::from_f64(value["multiplier"].as_f64().unwrap()).unwrap();
|
|
|
+
|
|
|
+ let amount_size = min_qty * ct_val;
|
|
|
+ let price_precision = Decimal::from_u32(tick_size.scale()).unwrap();
|
|
|
+ let amount_precision = Decimal::from_u32(ct_val.scale()).unwrap();
|
|
|
+ let min_notional = min_qty * ct_val;
|
|
|
+
|
|
|
+ let result = Market {
|
|
|
+ symbol: format!("{}_{}", base_asset_mapper, quote_asset_mapper),
|
|
|
+ base_asset: base_asset_mapper,
|
|
|
+ quote_asset: quote_asset_mapper,
|
|
|
+ tick_size,
|
|
|
+ amount_size,
|
|
|
+ price_precision,
|
|
|
+ amount_precision,
|
|
|
+ min_qty,
|
|
|
+ max_qty: Decimal::from_f64(value["maxOrderQty"].as_f64().unwrap()).unwrap(),
|
|
|
+ min_notional,
|
|
|
+ max_notional: Decimal::from_f64(value["maxPrice"].as_f64().unwrap()).unwrap(),
|
|
|
+ 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 ct_val = self.market.ct_val;
|
|
|
+ let res_data = self.request.get_orders_details(order_id.to_string(), custom_id.to_string()).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let result = format_order_item(res_data_json, ct_val);
|
|
|
+ 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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let ct_val = self.market.ct_val;
|
|
|
+ let res_data = self.request.get_orders(status.to_string(), symbol_format.clone()).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let order_list = res_data_json["items"].as_array().unwrap().clone();
|
|
|
+ let order_info: Vec<_> = order_list.iter().filter(|item| item["symbol"].as_str().unwrap_or("") == symbol_format.clone()).collect();
|
|
|
+ let result = order_info.iter().map(|&item| format_order_item(item.clone(), ct_val)).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_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let ct_val = self.market.ct_val;
|
|
|
+ let mut params = json!({
|
|
|
+ "clientOid": custom_id,
|
|
|
+ "symbol": symbol_format,
|
|
|
+ "leverage": "10",
|
|
|
+ "reduceOnly":false,
|
|
|
+ "price": price.to_string(),
|
|
|
+ });
|
|
|
+ params["type"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
|
|
|
+ let size = (amount / ct_val).floor();
|
|
|
+ params["size"] = json!(size);
|
|
|
+ match origin_side {
|
|
|
+ "kd" => {
|
|
|
+ params["side"] = json!("buy");
|
|
|
+ }
|
|
|
+ "pd" => {
|
|
|
+ params["side"] = json!("sell");
|
|
|
+ }
|
|
|
+ "kk" => {
|
|
|
+ params["side"] = json!("sell");
|
|
|
+ }
|
|
|
+ "pk" => {
|
|
|
+ params["side"] = json!("buy");
|
|
|
+ }
|
|
|
+ _ => { error!("下单参数错误"); }
|
|
|
+ };
|
|
|
+
|
|
|
+ let res_data = self.request.swap_order(params).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let id = res_data_json["orderId"].as_str().unwrap().to_string();
|
|
|
+ let result = Order {
|
|
|
+ id,
|
|
|
+ custom_id: custom_id.to_string(),
|
|
|
+ price,
|
|
|
+ amount,
|
|
|
+ deal_amount: Decimal::ZERO,
|
|
|
+ avg_price: Decimal::ZERO,
|
|
|
+ status: "NEW".to_string(),
|
|
|
+ order_type: "".to_string(),
|
|
|
+ trace_stack: TraceStack::new(0, Instant::now()).on_special("359 kucoin_swap".to_string()),
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.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> {
|
|
|
+ let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let mut params = json!({
|
|
|
+ "clientOid": custom_id,
|
|
|
+ "symbol": symbol_format,
|
|
|
+ "leverage": "10",
|
|
|
+ "reduceOnly":false,
|
|
|
+ "price": price.to_string(),
|
|
|
+ });
|
|
|
+ let size = (amount / ct_val).floor();
|
|
|
+ params["size"] = json!(size);
|
|
|
+ if price.eq(&Decimal::ZERO){
|
|
|
+ params["type"] = json!("market");
|
|
|
+ }
|
|
|
+ match origin_side {
|
|
|
+ "kd" => {
|
|
|
+ params["side"] = json!("buy");
|
|
|
+ }
|
|
|
+ "pd" => {
|
|
|
+ params["side"] = json!("sell");
|
|
|
+ }
|
|
|
+ "kk" => {
|
|
|
+ params["side"] = json!("sell");
|
|
|
+ }
|
|
|
+ "pk" => {
|
|
|
+ params["side"] = json!("buy");
|
|
|
+ }
|
|
|
+ _ => { error!("下单参数错误"); }
|
|
|
+ };
|
|
|
+
|
|
|
+ let res_data = self.request.swap_order(params).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let id = res_data_json["orderId"].as_str().unwrap().to_string();
|
|
|
+ let result = Order {
|
|
|
+ id,
|
|
|
+ custom_id: custom_id.to_string(),
|
|
|
+ price,
|
|
|
+ amount,
|
|
|
+ deal_amount: Decimal::ZERO,
|
|
|
+ avg_price: Decimal::ZERO,
|
|
|
+ status: "NEW".to_string(),
|
|
|
+ order_type: "".to_string(),
|
|
|
+ trace_stack: TraceStack::new(0, Instant::now()).on_special("408 kucoin_swap".to_string()),
|
|
|
+ };
|
|
|
+ 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 res_data = self.request.cancel_order(order_id.to_string(), custom_id.to_string()).await;
|
|
|
+ if order_id == "" {
|
|
|
+ error!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id);
|
|
|
+ panic!("Kucoin:撤销订单错误,该交易所为提供自定义订单号撤销订单!\ncancel_order:order_id={:?},custom_id={:?}", order_id, custom_id)
|
|
|
+ }
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
|
|
|
+ let id = cancel_ids[0].as_str().unwrap().to_string();
|
|
|
+ let result = Order {
|
|
|
+ id,
|
|
|
+ custom_id: custom_id.to_string(),
|
|
|
+ price: Decimal::ZERO,
|
|
|
+ amount: Decimal::ZERO,
|
|
|
+ deal_amount: Decimal::ZERO,
|
|
|
+ avg_price: Decimal::ZERO,
|
|
|
+ status: "REMOVE".to_string(),
|
|
|
+ order_type: "".to_string(),
|
|
|
+ trace_stack: TraceStack::new(0, Instant::now()).on_special("436 kucoin_swap".to_string()),
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
+ let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let res_data = self.request.cancel_orders(symbol_format).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
|
|
|
+ let result = cancel_ids.iter().map(|item|
|
|
|
+ Order {
|
|
|
+ id: item.as_str().unwrap().to_string(),
|
|
|
+ custom_id: "".to_string(),
|
|
|
+ price: Decimal::ZERO,
|
|
|
+ amount: Decimal::ZERO,
|
|
|
+ deal_amount: Decimal::ZERO,
|
|
|
+ avg_price: Decimal::ZERO,
|
|
|
+ status: "REMOVE".to_string(),
|
|
|
+ order_type: "".to_string(),
|
|
|
+ trace_stack: TraceStack::new(0, Instant::now()).on_special("461 kucoin_swap".to_string()),
|
|
|
+ }
|
|
|
+ ).collect();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
+ let res_data = self.request.cancel_order_all().await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ let res_data_json = res_data.data;
|
|
|
+ let cancel_ids = res_data_json["cancelledOrderIds"].as_array().unwrap();
|
|
|
+ let result = cancel_ids.iter().map(|item|
|
|
|
+ Order {
|
|
|
+ id: item.as_str().unwrap().to_string(),
|
|
|
+ custom_id: "".to_string(),
|
|
|
+ price: Decimal::ZERO,
|
|
|
+ amount: Decimal::ZERO,
|
|
|
+ deal_amount: Decimal::ZERO,
|
|
|
+ avg_price: Decimal::ZERO,
|
|
|
+ status: "REMOVE".to_string(),
|
|
|
+ order_type: "".to_string(),
|
|
|
+ trace_stack: TraceStack::new(0, Instant::now()).on_special("486 kucoin_swap".to_string()),
|
|
|
+ }
|
|
|
+ ).collect();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, 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, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn cancel_stop_loss_order(&mut self, _order_id: &str) -> Result<Value, Error> {
|
|
|
+ Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
|
|
|
+ Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn set_dual_leverage(&mut self, _leverage: &str) -> Result<String, Error> {
|
|
|
+ Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn set_auto_deposit_status(&mut self, status: bool) -> Result<String, Error> {
|
|
|
+ let symbol_mapper = utils::symbol_enter_mapper(ExchangeEnum::KucoinSwap, self.symbol.as_str());
|
|
|
+ let symbol_format = format!("{}M", utils::format_symbol(symbol_mapper.clone(), ""));
|
|
|
+ let res_data = self.request.auto_deposit_status(symbol_format, status).await;
|
|
|
+ if res_data.code == "200" {
|
|
|
+ Ok(res_data.data.to_string())
|
|
|
+ } else {
|
|
|
+ Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> {
|
|
|
+ Err(Error::new(ErrorKind::NotFound, "kucoin_swap:该交易所方法未实现".to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 指令下单
|
|
|
+ async fn command_order(&mut self, order_command: &mut OrderCommand, trace_stack: &TraceStack) {
|
|
|
+ let mut handles = vec![];
|
|
|
+ // 下单指令,limits_open里已经包含了limits_close,在core里面处理过了
|
|
|
+ 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();
|
|
|
+
|
|
|
+ let mut self_clone = self.clone();
|
|
|
+ let handle = tokio::spawn(async move {
|
|
|
+ ts.on_before_send();
|
|
|
+ let result = self_clone.take_order(&cid, &side, price, amount).await;
|
|
|
+ ts.on_after_send();
|
|
|
+
|
|
|
+ match result {
|
|
|
+ Ok(mut result) => {
|
|
|
+ // ts.on_after_send();
|
|
|
+ result.trace_stack = ts.clone();
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+ self_clone.order_sender.send(err_order).await.unwrap();
|
|
|
+ self_clone.error_sender.send(error).await.unwrap();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ 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 = tokio::spawn(async move {
|
|
|
+ if order_id != "" {
|
|
|
+ let result = self_clone.cancel_order(&order_id, &custom_id).await;
|
|
|
+ match result {
|
|
|
+ Ok(_) => {
|
|
|
+ // result_sd.send(result).await.unwrap();
|
|
|
+ }
|
|
|
+ Err(error) => {
|
|
|
+ // 取消失败去查订单。
|
|
|
+ let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
+ match query_rst {
|
|
|
+ Ok(order) => {
|
|
|
+ self_clone.order_sender.send(order).await.unwrap();
|
|
|
+ }
|
|
|
+ Err(_query_err) => {
|
|
|
+ // error!(?_query_err);
|
|
|
+ // error!("撤单失败,而且查单也失败了,kucoin_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 = tokio::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_order_item(order: Value, ct_val: Decimal) -> Order {
|
|
|
+ let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
|
|
|
+ let size = Decimal::from_f64(order["size"].as_f64().unwrap()).unwrap();
|
|
|
+ let status = order["status"].as_str().unwrap_or("");
|
|
|
+ let filled_size = Decimal::from_f64(order["filledSize"].as_f64().unwrap()).unwrap();
|
|
|
+ let filled_value = Decimal::from_str(order["filledValue"].as_str().unwrap()).unwrap();
|
|
|
+
|
|
|
+ let amount = size * ct_val;
|
|
|
+ let deal_amount = filled_size * ct_val;
|
|
|
+ let avg_price = if deal_amount.is_zero() { Decimal::ZERO } else { filled_value / deal_amount };
|
|
|
+ let custom_status;
|
|
|
+ if ["cancelled", "closed", "finished"].contains(&status) {
|
|
|
+ custom_status = "REMOVE".to_string();
|
|
|
+ } else if status == "open" {
|
|
|
+ custom_status = "NEW".to_string();
|
|
|
+ } else {
|
|
|
+ custom_status = "NULL".to_string();
|
|
|
+ };
|
|
|
+ Order {
|
|
|
+ id: order["id"].as_str().unwrap().to_string(),
|
|
|
+ custom_id: order["clientOid"].as_str().unwrap_or("").to_string(),
|
|
|
+ price,
|
|
|
+ amount,
|
|
|
+ deal_amount,
|
|
|
+ avg_price,
|
|
|
+ status: custom_status,
|
|
|
+ order_type: order["type"].as_str().unwrap().to_string(),
|
|
|
+ trace_stack: TraceStack::new(0, Instant::now()).on_special("655 kucoin_swap".to_string()),
|
|
|
+ }
|
|
|
+}
|