JiahengHe преди 2 години
родител
ревизия
d310607063
променени са 7 файла, в които са добавени 604 реда и са изтрити 180 реда
  1. 80 95
      Cargo.lock
  2. 15 1
      Cargo.toml
  3. 3 1
      README.md
  4. 360 22
      src/exchange_libs.rs
  5. 26 26
      src/exchange_middle_ware.rs
  6. 51 32
      src/main.rs
  7. 69 3
      tests/binance_ws_test.rs

+ 80 - 95
Cargo.lock

@@ -64,10 +64,12 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
 name = "as-market-binance-okx-spot"
 version = "0.1.0"
 dependencies = [
- "binance",
+ "base64 0.13.1",
  "chrono",
+ "hex",
  "ndarray",
  "reqwest",
+ "ring",
  "rust_decimal",
  "rust_decimal_macros",
  "serde",
@@ -75,6 +77,8 @@ dependencies = [
  "serde_json",
  "time 0.2.27",
  "tokio",
+ "tungstenite",
+ "url",
 ]
 
 [[package]]
@@ -116,22 +120,6 @@ version = "0.21.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
 
-[[package]]
-name = "binance"
-version = "0.20.2"
-source = "git+https://github.com/wisespace-io/binance-rs.git#0a7128d812b05953ee9564f04b76dd14df079b28"
-dependencies = [
- "error-chain",
- "hex",
- "hmac",
- "reqwest",
- "serde",
- "serde_json",
- "sha2",
- "tungstenite",
- "url",
-]
-
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -158,9 +146,9 @@ dependencies = [
 
 [[package]]
 name = "block-buffer"
-version = "0.10.4"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
 dependencies = [
  "generic-array",
 ]
@@ -311,25 +299,13 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "crypto-common"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
 [[package]]
 name = "digest"
-version = "0.10.7"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
 dependencies = [
- "block-buffer",
- "crypto-common",
- "subtle",
+ "generic-array",
 ]
 
 [[package]]
@@ -368,15 +344,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "error-chain"
-version = "0.12.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
-dependencies = [
- "version_check",
-]
-
 [[package]]
 name = "fastrand"
 version = "2.0.0"
@@ -434,12 +401,6 @@ version = "0.3.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
 
-[[package]]
-name = "futures-io"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
-
 [[package]]
 name = "futures-sink"
 version = "0.3.28"
@@ -459,12 +420,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
 dependencies = [
  "futures-core",
- "futures-io",
  "futures-task",
- "memchr",
  "pin-project-lite",
  "pin-utils",
- "slab",
 ]
 
 [[package]]
@@ -543,15 +501,6 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest",
-]
-
 [[package]]
 name = "http"
 version = "0.2.9"
@@ -666,6 +615,15 @@ dependencies = [
  "hashbrown 0.12.3",
 ]
 
+[[package]]
+name = "input_buffer"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413"
+dependencies = [
+ "bytes",
+]
+
 [[package]]
 name = "ipnet"
 version = "2.8.0"
@@ -847,6 +805,12 @@ version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
 [[package]]
 name = "openssl"
 version = "0.10.56"
@@ -1094,6 +1058,21 @@ dependencies = [
  "winreg",
 ]
 
+[[package]]
+name = "ring"
+version = "0.17.0-alpha.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4575a179070909595bea5f999d67934737c2e0757a1eb9839af555917817b257"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
 [[package]]
 name = "rkyv"
 version = "0.7.42"
@@ -1247,9 +1226,6 @@ name = "serde"
 version = "1.0.183"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
-dependencies = [
- "serde_derive",
-]
 
 [[package]]
 name = "serde_derive"
@@ -1286,23 +1262,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "sha1"
-version = "0.6.1"
+name = "sha-1"
+version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
+checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
 dependencies = [
- "sha1_smol",
+ "block-buffer",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
 ]
 
 [[package]]
 name = "sha1"
-version = "0.10.5"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
 dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
+ "sha1_smol",
 ]
 
 [[package]]
@@ -1311,17 +1289,6 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
 
-[[package]]
-name = "sha2"
-version = "0.10.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
 [[package]]
 name = "signal-hook-registry"
 version = "1.4.1"
@@ -1372,6 +1339,23 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "socks"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
+dependencies = [
+ "byteorder",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
 [[package]]
 name = "standback"
 version = "0.2.17"
@@ -1420,7 +1404,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "sha1 0.6.1",
+ "sha1",
  "syn 1.0.109",
 ]
 
@@ -1430,12 +1414,6 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
 
-[[package]]
-name = "subtle"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
-
 [[package]]
 name = "syn"
 version = "1.0.109"
@@ -1658,19 +1636,20 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
 
 [[package]]
 name = "tungstenite"
-version = "0.18.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788"
+version = "0.13.0"
+source = "git+https://github.com/PrivateRookie/tungstenite-rs.git?rev=1d9289276518e5ab7e5194126d40b441d8938375#1d9289276518e5ab7e5194126d40b441d8938375"
 dependencies = [
  "base64 0.13.1",
  "byteorder",
  "bytes",
  "http",
  "httparse",
+ "input_buffer",
  "log",
  "native-tls",
  "rand",
- "sha1 0.10.5",
+ "sha-1",
+ "socks",
  "thiserror",
  "url",
  "utf-8",
@@ -1703,6 +1682,12 @@ dependencies = [
  "tinyvec",
 ]
 
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
 [[package]]
 name = "url"
 version = "2.4.0"

+ 15 - 1
Cargo.toml

@@ -8,12 +8,26 @@ edition = "2021"
 [dependencies]
 rust_decimal = "1.31.0"
 rust_decimal_macros = "1.31.0"
-reqwest = "0.11"
+reqwest = { version = "0.11.14", features = ["json"] }
 chrono = "0.4.26"
 serde = "1.0.183"
 serde_derive = "1.0.183"
 serde_json = "1.0.104"
 ndarray = "0.15"
 time = "0.2"
+<<<<<<< HEAD
 binance = { git = "https://github.com/wisespace-io/binance-rs.git" }
 tokio = {version = "1.31.0", features = ["full"] }
+=======
+tokio = { version = "1.31.0", features = ["full"] }
+
+tungstenite = { git = "https://github.com/PrivateRookie/tungstenite-rs.git", rev = "1d9289276518e5ab7e5194126d40b441d8938375" }
+url = "2.4.0"
+base64 = "0.13"
+#rust-crypto = "0.2.36"
+
+#hmac = "0.8.0"
+#sha2 = "0.9"
+hex = "0.4"
+ring = "0.17.0-alpha.11"
+>>>>>>> 0fbf9435eeade0a1837b91e7976f703dcaad7604

+ 3 - 1
README.md

@@ -14,4 +14,6 @@
 ### 2. 运行方式
     clone到本地/服务器之后,直接编译即可运行(需要配置)。
 ### 3. 遇到的问题
-    各种类型不熟悉问题吧,总算是都克服了。
+* 各种类型不熟悉问题吧,总算是都克服了。
+* 如何在本地使用rust进行websocket连接binance和okx(最后总算解决了)
+    

+ 360 - 22
src/exchange_libs.rs

@@ -1,10 +1,13 @@
+use std::collections::{BTreeMap, HashMap};
 use std::env;
 use reqwest;
-
+use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
+use hex;
+use ring::hmac;
 
 
 /*币安-k线条*/
-pub async fn binan_k(symbol: &String, interval: &String, limit: &i32) -> String {
+pub async fn binan_k(symbol: &str, interval: &str, limit: &i32) -> String {
     let base_url = "https://api.binance.com/api/v3/klines?symbol=".to_string() + &symbol + "&interval=" + &interval + "&limit=" + &limit.to_string();
     let req = get(base_url.to_string());
     match req.await {
@@ -20,10 +23,6 @@ pub async fn binan_k(symbol: &String, interval: &String, limit: &i32) -> String
 }
 
 
-
-
-
-
 /*普通 Get 请求*/
 pub async fn get(url: String) -> Result<(String), reqwest::Error> {
     let mut conent = String::from("");
@@ -43,26 +42,365 @@ pub async fn get(url: String) -> Result<(String), reqwest::Error> {
     Ok((conent))
 }
 
+
+//map数据转 get请求参数
+fn parse_params_to_str(parameters: BTreeMap<&str, &str>) -> String {
+    parameters
+        .into_iter()
+        .map(|(key, value)| format!("{}={}", key, value))
+        .collect::<Vec<String>>()
+        .join("&")
+}
+
+//获取时时间
+fn get_timestamp() -> String {
+    chrono::Utc::now()
+        .format("%Y-%m-%dT%H:%M:%S%.3fZ")
+        .to_string()
+}
+
+
 //代理
 pub fn is_proxy() -> bool {
-    //获取命令行参数
-    let args: Vec<String> = env::args().collect();
-    // 打印程序名称
-    println!("启动参数名称 name: {}", args[0]);
-    // 打印传递的参数
-    if args.len() > 1 {
-        println!("参数:");
-        for arg in &args[1..] {
-            println!("{}", arg);
+
+    //拿到本机环境变量,
+    let env_var_name = "http_proxy";
+    // 使用std::env::var函数获取环境变量的值,如果返回Err,则说明环境变量不存在
+    match env::var(env_var_name) {
+        Ok(value) => {
+            println!("Environment variable {} exists with value: {}", env_var_name, value);
+            env::set_var("http_proxy", "http://127.0.0.1:7890");
+            env::set_var("https_proxy", "http://127.0.0.1:7890");
+            println!("代理设置成功");
+            true
         }
-        env::set_var("http_proxy", "http://127.0.0.1:7890");
-        env::set_var("https_proxy", "http://127.0.0.1:7890");
-        println!("代理设置成功");
-        true
-    } else {
-        println!("没有提供参数.");
-        false
+        Err(_) => {
+            println!("Environment variable {} does not exist.", env_var_name);
+            false
+        }
+    }
+}
+
+
+pub struct OkxExc {
+    base_url: String,
+    access_keu: String,
+    secret_key: String,
+    passphrase: String,
+}
+
+impl OkxExc {
+    pub fn new(access_keu: String, secret_key: String, passphrase: String) -> OkxExc {
+        OkxExc { base_url: "https://www.okx.com".to_string(), access_keu, secret_key, passphrase }
+    }
+
+    //获取订单信息
+    pub async fn okx_get_order(&self, inst_id: &str, ord_id: &str) -> ReqData {
+        let mut btree_map: BTreeMap<&str, &str> = BTreeMap::new();
+        btree_map.insert("instId", inst_id);//产品Id
+        btree_map.insert("ordId", ord_id);//顶顶那
+
+        let result = self.get_v(
+            "/api/v5/trade/order".to_string(),
+            btree_map,
+        ).await;
+
+        match result {
+            Ok(reqData) => {
+                if (reqData.code != "0") {
+                    reqData
+                } else {
+                    let body: String = reqData.data;
+                    let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
+                    let code = json_value["code"].to_string();
+                    let data = json_value["data"].to_string();
+                    let msg = json_value["msg"].to_string();
+
+                    let success = ReqData::new(code.parse().unwrap(),
+                                               msg.parse().unwrap(),
+                                               data.parse().unwrap());
+                    success
+                }
+            }
+            Err(err) => {
+                let error = ReqData::error(format!("json 解析失败:{}", err));
+                error
+            }
+        }
+    }
+
+    //撤单接口
+    pub async fn okx_revocation_order(&self, inst_id: &str, ord_id: &str) -> ReqData {
+        let mut btree_map: BTreeMap<&str, &str> = BTreeMap::new();
+        btree_map.insert("instId", inst_id);//产品Id
+        btree_map.insert("ordId", ord_id);//顶顶那
+
+        let result = self.post_v(
+            "/api/v5/trade/cancel-order".to_string(),
+            btree_map,
+        ).await;
+
+        match result {
+            Ok(reqData) => {
+                if (reqData.code != "0") {
+                    reqData
+                } else {
+                    let body: String = reqData.data;
+                    let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
+                    let code = json_value["code"].to_string();
+                    let data = json_value["data"].to_string();
+                    let msg = json_value["msg"].to_string();
+
+                    let success = ReqData::new(code.parse().unwrap(),
+                                               msg.parse().unwrap(),
+                                               data.parse().unwrap());
+                    success
+                }
+            }
+            Err(err) => {
+                let error = ReqData::error(format!("json 解析失败:{}", err));
+                error
+            }
+        }
+    }
+
+    //下单接口
+    pub async fn okx_order(&self, inst_id: &str, td_mode: &str, side: &str, ord_type: &str, px: &str, sz: &str) -> ReqData {
+        let mut btree_map: BTreeMap<&str, &str> = BTreeMap::new();
+        btree_map.insert("instId", inst_id);//产品Id
+        btree_map.insert("tdMode", td_mode);//交易模式
+        btree_map.insert("side", side);//订单方向
+        btree_map.insert("ordType", ord_type);//订单类
+        btree_map.insert("px", px);//委托价格
+        btree_map.insert("sz", sz);//委托数量
+
+        let result = self.post_v(
+            "/api/v5/trade/order".to_string(),
+            btree_map,
+        ).await;
+
+        match result {
+            Ok(reqData) => {
+                if (reqData.code != "0") {
+                    reqData
+                } else {
+                    let body: String = reqData.data;
+                    let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
+                    let code = json_value["code"].to_string();
+                    let data = json_value["data"].to_string();
+                    let msg = json_value["msg"].to_string();
+
+                    let success = ReqData::new(code.parse().unwrap(),
+                                               msg.parse().unwrap(),
+                                               data.parse().unwrap());
+                    success
+                }
+            }
+            Err(err) => {
+                let error = ReqData::error(format!("json 解析失败:{}", err));
+                error
+            }
+        }
+    }
+
+    //账户信息
+    pub async fn okx_acc(&self, ccy: &str) -> ReqData {
+        let mut btree_map: BTreeMap<&str, &str> = BTreeMap::new();
+        btree_map.insert("ccy", ccy);
+
+        let result = self.get_v(
+            "/api/v5/account/balance".to_string(),
+            btree_map,
+        ).await;
+        match result {
+            Ok(reqData) => {
+                if (reqData.code != "0") {
+                    reqData
+                } else {
+                    let body: String = reqData.data;
+                    let json_value: serde_json::Value = serde_json::from_str(&body).unwrap();
+                    let code = json_value["code"].to_string();
+                    let data = json_value["data"].to_string();
+                    let msg = json_value["msg"].to_string();
+
+                    // println!("--解析成功----code:{}",code);
+                    // println!("--解析成功----data:{}",data);
+                    // println!("--解析成功----msg:{}",msg);
+                    let success = ReqData::new(code.parse().unwrap(),
+                                               msg.parse().unwrap(),
+                                               data.parse().unwrap());
+                    success
+                }
+            }
+            Err(err) => {
+                let error = ReqData::error(format!("json 解析失败:{}", err));
+                error
+            }
+        }
+    }
+
+    //带认证-get
+    async fn get_v(&self, request_path: String, params: BTreeMap<&str, &str>) -> Result<(ReqData), reqwest::Error> {
+        let mut req_data: ReqData;
+
+        /*请求接口与 地址*/
+        let base_url = self.base_url.to_string();
+
+        /*账号 密钥 密码*/
+        let access_keu = self.access_keu.to_string();
+        let secret_key = self.secret_key.to_string();
+        let passphrase = self.passphrase.to_string();
+
+
+        /*签名生成*/
+        let timestamp = get_timestamp();
+        let mut params_str = parse_params_to_str(params);
+        let get_url_params = format!("{}?{}", request_path, params_str);    //接口与参数组装
+
+        // 时间戳 + 请求类型+ 请求参数字符串
+        let message = format!("{}GET{}", timestamp, get_url_params);
+        println!("---message:{:?}", message);
+        let mut sign = self.okx_sign(secret_key, message);
+
+        //添加请求头
+        let mut headers = self.okx_create_header(&access_keu, &passphrase, &sign, &timestamp);
+
+        let client = reqwest::Client::new();
+        let req = client.get(base_url + &get_url_params)
+            .headers(headers);
+        println!("--请求头:{:?}", req);
+
+        //拿到返回
+        let response = req.send()
+            .await?;
+        // 检查响应是否成功
+        println!("---状态:{:?},{}", response.status(), response.status().is_success());
+        if response.status().is_success() {
+            // 读取响应的内容
+            let body = response.text().await?;
+            println!("okx_acc-Response body:\n{}", body);
+            req_data = ReqData::new("0".to_string(), "success".to_string(), body);
+        } else {
+            let body = response.text().await?;
+            println!("okx_acc-Request failed with status: {}", body);
+            req_data = ReqData::error(body.to_string())
+        }
+        Ok((req_data))
+    }
+
+    //带认证-post
+    async fn post_v(&self, request_path: String, params: BTreeMap<&str, &str>) -> Result<(ReqData), reqwest::Error> {
+        let mut req_data: ReqData;
+
+        /*请求接口与 地址*/
+        let base_url = self.base_url.to_string();
+
+        /*账号 密钥 密码*/
+        let access_keu = self.access_keu.to_string();
+        let secret_key = self.secret_key.to_string();
+        let passphrase = self.passphrase.to_string();
+
+
+        /*签名生成*/
+        let timestamp = get_timestamp();
+        let params_str = serde_json::to_string(&params).unwrap();
+
+        // let params_json = serde_json::to_value(params_str.clone()).unwrap();
+
+        println!("---params:{:?}", params);
+        println!("---params-json_str:{ }", params_str.clone());
+        // println!("---params-json:{:?}", params_json);
+
+        // 时间戳 + 请求类型+ 请求参数字符串
+        let message = format!("{}POST{}{}", timestamp, request_path, &params_str);
+        println!("---message:{:?}", message);
+        let mut sign = self.okx_sign(secret_key, message);
+
+        //添加请求头
+        let mut headers = self.okx_create_header(&access_keu, &passphrase, &sign, &timestamp);
+
+        let client = reqwest::Client::new();
+        let url = format!("{}{}", base_url, request_path);
+        let req = client
+            .post(url)
+            .headers(headers)
+            .json(&params)
+            ;
+
+        let response = req.send()
+            .await?;
+        // 检查响应是否成功
+        println!("---状态:{:?},{}", response.status(), response.status().is_success());
+        if response.status().is_success() {
+            // 读取响应的内容
+            let body = response.text().await?;
+            println!("okx_order-Response body:\n{}", body);
+            req_data = ReqData::new("0".to_string(), "success".to_string(), body);
+        } else {
+            let body = response.text().await?;
+            println!("okx_order-Request failed with status: {}", body);
+            req_data = ReqData::error(body.to_string())
+        }
+        Ok((req_data))
+    }
+
+
+    //okx 签名生成
+    fn okx_sign(&self, secret_key: String, message: String) -> String {
+        // 做签名
+        let hmac_key = ring::hmac::Key::new(hmac::HMAC_SHA256, secret_key.as_bytes());
+        let result = ring::hmac::sign(&hmac_key, &message.as_bytes());
+        base64::encode(result)
     }
+
+    fn okx_create_header(&self, api_key: &str, passphrase: &str, sign: &str, timestamp: &str) -> HeaderMap {
+        // 处理请求头 headers
+
+        let mut header_map = HeaderMap::new();
+
+        header_map.insert(
+            "OK-ACCESS-KEY",
+            HeaderValue::from_str(&api_key).unwrap(),
+        );
+        header_map.insert(
+            "OK-ACCESS-SIGN",
+            HeaderValue::from_str(&sign).unwrap());
+        header_map.insert(
+            "OK-ACCESS-TIMESTAMP",
+            HeaderValue::from_str(&timestamp).unwrap(),
+        );
+        header_map.insert(
+            "OK-ACCESS-PASSPHRASE",
+            HeaderValue::from_str(&passphrase).unwrap(),
+        );
+        header_map.insert(
+            "CONTENT-TYPE",
+            HeaderValue::from_static("application/json; charset=UTF-8"),
+        );
+        // header_map.insert(
+        //     reqwest::header::CONTENT_TYPE,
+        //     HeaderValue::from_static("application/json; charset=UTF-8"),
+        // );
+        header_map
+    }
+}
+
+
+//统一返回
+#[derive(Debug)]
+pub struct ReqData {
+    pub code: String,
+    pub message: String,
+    pub data: String,
 }
 
+impl ReqData {
+    pub fn new(code: String, message: String, data: String) -> ReqData {
+        // original_string.replace("world", "Rust");
+        ReqData { code, message, data }
+    }
+    pub fn error(message: String) -> ReqData {
+        ReqData { code: "-1".to_string(), message: "请求失败:".to_string() + &message, data: "".to_string() }
+    }
+}
 

+ 26 - 26
src/exchange_middle_ware.rs

@@ -8,48 +8,48 @@ use crate::exchange_libs;
 // 深度结构体
 #[derive(Debug)]
 pub struct Depth {
-    pub(crate) asks: Vec<DepthItem>,
-    pub(crate) bids: Vec<DepthItem>,
+    pub asks: Vec<DepthItem>,
+    pub bids: Vec<DepthItem>,
 }
 #[derive(Debug)]
 pub struct DepthItem {
-    pub(crate) price: f64,
-    pub(crate) amount: f64,
+    pub price: f64,
+    pub amount: f64,
 }
 
 // k线数据结构体
 #[derive(Debug)]
 pub struct Record {
-    pub(crate) time: i64,      // 时间
-    pub(crate) open: f64,      // 开盘价
-    pub(crate) high: f64,      // 最高价
-    pub(crate) low: f64,       // 最低价
-    pub(crate) close: f64,     // 收盘价
-    pub(crate) volume: f64,    // 交易量
+    pub time: i64,      // 时间
+    pub open: f64,      // 开盘价
+    pub high: f64,      // 最高价
+    pub low: f64,       // 最低价
+    pub close: f64,     // 收盘价
+    pub volume: f64,    // 交易量
 }
 
 // Account信息结构体
 pub struct Account{
-    pub(crate) balance: f64,           // 可用计价币数量
-    pub(crate) frozen_balance: f64,    // Balance挂单的冻结数量
-    pub(crate) stocks: f64,            // 可用交易币数量
-    pub(crate) frozen_stocks: f64,     // stocks挂单的冻结数量
+    pub balance: f64,           // 可用计价币数量
+    pub frozen_balance: f64,    // Balance挂单的冻结数量
+    pub stocks: f64,            // 可用交易币数量
+    pub frozen_stocks: f64,     // stocks挂单的冻结数量
 }
 
 // 币对市场信息结构体
 pub struct Market {
-    pub(crate) symbol: String,
-    pub(crate) base_asset: String,
-    pub(crate) quote_asset: String,
-    pub(crate) tick_size: f64,
-    pub(crate) amount_size: f64,       // 下单量最小变动数值
-    pub(crate) price_precision: f64,   // 价格精度,表示价格精确到2位小数
-    pub(crate) amount_precision: f64,  // 下单量精度,表示下单量精确到3位小数
-    pub(crate) min_qty: f64,           // 最小下单量
-    pub(crate) max_qty: f64,           // 最大下单量
-    pub(crate) min_notional: f64,      // 最小下单金额
-    pub(crate) max_notional: f64,      // 最大下单金额
-    pub(crate) ct_val: f64,            // 合约价值
+    pub symbol: String,
+    pub base_asset: String,
+    pub quote_asset: String,
+    pub tick_size: f64,
+    pub amount_size: f64,       // 下单量最小变动数值
+    pub price_precision: f64,   // 价格精度,表示价格精确到2位小数
+    pub amount_precision: f64,  // 下单量精度,表示下单量精确到3位小数
+    pub min_qty: f64,           // 最小下单量
+    pub max_qty: f64,           // 最大下单量
+    pub min_notional: f64,      // 最小下单金额
+    pub max_notional: f64,      // 最大下单金额
+    pub ct_val: f64,            // 合约价值
 }
 
 // 处理交易对格式

+ 51 - 32
src/main.rs

@@ -1,5 +1,5 @@
+
 use ndarray::prelude::*;
-use chrono::{Timelike};
 use crate::as_libs::*;
 use crate::exchange_middle_ware::{Account, get_binance_depth, get_binance_klines, get_okx_account};
 use time::OffsetDateTime;
@@ -8,6 +8,7 @@ mod as_libs;
 mod exchange_libs;
 mod exchange_middle_ware;
 
+#[derive(Debug)]
 struct Ira{
     ira: f64,
     gamma: f64,
@@ -29,36 +30,46 @@ struct OrderDict {
     sell_price: f64
 }
 // 订单信息
+
+#[derive(Debug)]
 struct OrderInfo {
-    id: str,
+    id: String,
     time_num: i64
 }
 
 
-fn do_logic(spreadlist: &mut Vec<f64>, symbol: &String, limit: i32, account_params: &Vec<String>, short_interval: &String, ira_start: f64, ira_end: f64, quantity_max: f64, amount_decimal_places: usize, order_info_list: &mut Vec<OrderInfo>) -> i8{
+async fn do_logic(spreadlist: &mut Vec<f64>, symbol: &String, limit: i32, account_params: &Vec<String>, short_interval: &String, ira_start: f64, ira_end: f64, quantity_max: f64, amount_decimal_places: usize, order_info_list: &mut Vec<OrderInfo>) -> i8{
+    let depth = get_binance_depth(&symbol, limit).await;
+
     // 1.获取最新spread,如何将主逻辑和spread(ws推送方式)关联起来
-    let (spread, mid_price, ask, bid) = get_spread(&get_binance_depth(&symbol, limit));
+    let (spread, mid_price, ask, bid) = get_spread(&depth);
 
     if spreadlist.len() > 300 {
         spreadlist.remove(0);
     }
     spreadlist.push(spread);
     // 2.获取最大spread????
-    let max_spread = *spreadlist.iter().max().unwrap();
+    // 使用 max_by 方法和 partial_cmp 进行比较
+    let max_option = spreadlist.iter().max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
+
+    match max_option {
+        Some(max_value) => println!("最大值是: {}", max_value),
+        None => println!("列表为空"),
+    }
+    let max_spread =  *max_option.unwrap();
     if spreadlist.len() < 10 {
 
     } else {
         // 1.获取账户信息
         let balance_info:Account = get_okx_account( &account_params);
         // 2.获取最新k线
-        let df2 = get_binance_klines(&symbol.to_string(), &short_interval, 200);
+        let df2 = get_binance_klines(&symbol.to_string(), &short_interval, &200).await;
         // 计算最近20根K线的标准差
-        let std = std_n_by_records(&df2, 20);
         // 3.获取标准差数组的最后一个值
-        let last_std = std.last().unwrap();
+        let last_std = std_n_by_records(&df2, 20);
         // 4.计算库存资产折合本位币价值
         let mut q = (balance_info.stocks + balance_info.frozen_stocks) * mid_price;
-        if q == 0{
+        if q == 0.0{
             q = 10.0;
         }
         // 5.计算gamma值
@@ -95,14 +106,14 @@ fn do_logic(spreadlist: &mut Vec<f64>, symbol: &String, limit: i32, account_para
             }
         }
         // 12.df4要求过滤出,挂单卖出价 >= 实际卖一价的部分,相当于是更严格的挂单(防止吃单)
-        let df4 = ira_list.iter().filter(|&ira_info| ira_info.ask >= ira_info.ask_best).cloned().collect();
-        println!("df4: {}", df4);
+        let df4:Vec<&Ira> = ira_list.iter().filter(|&ira_info| ira_info.ask >= ira_info.ask_best).collect();
+        println!("df4: {:?}", df4);
 
         // 13.对不同资产级别的本位币应用不同的库存处理方式(仓位等级),最后分别得出:A. 开多资金 B. 开多价格 C. 开空资金 D. 开空价格
         let rate = 50;
         let mut order_dict :OrderDict;
         if rate < 40 {
-            let mut zj = calc_order_amount(ira_end, ira_list.first().unwrap().ira, q, quantity_max, ira_list.first().unwrap().bid_price, amount_decimal_places);
+            let mut zj = calc_order_amount(ira_end, ira_list.first().unwrap().ira, q, quantity_max, ira_list.first().unwrap().bid, amount_decimal_places);
 
             // 使用资金范围限制
             if zj < 10.0 {
@@ -117,36 +128,42 @@ fn do_logic(spreadlist: &mut Vec<f64>, symbol: &String, limit: i32, account_para
             };
             println!("order_dict: {:?}", order_dict);
         } else if rate <= 60{
-            let index :i8 = (ira_list.len() / 2) as i8;
-            let zj = calc_order_amount(ira_end, ira_list.get(index).ira, q, quantity_max, ira_list.get(index).bid_price, amount_decimal_places);
+            let index :usize = (ira_list.len() / 2);
+            let zj = calc_order_amount(ira_end, ira_list.get(index).unwrap().ira, q, quantity_max, ira_list.get(index).unwrap().bid, amount_decimal_places);
             order_dict = OrderDict{
                 zj,
-                buy_price: truncate_decimal_places(ira_list.get(index).bid, 2),
-                sell_price: truncate_decimal_places(ira_list.get(index).ask, 2)
+                buy_price: truncate_decimal_places(ira_list.get(index).unwrap().bid, 2),
+                sell_price: truncate_decimal_places(ira_list.get(index).unwrap().ask, 2)
             };
             println!("order_dict: {:?}", order_dict);
         } else if rate > 60 {
-            let zj = calc_order_amount(ira_end, ira_list.last().unwrap().ira, q, quantity_max, ira_list.last().unwrap().bid_price, amount_decimal_places);
-            order_dict = OrderDict{
+            let zj = calc_order_amount(ira_end, ira_list.last().unwrap().ira, q, quantity_max, ira_list.last().unwrap().bid, amount_decimal_places);
+            order_dict = OrderDict {
                 zj,
                 buy_price: truncate_decimal_places(ira_list.last().unwrap().bid, 2),
                 sell_price: truncate_decimal_places(ira_list.last().unwrap().ask, 2)
             };
             println!("order_dict: {:?}", order_dict);
         }
-        /**
-            TODO: 发起交易
-        **/
+        //     TODO: 发起交易
 
     }
     if order_info_list.len() > 0{
         let now_time = OffsetDateTime::now_utc().unix_timestamp();
+        let mut next_list:Vec<OrderInfo> = vec![];
+
         // 超300s 需取消的订单
-        let check_order:Vec<OrderInfo> = order_info_list.iter().filter(|info| info.time_num + 300 >= now_time).cloned().collect();
-        let next_list:Vec<OrderInfo> =  order_info_list.iter().filter(|info| info.time_num + 300 < now_time).cloned().collect();
-        if check_order.len() > 0 {
-            for info in check_order{
-                // 取消订单
+        for order in order_info_list.iter_mut(){
+            if order.time_num + 300 <= now_time {
+                // 取消订单,可以在这里执行取消订单的操作
+                println!("取消订单: {}", order.id);
+            } else {
+                // 使用解构复制满足条件的订单
+                let new_order = OrderInfo {
+                    id: order.id.clone(),
+                    time_num: order.time_num
+                };
+                next_list.push(new_order); // 克隆满足条件的订单并添加到 next_list 中
             }
         }
         order_info_list.clear();
@@ -156,7 +173,8 @@ fn do_logic(spreadlist: &mut Vec<f64>, symbol: &String, limit: i32, account_para
     return 1;
 }
 
-fn main() {
+#[tokio::main]
+async fn main() {
     // 获取账户信息参数
     let account_params = vec!["213".to_string()];
     let mut spreadlist:Vec<f64> = Vec::new();
@@ -177,11 +195,12 @@ fn main() {
     let mut  order_info_list:Vec<OrderInfo> = Vec::new();
 
     while true {
-        let result = do_logic(&mut spreadlist, &symbol, limit, &account_params, &short_interval, ira_start, ira_end, quantity_max, amount_decimal_places, &mut order_info_list);
-        match result {
-            Ok(value) => println!("Result: {}", value),
-            Err(error) => println!("Error: {}", error),
-        }
+        do_logic(&mut spreadlist, &symbol, limit, &account_params, &short_interval, ira_start, ira_end, quantity_max, amount_decimal_places, &mut order_info_list).await;
+
+        // match future_result {
+        //     Ok(value) => println!("Result: "),
+        //     Err(error) => println!("Error: "),
+        // }
 
     }
 

+ 69 - 3
tests/binance_ws_test.rs

@@ -1,4 +1,70 @@
-#[test]
-fn it_works() {
-    println!("Hello World.")
+use tungstenite::client::connect_with_proxy;
+use tungstenite::protocol::WebSocketConfig;
+use tungstenite::Message;
+use std::net::{SocketAddr, IpAddr, Ipv4Addr};
+use serde_json::json;
+use url::Url;
+
+#[tokio::test]
+async fn test() {
+    let request_url = Url::parse("wss://stream.binance.com:443/ws/btcusdt@depth10@100ms").unwrap();
+    let proxy_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9080);
+    let websocket_config = Some(WebSocketConfig {
+        max_send_queue: Some(16),
+        max_message_size: Some(16 * 1024 * 1024),
+        max_frame_size: Some(16 * 1024 * 1024),
+        accept_unmasked_frames: false,
+    });
+    let max_redirects = 5;
+
+    let (mut socket, response) =
+        connect_with_proxy(request_url, proxy_address, websocket_config, max_redirects)
+            .expect("Can't connect");
+
+    // println!("connected:{}", response.status());
+
+    let j = json!({
+        "method": "SUBSCRIBE",
+        "params":
+        [
+            "btcusdt@depth1@100ms"
+        ],
+        "id": 1
+    });
+
+    println!("{}", j.to_string());
+
+    socket
+        .write_message(Message::Text(j.to_string().into()))
+        .unwrap();
+
+    loop {
+        if !socket.can_read() {
+            continue;
+        }
+
+        let msg = socket.read_message();
+
+        println!("Received msg");
+
+        match msg {
+            Ok(Message::Text(text)) => {
+                println!("Received text: {}", text);
+            }
+            Ok(Message::Binary(bin)) => {
+                println!("Received binary: {:?}", bin);
+            }
+            Ok(Message::Ping(_)) | Ok(Message::Pong(_)) | Ok(Message::Close(_)) => {
+                socket.write_message(Message::Pong(vec![]))
+                    .unwrap();
+                println!("ping");
+            }
+            Err(error) => {
+                println!("Error receiving message: {}", error);
+                break;
+            }
+        }
+    }
+
+    socket.close(None).unwrap();
 }