| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- use std::env;
- use std::io::{BufRead, Error, ErrorKind};
- use std::future::Future;
- use std::sync::Arc;
- use serde_json::json;
- use tokio::sync::Mutex;
- use crate::{Bot};
- use crate::exchange_libs::{BinanceExc, OkxExc, ResponseData, SocketTool};
- // 深度结构体
- #[derive(Debug)]
- pub struct Depth {
- // 卖单数组
- pub asks: Vec<DepthItem>,
- // 买单数组
- pub bids: Vec<DepthItem>,
- }
- #[derive(Debug)]
- pub struct DepthItem {
- // 价格
- pub price: f64,
- // 数量
- pub amount: f64,
- }
- // k线数据结构体
- #[derive(Debug)]
- pub struct Record {
- // 时间
- pub time: i64,
- // 开盘价
- pub open: f64,
- // 最高价
- pub high: f64,
- // 最低价
- pub low: f64,
- // 收盘价
- pub close: f64,
- // 交易量
- pub volume: f64,
- }
- // Account信息结构体
- #[derive(Debug)]
- pub struct Account {
- // 可用计价币数量
- pub balance: f64,
- // Balance挂单的冻结数量
- pub frozen_balance: f64,
- // 可用交易币数量
- pub stocks: f64,
- // stocks挂单的冻结数量
- pub frozen_stocks: f64,
- }
- #[derive(Debug)]
- pub struct Order {
- // 交易单唯一标识
- pub id: String,
- // 下单价格
- pub price: f64,
- // 下单数量
- pub amount: f64,
- // 成交数量
- pub deal_amount: f64,
- // 成交均价
- pub avg_price: f64,
- // 订单状态
- pub status: String,
- // 订单类型
- pub order_type: String,
- }
- #[derive(Debug)]
- pub struct Market {
- symbol: String,
- base_asset: String,
- quote_asset: String,
- tick_size: f64,
- amount_size: f64,
- price_precision: f64,
- amount_precision: f64,
- min_qty: f64,
- max_qty: f64,
- min_notional: f64,
- max_notional: f64,
- ct_val: f64,
- }
- pub struct Exchange {
- // okx okx_access_key
- okx_access_key: String,
- // okx okx_secret_key
- okx_secret_key: String,
- // okx okx_passphrase
- okx_passphrase: String,
- // binance BinanceExc请求方法结构体
- binance_exc: BinanceExc,
- // okx OkxExc请求方法结构体
- okx_exc: OkxExc,
- // binance Binance,
- }
- impl Exchange {
- // new Exchange结构体
- // okx_access_key: okx_access_key
- // okx_secret_key: okx_secret_key
- // okx_passphrase: okx_passphrase
- pub fn new(okx_access_key: String, okx_secret_key: String, okx_passphrase: String) -> Exchange {
- let binance_exc = BinanceExc::new("".to_string(), "".to_string());
- let okx_exc = OkxExc::new(okx_access_key.clone(), okx_secret_key.clone(), okx_passphrase.clone());
- Exchange { okx_access_key, okx_secret_key, okx_passphrase, binance_exc, okx_exc }
- }
- // 处理交易对格式
- // symbol: 交易币对, "BTC_USDT"
- // str: 替换字符串, "-"
- pub fn get_real_symbol(&self, symbol: &String, str: String) -> String {
- return symbol.to_uppercase().replace("_", &*str);
- }
- // 获取币安深度信息
- // symbol: 交易币对, "BTC_USDT"
- // limit: 返回条数, 最大 5000. 可选值:[5, 10, 20, 50, 100, 500, 1000, 5000]
- pub async fn subscribe_binance_depth(&self, symbol: &String, limit: i32, mut bot_arc: Arc<Mutex<Bot>>) {
- let real_symbol = self.get_real_symbol(symbol, "".to_string());
- let get_res_data = move |res_data: ResponseData| {
- let bot_arc_clone = Arc::clone(&bot_arc);
- async move {
- if res_data.code == "0" {
- let res_data_str = res_data.data;
- let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
- let depth_asks: Vec<DepthItem> = parse_depth_items(&res_data_json["asks"]);
- let depth_bids: Vec<DepthItem> = parse_depth_items(&res_data_json["bids"]);
- let result = Depth {
- asks: depth_asks,
- bids: depth_bids,
- };
- {
- let mut bot = bot_arc_clone.lock().await;
- bot.depth_handler(result)
- }
- } else {
- panic!("get_binance_depth: {}", res_data.message);
- }
- }
- };
- SocketTool::binance_run_depth(vec![&real_symbol], limit.to_string(), get_res_data)
- }
- // 获取币安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) -> Result<Vec<Record>, Error> {
- let real_symbol = self.get_real_symbol(symbol, "".to_string());
- let res_data = self.binance_exc.binance_k(&real_symbol, interval, &limit.to_string()).await;
- if res_data.code == "0" {
- let res_data_str = res_data.data;
- let res_data_json: Vec<Vec<serde_json::Value>> = serde_json::from_str(&*res_data_str).unwrap();
- let mut result: Vec<Record> = vec![];
- for item in res_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(Error::new(ErrorKind::Other, res_data.message))
- }
- }
- // 获取okx账户信息数据
- // symbol: 交易币对, "BTC_USDT"
- pub async fn get_okx_account(&self, symbol: &String) -> Result<Account, Error> {
- let real_symbol = self.get_real_symbol(symbol, ",".to_string());
- let res_data = self.okx_exc.okx_acc(&real_symbol).await;
- if res_data.code == "0" {
- let res_data_str = res_data.data;
- let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
- let symbol_array: Vec<&str> = symbol.split("_").collect();
- let details = res_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: balance_info["availBal"].as_str().unwrap().parse().unwrap_or(0.0),
- frozen_balance: balance_info["fixedBal"].as_str().unwrap().parse().unwrap_or(0.0),
- stocks: stocks_info["availBal"].as_str().unwrap().parse().unwrap_or(0.0),
- frozen_stocks: stocks_info["fixedBal"].as_str().unwrap().parse().unwrap_or(0.0),
- };
- Ok(result)
- } else {
- Err(Error::new(ErrorKind::Other, res_data.message))
- }
- }
- // OKX下单
- // symbol: 交易币对, "BTC_USDT"
- // side: 订单方向, buy:买,sell:卖
- // 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) -> Result<String, Error> {
- let real_symbol = self.get_real_symbol(symbol, "-".to_string());
- let res_data = self.okx_exc.okx_order(&real_symbol, "cash", side, order_type, price, amount).await;
- if res_data.code == "0" {
- let res_data_str = res_data.data;
- let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
- let result = res_data_json[0]["ordId"].as_str().unwrap().parse().unwrap();
- Ok(result)
- } else {
- Err(Error::new(ErrorKind::Other, res_data.message))
- }
- }
- // OKX查询订单
- // symbol: 交易币对, "BTC_USDT"
- // order_id: 订单ID, "590910403358593111"
- pub async fn get_okx_order(&self, symbol: &String, order_id: &String) -> Result<Order, Error> {
- let real_symbol = self.get_real_symbol(symbol, "-".to_string());
- let res_data = self.okx_exc.okx_get_order(&real_symbol, order_id).await;
- if res_data.code == "0" {
- let res_data_str = res_data.data;
- let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
- let result = parse_order_info(res_data_json[0].clone());
- Ok(result)
- } else {
- Err(Error::new(ErrorKind::Other, res_data.message))
- }
- }
- // OKX订阅订单
- // symbol: 交易币对, "BTC_USDT"
- pub async fn subscribe_okx_order(&self, symbol: &String, mut bot_arc: Arc<Mutex<Bot>>) {
- let real_symbol = self.get_real_symbol(symbol, "-".to_string());
- let get_res_data = move |res_data: ResponseData| {
- let bot_arc_clone = Arc::clone(&bot_arc);
- async move {
- if res_data.code == "0" {
- let res_data_str = res_data.data;
- let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
- let order_info_arr: serde_json::Value = res_data_json["data"].clone();
- let result = parse_order_info(order_info_arr[0].clone());
- print!("{:?}", result);
- {
- let mut bot = bot_arc_clone.lock().await;
- bot.order_change_response(result).await;
- }
- } else {
- panic!("subscribe_okx_order: {}", res_data.message);
- }
- }
- };
- let okx_access_key = env::var("okx_access_key").unwrap();
- let okx_secret_key = env::var("okx_secret_key").unwrap();
- let okx_passphrase = env::var("okx_passphrase").unwrap();
- SocketTool::okx_pr_run_orders(vec![&real_symbol], okx_access_key, okx_secret_key, okx_passphrase, get_res_data);
- }
- // OKX撤销订单
- // symbol: 交易币对, "BTC_USDT"
- // order_id: 订单ID, "590910403358593111"
- pub async fn cancel_okx_order(&self, symbol: &String, order_id: &String) -> Result<bool, Error> {
- let real_symbol = self.get_real_symbol(symbol, "-".to_string());
- let res_data = self.okx_exc.okx_revocation_order(&real_symbol, order_id).await;
- if res_data.code == "0" {
- let res_data_str = res_data.data;
- let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
- let order_info = res_data_json[0]["sCode"].as_str().unwrap();
- let result = if order_info == "0" { true } else { false };
- Ok(result)
- } else {
- Err(Error::new(ErrorKind::Other, res_data.message))
- }
- }
- // OKX撤销订单
- // symbol: 交易币对, "BTC_USDT"
- // order_id: 订单ID, "590910403358593111"
- pub async fn get_okx_instruments(&self, symbol: &String) {
- // let real_symbol = self.get_real_symbol(symbol, "-".to_string());
- // let mut btree_map: BTreeMap<&str, &str> = BTreeMap::new();
- // btree_map.insert("instType", "SPOT");
- // let result = self.okx_exc.get_v("/api/v5/public/instruments".to_string(), btree_map).await;
- // match result {
- // Ok(res_data) => {
- // let symbol_array: Vec<&str> = symbol.split("_").collect();
- // let res_data_str = res_data.data;
- // let res_data_json: serde_json::Value = serde_json::from_str(&*res_data_str).unwrap();
- // let order_info = res_data_json["data"].as_array().unwrap();
- // let info = order_info.iter().find(|item| item["baseCcy"].as_str().unwrap() == symbol_array[0] && item["quoteCcy"].as_str().unwrap() == symbol_array[1]).unwrap();
- // println!("\n\n{:?}", info);
- //
- // let min_qty = info["minSz"].as_str().unwrap_or("0").parse().unwrap_or(0.0);
- // let amount_size = info["lotSz"].as_str().unwrap_or("0").parse().unwrap_or(0.0);
- // let result = Market {
- // symbol: info["instId"].as_str().unwrap().parse().unwrap(),
- // base_asset: info["baseCcy"].as_str().unwrap().parse().unwrap(),
- // quote_asset: info["quoteCcy"].as_str().unwrap().parse().unwrap(),
- // tick_size: info["tickSz"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
- // amount_size,
- // price_precision: info["tickSz"].as_str().unwrap().parse().to_string().split(".").collect()[1],
- // amount_precision:info["lotSz"].as_str().unwrap().parse().to_string().split(".").collect()[1],
- // min_qty,
- // max_qty: info["minSz"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
- // min_notional: amount_size * min_qty,
- // max_notional: 0.01,
- // ct_val: info["ctVal"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
- // };
- // println!("\n\n{:?}", result);
- // // let order_info = res_data_json;
- // }
- // Err(err) => {}
- // }
- // let real_symbol = self.get_real_symbol(symbol, "-".to_string());
- // let res_data = self.okx_exc.get_v("/api/v5/public/instruments", order_id).await;
- // if res_data.code == "0" {
- // let res_data_str = res_data.data;
- // let res_data_json: Vec<serde_json::Value> = serde_json::from_str(&*res_data_str).unwrap();
- // let order_info = res_data_json[0]["sCode"].as_str().unwrap();
- // let result = if order_info == "0" { true } else { false };
- // Ok(result)
- // } else {
- // Err(Error::new(ErrorKind::Other, res_data.message))
- // }
- }
- }
- fn parse_order_info(res_data_json: serde_json::Value) -> Order {
- let order = Order {
- id: res_data_json["ordId"].as_str().unwrap().parse().unwrap(),
- price: res_data_json["px"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
- amount: res_data_json["sz"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
- deal_amount: res_data_json["accFillSz"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
- avg_price: res_data_json["avgPx"].as_str().unwrap_or("0").parse().unwrap_or(0.0),
- status: res_data_json["state"].as_str().unwrap().parse().unwrap(),
- order_type: res_data_json["instType"].as_str().unwrap().parse().unwrap(),
- };
- return order;
- }
- // 深度信息买单/卖单处理
- 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)]
- mod tests {
- use std::env;
- use std::io::{self, Write};
- use crate::exchange_middle_ware::{Exchange};
- use crate::exchange_libs::{ResponseData, SocketTool, http_enable_proxy};
- // new Exchange
- fn new_exchange() -> Exchange {
- let okx_access_key = env::var("okx_access_key").unwrap();
- let okx_secret_key = env::var("okx_secret_key").unwrap();
- let okx_passphrase = env::var("okx_passphrase").unwrap();
- Exchange::new(okx_access_key, okx_secret_key, okx_passphrase)
- }
- // 测试new Exchange
- #[tokio::test]
- async fn test_new_exchange() {
- http_enable_proxy();
- 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);
- }
- // 测试交易对处理
- #[tokio::test]
- async fn test_get_real_symbol() {
- http_enable_proxy();
- let exchange = new_exchange();
- let real_symbol = exchange.get_real_symbol(&"BTC_USDT".to_string(), "".to_string());
- println!("test_get_real_symbol:{}", real_symbol);
- }
- // 测试binance获取深度信息
- #[tokio::test]
- async fn test_get_binance_depth() {
- let exchange = new_exchange();
- let get_res_data = move |res_data: ResponseData| {
- async move {
- let mut stdout = io::stdout();
- writeln!(stdout, "test_get_binance_depth: {:?}", res_data).expect("TODO: panic message");
- }
- };
- SocketTool::binance_run_depth(vec![&"BTCUSDT"], "10".to_string(), get_res_data)
- }
- // 测试binance获取k线
- #[tokio::test]
- async fn test_get_binance_klines() {
- http_enable_proxy();
- 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() {
- http_enable_proxy();
- let exchange = new_exchange();
- let account = exchange.get_okx_account(&"BTC_USDT".to_string()).await;
- println!("test_get_okx_account:{:?}", account);
- }
- // 测试okx下订单
- #[tokio::test]
- async fn test_place_okx_order() {
- http_enable_proxy();
- 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_place_okx_order:{:?}", order_id);
- }
- // 测试okx查询订单
- #[tokio::test]
- async fn test_get_okx_order() {
- http_enable_proxy();
- let exchange = new_exchange();
- let get_res_data = move |res_data: ResponseData| {
- writeln!(io::stdout(), "Current number: {:?}", res_data).expect("TODO: panic message");
- async move {}
- };
- SocketTool::okx_pr_run_orders(vec![&"BTC-USDT"], exchange.okx_access_key, exchange.okx_secret_key, exchange.okx_passphrase, get_res_data);
- }
- // 测试okx撤单
- #[tokio::test]
- async fn test_cancel_okx_order() {
- http_enable_proxy();
- let exchange = new_exchange();
- let is_success = exchange.cancel_okx_order(&"BTC_USDT".to_string(), &"612034971737800726".to_string()).await;
- println!("test_cancel_okx_order:{:?}", is_success);
- }
- // 测试okx撤单
- #[tokio::test]
- async fn test_get_okx_instruments() {
- http_enable_proxy();
- let exchange = new_exchange();
- let is_success = exchange.get_okx_instruments(&"BTC_USDT".to_string()).await;
- println!("test_cancel_okx_order:{:?}", is_success);
- }
- }
|