Kaynağa Gözat

Merge remote-tracking branch 'origin/avellaneda_stoikov_s1' into avellaneda_stoikov_s1

skyfffire 1 yıl önce
ebeveyn
işleme
7b14aa7849

+ 1 - 1
derive/src/binance_swap_export.rs

@@ -90,7 +90,7 @@ impl ExportConnector for BinanceSwapExport {
                     ]);
                 }
 
-                if trades_info.len().to_i64().unwrap() < 96 {
+                if trades_info.len().to_i64().unwrap() < limit_params {
                     break;
                 }
             }

+ 135 - 0
derive/src/bybit_swap_export.rs

@@ -0,0 +1,135 @@
+use std::collections::{BTreeMap};
+use std::str::FromStr;
+use async_trait::async_trait;
+use chrono::{FixedOffset, NaiveDateTime, TimeZone};
+use rust_decimal::Decimal;
+use rust_decimal::prelude::ToPrimitive;
+use serde::{Deserialize, Serialize};
+use tracing::{warn};
+use exchanges::bybit_swap_rest::BybitSwapRest;
+use standard::utils;
+use crate::ExportConnector;
+
+pub struct BybitSwapExport {
+    request: BybitSwapRest,
+}
+
+impl BybitSwapExport {
+    pub async fn new(is_colo: bool, params: BTreeMap<String, String>) -> BybitSwapExport {
+        BybitSwapExport {
+            request: BybitSwapRest::new(is_colo, params.clone())
+        }
+    }
+}
+
+/// TradesSwap
+/// - `symbol`: String,
+/// - `order_id`: String,
+/// - `order_link_id`: String,
+/// - `side`: String,
+/// - `order_price`: String,
+/// - `order_qty`: String,
+/// - `leaves_qty`: String,
+/// - `create_type`: String,
+/// - `order_type`: String,
+/// - `stop_order_type`: String,
+/// - `exec_fee`: String,
+/// - `exec_id`: String,
+/// - `exec_price`: String,
+/// - `exec_qty`: String,
+/// - `exec_type`: String,
+/// - `exec_value`: String,
+/// - `exec_time`: String,
+/// - `fee_currency`: String,
+/// - `is_maker`: bool,
+/// - `fee_rate`: String,
+/// - `trade_iv`: String,
+/// - `mark_iv`: String,
+/// - `mark_price`: String,
+/// - `index_price`: String,
+/// - `underlying_price`: String,
+/// - `block_trade_id`: String,
+/// - `closed_size`: String,
+/// - `seq`: i64,
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct TradesSwap {
+    symbol: String,
+    order_id: String,
+    order_link_id: String,
+    side: String,
+    order_price: String,
+    order_qty: String,
+    leaves_qty: String,
+    create_type: String,
+    order_type: String,
+    stop_order_type: String,
+    exec_fee: String,
+    exec_id: String,
+    exec_price: String,
+    exec_qty: String,
+    exec_type: String,
+    exec_value: String,
+    exec_time: String,
+    fee_currency: String,
+    is_maker: bool,
+    fee_rate: String,
+    trade_iv: String,
+    mark_iv: String,
+    mark_price: String,
+    index_price: String,
+    underlying_price: String,
+    block_trade_id: String,
+    closed_size: String,
+    seq: i64,
+}
+
+#[async_trait]
+impl ExportConnector for BybitSwapExport {
+    async fn export_trades(&mut self, prefix_name: &str, symbol: String, start_time: i64, end_time: i64, limit: i64) -> String {
+        let symbol_format = utils::format_symbol(symbol.clone(), "");
+        let limit_params = if limit > 1000 {
+            warn!("查询条数最大为1000条,已修改为1000条!");
+            1000
+        } else { limit };
+        let mut cursor = "".to_string();
+        let mut data_array = vec![];
+
+        loop {
+            let res_data = self.request.get_user_trades(symbol_format.clone(), start_time, end_time, limit_params, cursor.clone()).await;
+            if res_data.code == 200 {
+                let data_clone = res_data.data.clone();
+                let trades_info: Vec<TradesSwap> = serde_json::from_str(&*data_clone["list"].to_string()).unwrap();
+                cursor = data_clone["nextPageCursor"].as_str().unwrap_or("").to_string();
+                for value in trades_info.iter() {
+                    let time = FixedOffset::east_opt(8 * 3600).unwrap().from_utc_datetime(&NaiveDateTime::from_timestamp_millis(value.exec_time.parse::<i64>().unwrap()).unwrap()).format("%Y-%m-%d %H:%M:%S%.3f").to_string();
+                    let order_type = value.order_type.clone();
+                    let income = (Decimal::from_str(&value.exec_price).unwrap() - Decimal::from_str(&value.order_price).unwrap()) * Decimal::from_str(&value.exec_qty).unwrap();
+
+                    data_array.push(vec![
+                        value.exec_id.clone(),
+                        value.order_id.clone(),
+                        value.symbol.clone(),
+                        value.side.clone(),
+                        value.exec_price.clone(),
+                        value.exec_qty.clone(),
+                        order_type.to_string(),
+                        value.exec_value.clone(),
+                        value.exec_fee.clone(),
+                        value.fee_rate.clone(),
+                        income.to_string(),
+                        time,
+                    ]);
+                }
+
+                if trades_info.len().to_i64().unwrap() < limit_params {
+                    break;
+                }
+            }
+        }
+
+        let header_array = vec!["交易编号", "订单编号", "交易币对", "买卖方向", "成交价格", "成交数量", "交易类型", "成交价值", "交易费用", "交易费率", "交易盈亏", "交易时间"];
+        data_array.reverse();
+        global::export_utils::export_excel(header_array, data_array, prefix_name)
+    }
+}

+ 5 - 0
derive/src/export_excel.rs

@@ -1,5 +1,6 @@
 use std::collections::BTreeMap;
 use crate::binance_swap_export::BinanceSwapExport;
+use crate::bybit_swap_export::BybitSwapExport;
 // use crate::bitget_spot_export::BitgetSpotExport;
 use crate::ExportConnector;
 use crate::gate_swap_export::GateSwapExport;
@@ -13,6 +14,7 @@ pub enum ExportEnum {
     // KucoinSwap,
     // KucoinSpot,
     GateSwap,
+    BybitSwap,
     // BitgetSpot,
     // OkxSwap,
 }
@@ -35,6 +37,9 @@ impl ExportExcel {
             ExportEnum::GateSwap => {
                 Box::new(GateSwapExport::new(is_colo, params).await)
             }
+            ExportEnum::BybitSwap => {
+                Box::new(BybitSwapExport::new(is_colo, params).await)
+            }
             // ExportEnum::BitgetSpot => {
             //     Box::new(BitgetSpotExport::new(is_colo, params).await)
             // }

+ 1 - 0
derive/src/lib.rs

@@ -7,6 +7,7 @@ pub mod export_excel;
 mod gate_swap_export;
 mod bitget_spot_export;
 mod okx_swap_export;
+mod bybit_swap_export;
 
 #[async_trait]
 pub trait ExportConnector {

+ 19 - 0
derive/tests/bybit_swap_export_test.rs

@@ -0,0 +1,19 @@
+mod export_excel_test;
+
+use tracing::{instrument, trace};
+use derive::export_excel::ExportEnum;
+use crate::export_excel_test::test_new_export;
+
+
+const SYMBOL: &str = "ORDI_USDT";
+
+// 测试获取Exchange实体
+#[tokio::test]
+#[instrument(level = "TRACE")]
+async fn test_get_self_exchange() {
+    global::log_utils::init_log_with_trace();
+
+    let mut export = test_new_export(ExportEnum::BybitSwap).await;
+    let export_trades = export.export_trades("bybit_swap", SYMBOL.to_string(), 1726109857000, 1726132850000, 100).await;
+    trace!(?export_trades);
+}

+ 8 - 0
derive/tests/export_excel_test.rs

@@ -53,6 +53,14 @@ pub async fn test_new_export(export_enum: ExportEnum) -> Box<dyn ExportConnector
             params.insert("secret_key".to_string(), secret_key);
             ExportExcel::new(ExportEnum::GateSwap, false, params).await
         }
+        ExportEnum::BybitSwap => {
+            let mut params: BTreeMap<String, String> = BTreeMap::new();
+            let access_key = account_info.bybit_access_key;
+            let secret_key = account_info.bybit_secret_key;
+            params.insert("access_key".to_string(), access_key);
+            params.insert("secret_key".to_string(), secret_key);
+            ExportExcel::new(ExportEnum::BybitSwap, false, params).await
+        }
         // ExportEnum::BitgetSpot => {
         //     let mut params: BTreeMap<String, String> = BTreeMap::new();
         //     let access_key = account_info.bitget_access_key;

+ 20 - 11
exchanges/src/bybit_swap_rest.rs

@@ -130,7 +130,6 @@ impl BybitSwapRest {
         ).await;
         data
     }
-
     //查看持仓信息
     pub async fn get_positions(&mut self, symbol: String, settle_coin: String) -> ResponseData {
         let mut params = serde_json::json!({
@@ -165,10 +164,8 @@ impl BybitSwapRest {
         ).await;
         data
     }
-
     //設置槓桿
-    pub async fn set_leverage(&mut self, symbol: String,
-                              lever: String) -> ResponseData {
+    pub async fn set_leverage(&mut self, symbol: String, lever: String) -> ResponseData {
         let params = serde_json::json!({
              "category": "linear",
              "symbol": symbol,
@@ -183,8 +180,6 @@ impl BybitSwapRest {
         ).await;
         data
     }
-
-
     //查詢錢包餘額
     pub async fn get_account_balance(&mut self, symbol: String) -> ResponseData {
         let params = serde_json::json!({
@@ -199,7 +194,6 @@ impl BybitSwapRest {
         ).await;
         data
     }
-
     //創建委託單
     pub async fn swap_order(&mut self, params: serde_json::Value) -> ResponseData {
         let data = self.request("POST".to_string(),
@@ -230,7 +224,6 @@ impl BybitSwapRest {
         ).await;
         data
     }
-
     //撤单
     pub async fn cancel_order(&mut self, symbol: String, order_id: String, order_link_id: String) -> ResponseData {
         let mut params = serde_json::json!({
@@ -251,7 +244,6 @@ impl BybitSwapRest {
         ).await;
         data
     }
-
     //撤銷所有訂單
     pub async fn cancel_orders(&mut self, symbol: String) -> ResponseData {
         let params = serde_json::json!({
@@ -266,8 +258,25 @@ impl BybitSwapRest {
         ).await;
         data
     }
-
-
+    //账户成交历史
+    pub async fn get_user_trades(&mut self, symbol: String, start_time: i64, end_time: i64, limit: i64, cursor: String) -> ResponseData {
+        let mut params = serde_json::json!({
+            "category": "linear",
+            "limit": 100
+         });
+        if symbol != "" { params["symbol"] = serde_json::json!(symbol); }
+        if start_time > 0 { params["startTime"] = serde_json::json!(start_time); }
+        if end_time > 0 { params["endTime"] = serde_json::json!(end_time); }
+        if limit > 0 { params["limit"] = serde_json::json!(limit); }
+        if cursor != "" { params["cursor"] = serde_json::json!(cursor); }
+        let data = self.request("GET".to_string(),
+                                "/v5".to_string(),
+                                "/execution/list".to_string(),
+                                true,
+                                params.to_string(),
+        ).await;
+        data
+    }
     /*******************************************************************************************************/
     /*****************************************工具函数********************************************************/
     /*******************************************************************************************************/

+ 4 - 0
global/src/account_info.rs

@@ -19,6 +19,8 @@ pub struct AccountInfo {
     pub bitget_access_key: String,
     pub bitget_secret_key: String,
     pub bitget_pass: String,
+    pub bybit_access_key: String,
+    pub bybit_secret_key: String,
     pub htx_access_key: String,
     pub htx_secret_key: String,
     pub htx_pass: String,
@@ -40,6 +42,8 @@ impl AccountInfo {
             bitget_access_key: "".to_string(),
             bitget_secret_key: "".to_string(),
             bitget_pass: "".to_string(),
+            bybit_access_key: "".to_string(),
+            bybit_secret_key: "".to_string(),
             htx_access_key: "".to_string(),
             htx_secret_key: "".to_string(),
             htx_pass: "".to_string(),