|
@@ -1,10 +1,13 @@
|
|
|
|
|
+use std::collections::{BTreeMap, HashMap};
|
|
|
use std::env;
|
|
use std::env;
|
|
|
use reqwest;
|
|
use reqwest;
|
|
|
-
|
|
|
|
|
|
|
+use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
|
|
|
|
|
+use hex;
|
|
|
|
|
+use ring::hmac;
|
|
|
|
|
|
|
|
|
|
|
|
|
/*币安-k线条*/
|
|
/*币安-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 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());
|
|
let req = get(base_url.to_string());
|
|
|
match req.await {
|
|
match req.await {
|
|
@@ -20,10 +23,6 @@ pub async fn binan_k(symbol: &String, interval: &String, limit: &i32) -> String
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
/*普通 Get 请求*/
|
|
/*普通 Get 请求*/
|
|
|
pub async fn get(url: String) -> Result<(String), reqwest::Error> {
|
|
pub async fn get(url: String) -> Result<(String), reqwest::Error> {
|
|
|
let mut conent = String::from("");
|
|
let mut conent = String::from("");
|
|
@@ -43,26 +42,365 @@ pub async fn get(url: String) -> Result<(String), reqwest::Error> {
|
|
|
Ok((conent))
|
|
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 {
|
|
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, ×tamp);
|
|
|
|
|
+
|
|
|
|
|
+ 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(¶ms).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, ¶ms_str);
|
|
|
|
|
+ println!("---message:{:?}", message);
|
|
|
|
|
+ let mut sign = self.okx_sign(secret_key, message);
|
|
|
|
|
+
|
|
|
|
|
+ //添加请求头
|
|
|
|
|
+ let mut headers = self.okx_create_header(&access_keu, &passphrase, &sign, ×tamp);
|
|
|
|
|
+
|
|
|
|
|
+ let client = reqwest::Client::new();
|
|
|
|
|
+ let url = format!("{}{}", base_url, request_path);
|
|
|
|
|
+ let req = client
|
|
|
|
|
+ .post(url)
|
|
|
|
|
+ .headers(headers)
|
|
|
|
|
+ .json(¶ms)
|
|
|
|
|
+ ;
|
|
|
|
|
+
|
|
|
|
|
+ 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(×tamp).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() }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|