|
|
@@ -1,5 +1,5 @@
|
|
|
use serde_json::json;
|
|
|
-use crate::exchange_libs::{BinanceExc, OkxExc};
|
|
|
+use crate::exchange_libs::{BinanceExc, OkxExc, ReqData};
|
|
|
|
|
|
// 深度结构体
|
|
|
#[derive(Debug)]
|
|
|
@@ -50,19 +50,20 @@ pub struct Account {
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
pub struct Order {
|
|
|
- pub id: String,
|
|
|
// 交易单唯一标识
|
|
|
- pub price: f64,
|
|
|
+ pub id: String,
|
|
|
// 下单价格
|
|
|
- pub amount: f64,
|
|
|
+ pub price: f64,
|
|
|
// 下单数量
|
|
|
- pub deal_amount: f64,
|
|
|
+ pub amount: f64,
|
|
|
// 成交数量
|
|
|
- pub avg_price: f64,
|
|
|
+ pub deal_amount: f64,
|
|
|
// 成交均价
|
|
|
- pub status: String,
|
|
|
+ pub avg_price: f64,
|
|
|
// 订单状态
|
|
|
- pub order_type: String, // 订单类型
|
|
|
+ pub status: String,
|
|
|
+ // 订单类型
|
|
|
+ pub order_type: String,
|
|
|
}
|
|
|
|
|
|
pub struct Exchange {
|
|
|
@@ -99,78 +100,75 @@ impl Exchange {
|
|
|
// 获取币安深度信息
|
|
|
// symbol: 交易币对, "BTC_USDT"
|
|
|
// limit: 返回条数, 最大 5000. 可选值:[5, 10, 20, 50, 100, 500, 1000, 5000]
|
|
|
- pub async fn get_binance_depth(&self, symbol: &String, limit: i32) -> Depth {
|
|
|
+ pub async fn get_binance_depth(&self, symbol: &String, limit: i32) -> Result<Depth, ReqData> {
|
|
|
let real_symbol = self.get_real_symbol(symbol, "".to_string());
|
|
|
let req_data = self.binance_exc.binance_depth(&real_symbol, &limit.to_string()).await;
|
|
|
- let depth_json_str = req_data.data;
|
|
|
- let depth: serde_json::Value = serde_json::from_str(&*depth_json_str).unwrap();
|
|
|
- let mut depth_asks: Vec<DepthItem> = vec![];
|
|
|
- for value in depth["asks"].as_array().unwrap() {
|
|
|
- let asks_item = DepthItem {
|
|
|
- price: value[0].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- amount: value[1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- };
|
|
|
- depth_asks.push(asks_item)
|
|
|
- }
|
|
|
- let mut depth_bids: Vec<DepthItem> = vec![];
|
|
|
- for value in depth["bids"].as_array().unwrap() {
|
|
|
- let bids_item = DepthItem {
|
|
|
- price: value[0].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- amount: value[1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ if req_data.code == "0" {
|
|
|
+ let req_data_str = req_data.data;
|
|
|
+ let req_data_json: serde_json::Value = serde_json::from_str(&*req_data_str).unwrap();
|
|
|
+ let depth_asks: Vec<DepthItem> = parse_depth_items(&req_data_json["asks"]);
|
|
|
+ let depth_bids: Vec<DepthItem> = parse_depth_items(&req_data_json["bids"]);
|
|
|
+ let result = Depth {
|
|
|
+ asks: depth_asks,
|
|
|
+ bids: depth_bids,
|
|
|
};
|
|
|
- depth_bids.push(bids_item)
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(req_data)
|
|
|
}
|
|
|
-
|
|
|
- let result = Depth {
|
|
|
- asks: depth_asks,
|
|
|
- bids: depth_bids,
|
|
|
- };
|
|
|
- return result;
|
|
|
}
|
|
|
|
|
|
// 获取币安K线数据信息
|
|
|
// symbol: 交易币对, "BTC_USDT"
|
|
|
// interval: K线间隔, 可选值:[1s, 1m, 3m, 5m, 15m,30m,1h, 2h, 4h, 6h, 8h,12h, 1d, 3d, 1w, 1M]
|
|
|
// limit: 返回条数, 最大 1000
|
|
|
- pub async fn get_binance_klines(&self, symbol: &String, interval: &String, limit: &i32) -> Vec<Record> {
|
|
|
+ pub async fn get_binance_klines(&self, symbol: &String, interval: &String, limit: &i32) -> Result<Vec<Record>, ReqData> {
|
|
|
let real_symbol = self.get_real_symbol(symbol, "".to_string());
|
|
|
let req_data = self.binance_exc.binance_k(&real_symbol, interval, &limit.to_string()).await;
|
|
|
- let klines_json_str = req_data.data;
|
|
|
- let klines: Vec<Vec<serde_json::Value>> = serde_json::from_str(&*klines_json_str).unwrap();
|
|
|
- let mut result: Vec<Record> = vec![];
|
|
|
- for i in 0..klines.len() {
|
|
|
- let record = Record {
|
|
|
- time: klines[i][0].as_i64().unwrap_or(0),
|
|
|
- open: klines[i][1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- high: klines[i][2].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- low: klines[i][3].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- close: klines[i][4].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- volume: klines[i][5].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- };
|
|
|
- result.push(record);
|
|
|
+ if req_data.code == "0" {
|
|
|
+ let req_data_str = req_data.data;
|
|
|
+ let req_data_json: Vec<Vec<serde_json::Value>> = serde_json::from_str(&*req_data_str).unwrap();
|
|
|
+ let mut result: Vec<Record> = vec![];
|
|
|
+ for item in req_data_json.iter() {
|
|
|
+ let record = Record {
|
|
|
+ time: item[0].as_i64().unwrap_or(0),
|
|
|
+ open: item[1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ high: item[2].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ low: item[3].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ close: item[4].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ volume: item[5].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ };
|
|
|
+ result.push(record);
|
|
|
+ }
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(req_data)
|
|
|
}
|
|
|
- return result;
|
|
|
}
|
|
|
|
|
|
// 获取okx账户信息数据
|
|
|
// symbol: 交易币对, "BTC_USDT"
|
|
|
- pub async fn get_okx_account(&self, symbol: &String) -> Account {
|
|
|
+ pub async fn get_okx_account(&self, symbol: &String) -> Result<Account, ReqData> {
|
|
|
let real_symbol = self.get_real_symbol(symbol, ",".to_string());
|
|
|
- let symbol_array: Vec<&str> = symbol.split("_").collect();
|
|
|
let req_data = self.okx_exc.okx_acc(&real_symbol).await;
|
|
|
- let account_json_str = req_data.data;
|
|
|
- let account: Vec<serde_json::Value> = serde_json::from_str(&*account_json_str).unwrap();
|
|
|
- let details = account[0]["details"].as_array().unwrap();
|
|
|
- let default_info = json!({"availBal":"0","fixedBal":"0"});
|
|
|
- let stocks_info = details.iter().find(|item| item["ccy"].as_str().unwrap() == symbol_array[0].to_string()).unwrap_or(&default_info);
|
|
|
- let balance_info = details.iter().find(|item| item["ccy"].as_str().unwrap() == symbol_array[1].to_string()).unwrap_or(&default_info);
|
|
|
- let result = Account {
|
|
|
- balance: stocks_info["availBal"].as_str().unwrap().parse().unwrap_or(0.0),
|
|
|
- frozen_balance: stocks_info["fixedBal"].as_str().unwrap().parse().unwrap_or(0.0),
|
|
|
- stocks: balance_info["availBal"].as_str().unwrap().parse().unwrap_or(0.0),
|
|
|
- frozen_stocks: balance_info["fixedBal"].as_str().unwrap().parse().unwrap_or(0.0),
|
|
|
- };
|
|
|
- return result;
|
|
|
+ if req_data.code == "0" {
|
|
|
+ let req_data_str = req_data.data;
|
|
|
+ let req_data_json: Vec<serde_json::Value> = serde_json::from_str(&*req_data_str).unwrap();
|
|
|
+ let symbol_array: Vec<&str> = symbol.split("_").collect();
|
|
|
+ let details = req_data_json[0]["details"].as_array().unwrap();
|
|
|
+ let default_info = json!({"availBal":"0","fixedBal":"0"});
|
|
|
+ let stocks_info = details.iter().find(|item| item["ccy"].as_str().unwrap() == symbol_array[0].to_string()).unwrap_or(&default_info);
|
|
|
+ let balance_info = details.iter().find(|item| item["ccy"].as_str().unwrap() == symbol_array[1].to_string()).unwrap_or(&default_info);
|
|
|
+ let result = Account {
|
|
|
+ balance: stocks_info["availBal"].as_str().unwrap().parse().unwrap_or(0.0),
|
|
|
+ frozen_balance: stocks_info["fixedBal"].as_str().unwrap().parse().unwrap_or(0.0),
|
|
|
+ stocks: balance_info["availBal"].as_str().unwrap().parse().unwrap_or(0.0),
|
|
|
+ frozen_stocks: balance_info["fixedBal"].as_str().unwrap().parse().unwrap_or(0.0),
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(req_data)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// OKX下单
|
|
|
@@ -179,50 +177,72 @@ impl Exchange {
|
|
|
// order_type: 订单类型, market:市价单, limit:限价单等 具体文档查看 https://www.okx.com/docs-v5/zh/#order-book-trading-trade-post-place-order
|
|
|
// price: 委托价格
|
|
|
// amount: 委托数量
|
|
|
- pub async fn place_okx_order(&self, symbol: &String, side: &String, order_type: &String, price: &String, amount: &String) -> String {
|
|
|
+ pub async fn place_okx_order(&self, symbol: &String, side: &String, order_type: &String, price: &String, amount: &String) -> Result<String, ReqData> {
|
|
|
let real_symbol = self.get_real_symbol(symbol, "-".to_string());
|
|
|
let req_data = self.okx_exc.okx_order(&real_symbol, "cash", side, order_type, price, amount).await;
|
|
|
- let order_json_str = req_data.data;
|
|
|
- let result_info: Vec<serde_json::Value> = serde_json::from_str(&*order_json_str).unwrap();
|
|
|
- let result = result_info[0]["ordId"].as_str().unwrap().parse().unwrap();
|
|
|
- return result;
|
|
|
+ if req_data.code == "0" {
|
|
|
+ let req_data_str = req_data.data;
|
|
|
+ let req_data_json: Vec<serde_json::Value> = serde_json::from_str(&*req_data_str).unwrap();
|
|
|
+ let result = req_data_json[0]["ordId"].as_str().unwrap().parse().unwrap();
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(req_data)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// OKX查询订单
|
|
|
// symbol: 交易币对, "BTC_USDT"
|
|
|
// order_id: 订单ID, "590910403358593111"
|
|
|
- pub async fn get_okx_order(&self, symbol: &String, order_id: &String) -> Order {
|
|
|
+ pub async fn get_okx_order(&self, symbol: &String, order_id: &String) -> Result<Order, ReqData> {
|
|
|
let real_symbol = self.get_real_symbol(symbol, "-".to_string());
|
|
|
let req_data = self.okx_exc.okx_get_order(&real_symbol, order_id).await;
|
|
|
- let order_json_str = req_data.data;
|
|
|
- let result_info: Vec<serde_json::Value> = serde_json::from_str(&*order_json_str).unwrap();
|
|
|
- let result = Order {
|
|
|
- id: result_info[0]["ordId"].as_str().unwrap().parse().unwrap(),
|
|
|
- price: result_info[0]["px"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- amount: result_info[0]["sz"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- deal_amount: result_info[0]["accFillSz"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- avg_price: result_info[0]["avgPx"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
- status: result_info[0]["state"].as_str().unwrap().parse().unwrap(),
|
|
|
- order_type: result_info[0]["instType"].as_str().unwrap().parse().unwrap(),
|
|
|
- };
|
|
|
- println!("{:?}", result);
|
|
|
- return result;
|
|
|
+ if req_data.code == "0" {
|
|
|
+ let req_data_str = req_data.data;
|
|
|
+ let req_data_json: Vec<serde_json::Value> = serde_json::from_str(&*req_data_str).unwrap();
|
|
|
+ let result = Order {
|
|
|
+ id: req_data_json[0]["ordId"].as_str().unwrap().parse().unwrap(),
|
|
|
+ price: req_data_json[0]["px"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ amount: req_data_json[0]["sz"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ deal_amount: req_data_json[0]["accFillSz"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ avg_price: req_data_json[0]["avgPx"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ status: req_data_json[0]["state"].as_str().unwrap().parse().unwrap(),
|
|
|
+ order_type: req_data_json[0]["instType"].as_str().unwrap().parse().unwrap(),
|
|
|
+ };
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(req_data)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// OKX撤销订单
|
|
|
// symbol: 交易币对, "BTC_USDT"
|
|
|
// order_id: 订单ID, "590910403358593111"
|
|
|
- pub async fn cancel_okx_order(&self, symbol: &String, order_id: &String) -> bool {
|
|
|
+ pub async fn cancel_okx_order(&self, symbol: &String, order_id: &String) -> Result<bool, ReqData> {
|
|
|
let real_symbol = self.get_real_symbol(symbol, "-".to_string());
|
|
|
let req_data = self.okx_exc.okx_revocation_order(&real_symbol, order_id).await;
|
|
|
- let order_json_str = req_data.data;
|
|
|
- let order_info: Vec<serde_json::Value> = serde_json::from_str(&*order_json_str).unwrap();
|
|
|
- let result_info = order_info[0]["sCode"].as_str().unwrap();
|
|
|
- let result = if result_info == "0" { true } else { false };
|
|
|
- return result;
|
|
|
+ if req_data.code == "0" {
|
|
|
+ let req_data_str = req_data.data;
|
|
|
+ let req_data_json: Vec<serde_json::Value> = serde_json::from_str(&*req_data_str).unwrap();
|
|
|
+ let order_info = req_data_json[0]["sCode"].as_str().unwrap();
|
|
|
+ let result = if order_info == "0" { true } else { false };
|
|
|
+ Ok(result)
|
|
|
+ } else {
|
|
|
+ Err(req_data)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 深度信息买单/卖单处理
|
|
|
+fn parse_depth_items(value: &serde_json::Value) -> Vec<DepthItem> {
|
|
|
+ let mut depth_items: Vec<DepthItem> = vec![];
|
|
|
+ for value in value.as_array().unwrap() {
|
|
|
+ depth_items.push(DepthItem {
|
|
|
+ price: value[0].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ amount: value[1].as_str().unwrap_or("0").parse().unwrap_or(0.0),
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return depth_items;
|
|
|
+}
|
|
|
|
|
|
// 单元测试集
|
|
|
#[cfg(test)]
|
|
|
@@ -230,11 +250,16 @@ mod tests {
|
|
|
use crate::exchange_middle_ware::{Exchange};
|
|
|
use crate::exchange_libs::{is_proxy};
|
|
|
|
|
|
+ // new Exchange
|
|
|
+ fn new_exchange() -> Exchange {
|
|
|
+ Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string())
|
|
|
+ }
|
|
|
+
|
|
|
// 测试new Exchange
|
|
|
#[tokio::test]
|
|
|
async fn test_new_exchange() {
|
|
|
is_proxy();
|
|
|
- let exchange = Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string());
|
|
|
+ let exchange = new_exchange();
|
|
|
println!("test_new_exchange:okx_access_key:{},okx_secret_key:{},okx_passphrase:{}", exchange.okx_access_key, exchange.okx_secret_key, exchange.okx_passphrase);
|
|
|
}
|
|
|
|
|
|
@@ -242,7 +267,7 @@ mod tests {
|
|
|
#[tokio::test]
|
|
|
async fn test_get_real_symbol() {
|
|
|
is_proxy();
|
|
|
- let exchange = Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string());
|
|
|
+ let exchange = new_exchange();
|
|
|
let real_symbol = exchange.get_real_symbol(&"BTC_USDT".to_string(), "".to_string());
|
|
|
println!("test_get_real_symbol:{}", real_symbol);
|
|
|
}
|
|
|
@@ -251,53 +276,53 @@ mod tests {
|
|
|
#[tokio::test]
|
|
|
async fn test_get_binance_depth() {
|
|
|
is_proxy();
|
|
|
- let exchange = Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string());
|
|
|
+ let exchange = new_exchange();
|
|
|
let depth = exchange.get_binance_depth(&"DOGE_USDT".to_string(), 10).await;
|
|
|
- println!("test_get_real_symbol:{:?}", depth);
|
|
|
+ println!("test_get_binance_depth:{:?}", depth);
|
|
|
}
|
|
|
|
|
|
// 测试binance获取k线
|
|
|
#[tokio::test]
|
|
|
async fn test_get_binance_klines() {
|
|
|
is_proxy();
|
|
|
- let exchange = Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string());
|
|
|
- let depth = exchange.get_binance_klines(&"DOGE_USDT".to_string(), &"5m".to_string(), &10).await;
|
|
|
- println!("test_get_real_symbol:{:?}", depth);
|
|
|
+ let exchange = new_exchange();
|
|
|
+ let klines = exchange.get_binance_klines(&"DOGE_USDT".to_string(), &"5m".to_string(), &10).await;
|
|
|
+ println!("test_get_binance_klines:{:?}", klines);
|
|
|
}
|
|
|
|
|
|
// 测试okx查询账户信息
|
|
|
#[tokio::test]
|
|
|
async fn test_get_okx_account() {
|
|
|
is_proxy();
|
|
|
- let exchange = Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string());
|
|
|
+ let exchange = new_exchange();
|
|
|
let account = exchange.get_okx_account(&"BTC_USDT".to_string()).await;
|
|
|
- println!("test_get_real_symbol:{:?}", account);
|
|
|
+ println!("test_get_okx_account:{:?}", account);
|
|
|
}
|
|
|
|
|
|
// 测试okx下订单
|
|
|
#[tokio::test]
|
|
|
async fn test_place_okx_order() {
|
|
|
is_proxy();
|
|
|
- let exchange = Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string());
|
|
|
+ let exchange = new_exchange();
|
|
|
let order_id = exchange.place_okx_order(&"BTC_USDT".to_string(), &"buy".to_string(), &"limit".to_string(), &"20000".to_string(), &"0.0001".to_string()).await;
|
|
|
- println!("test_get_real_symbol:{:?}", order_id);
|
|
|
+ println!("test_place_okx_order:{:?}", order_id);
|
|
|
}
|
|
|
|
|
|
// 测试okx查询订单
|
|
|
#[tokio::test]
|
|
|
async fn test_get_okx_order() {
|
|
|
is_proxy();
|
|
|
- let exchange = Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string());
|
|
|
+ let exchange = new_exchange();
|
|
|
let order = exchange.get_okx_order(&"BTC_USDT".to_string(), &"612034971737800726".to_string()).await;
|
|
|
- println!("test_get_real_symbol:{:?}", order);
|
|
|
+ println!("test_get_okx_order:{:?}", order);
|
|
|
}
|
|
|
|
|
|
// 测试okx撤单
|
|
|
#[tokio::test]
|
|
|
async fn test_cancel_okx_order() {
|
|
|
is_proxy();
|
|
|
- let exchange = Exchange::new("a4cf4f54-f4d3-447d-a57c-166fd1ead2e0".to_string(), "556DAB6773CA26DDAAA114F7044138CA".to_string(), "rust_Test123".to_string());
|
|
|
+ let exchange = new_exchange();
|
|
|
let is_success = exchange.cancel_okx_order(&"BTC_USDT".to_string(), &"612034971737800726".to_string()).await;
|
|
|
- println!("test_get_real_symbol:{:?}", is_success);
|
|
|
+ println!("test_cancel_okx_order:{:?}", is_success);
|
|
|
}
|
|
|
}
|