skyffire 9 місяців тому
батько
коміт
10eaeb748d
4 змінених файлів з 269 додано та 4 видалено
  1. 36 0
      README.MD
  2. 32 0
      src/db_connector.rs
  3. 188 3
      src/msv.rs
  4. 13 1
      src/server.rs

+ 36 - 0
README.MD

@@ -49,6 +49,42 @@
 }
 ```
 
+### 2. fot(force order time爆仓单触发)
+``` json
+{
+    "indicator": "fot",                     // 指标名
+    "query": {                              // 查询结构
+        "exchange": "binance_usdt_swap",    // 交易所,当前支持[binance_usdt_swap]
+        "symbol": "btc",                    // 符号,支持大小写,如果不写usdt会自动在后面添加_USDT
+        "minute_time_range": "240",         // 查询多少分钟的数据
+        "value_ln": "10",                   // 量的阈值(ln)
+    }
+}
+```
+
+##### response
+``` json
+{
+    "query": {},                            // 你的查询参数,用于接口联调       
+    "message": "hello",                     // 后台提醒
+    "code": 200,                            // 200 就是对的
+    "data": {                               // 指标结果
+        "profits": [                        // 收益率
+            // i1      i2     i3     ...
+            ["0.06", "0.01", "0.03", ...],  // 第一根线 [收益率0, 收益率1, 收益率2, ...]
+            ["0.03", "0.02", "-0.03", ...], // 第二根线 [收益率0, 收益率1, 收益率2, ...]
+            ...
+        ],
+        "profit_total_list": [              // 第n秒收益率之和,柱状图吧
+            "0.06", 
+            "0.01", 
+            "0.03", 
+            ...
+        ]
+    },
+}
+```
+
 ### 2. Trades数据
 ``` json
 {

+ 32 - 0
src/db_connector.rs

@@ -44,6 +44,38 @@ pub async fn get_trades_json(exchange: &str, symbol: &str, start_at: i64, end_at
     }
 }
 
+pub async fn get_force_orders_json(exchange: &str, symbol: &str, start_at: i64, end_at: i64) -> Response {
+    let url = "http://dc.skyfffire.com:8888/force_orders";
+    let params = json!({
+        "exchange": exchange,
+        "symbol": symbol,
+        "start_time": start_at,
+        "end_time": end_at
+    });
+
+    // 创建 HTTP 客户端
+    let client = Client::new();
+
+    // 发送 GET 请求
+    let response = client.get(url)
+        .query(&params)
+        .send()
+        .await.unwrap();
+
+    // 错误处理
+    if response.status().is_success() {
+        let response_text = response.text().await.unwrap();
+        serde_json::from_str(response_text.as_str()).unwrap()
+    } else {
+        Response {
+            msg: Some("get_force_orders_json 请求失败,预计是指标层的网络请求错误。".to_string()),
+            query: params,
+            data: Default::default(),
+            code: 500,
+        }
+    }
+}
+
 pub async fn get_records_json(exchange: &str, symbol: &str, start_at: i64, end_at: i64) -> Response {
     let url = "http://dc.skyfffire.com:8888/records";
     let params = json!({

+ 188 - 3
src/msv.rs

@@ -1,14 +1,15 @@
 use std::cmp::{max, min};
+use std::collections::HashMap;
 use std::str::FromStr;
 use actix_web::{HttpResponse};
 use chrono::Utc;
-use rust_decimal::{Decimal};
+use rust_decimal::{Decimal, MathematicalOps};
 use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
 use rust_decimal_macros::dec;
 use serde_json::{json, Value};
-use crate::db_connector::{get_simple_depths_json, get_trades_json};
+use crate::db_connector::{get_force_orders_json, get_simple_depths_json, get_trades_json};
 use crate::params_utils::{get_str, parse_str_to_decimal};
-use crate::server::{Response, SimpleDepth, Trade};
+use crate::server::{ForceOrder, Response, SimpleDepth, Trade};
 
 pub fn symbol_fix(symbol: &str) -> String {
     let mut fixed = symbol.to_uppercase();
@@ -419,6 +420,172 @@ pub fn generate_msv_by_trades(mut trades: Vec<Trade>, mills_back: Decimal, simpl
     })
 }
 
+pub async fn generate_fot(query_value: Value) -> HttpResponse {
+    // 参数处理
+    let exchange = match get_str(query_value.clone(), "exchange") {
+        Ok(str) => {
+            str
+        }
+        Err(response) => {
+            return response
+        }
+    };
+    let symbol = match get_str(query_value.clone(), "symbol") {
+        Ok(symbol) => {
+            symbol_fix(symbol.as_str())
+        }
+        Err(response) => {
+            return response
+        }
+    };
+    let minute_time_range = match parse_str_to_decimal(query_value.clone(), "minute_time_range") {
+        Ok(t) => {
+            t.to_i64().unwrap()
+        }
+        Err(response) => {
+            return response
+        }
+    };
+    let value_ln = match parse_str_to_decimal(query_value.clone(), "value_ln") {
+        Ok(t) => {
+            t
+        }
+        Err(response) => {
+            return response
+        }
+    };
+    // 链接数据服务器查询数据
+    let end_time = Utc::now().timestamp_millis();
+    let start_time = end_time - minute_time_range * 60 * 1000;
+    let trades_response = get_trades_json(
+        exchange.as_str(),
+        symbol.as_str(),
+        start_time,
+        end_time,
+    ).await;
+    let force_orders_response = get_force_orders_json(
+        exchange.as_str(),
+        symbol.as_str(),
+        start_time,
+        end_time,
+    ).await;
+
+    // 对数据库返回的数据进行容错处理
+    if trades_response.code == 200 && force_orders_response.code == 200 {
+        // trades数据处理
+        let mut trades = parse_json_to_trades(trades_response.data);
+        // 按时间顺序排列trades数据
+        trades.sort_by(|a, b| a.time.cmp(&b.time));
+        // 处理trade,一秒只需要一个数据
+        let mut processed_trades: HashMap<Decimal, Decimal> = HashMap::new();
+        for trade in &trades {
+            let mut insert_time_second = trade.time / Decimal::ONE_THOUSAND;
+            insert_time_second.rescale(0);
+
+            // 如若是空值,直接插入
+            if processed_trades.get(&insert_time_second).is_none() {
+                processed_trades.insert(insert_time_second, trade.price);
+            }
+        }
+        // force_orders处理
+        let force_orders = parse_json_to_force_orders(force_orders_response.data);
+
+        let indicator = generate_fot_by_trades(processed_trades, force_orders, value_ln);
+
+        // 返回数据
+        let response = Response {
+            query: query_value.clone(),
+            msg: Some("指标生成完毕".to_string()),
+            code: 200,
+            data: indicator,
+        };
+
+        let json_string = serde_json::to_string(&response).unwrap();
+        HttpResponse::Ok().content_type("application/json").body(json_string)
+    } else {
+        let json_string = serde_json::to_string(&trades_response).unwrap();
+        HttpResponse::Ok().content_type("application/json").body(json_string)
+    }
+}
+
+pub fn generate_fot_by_trades(processed_trades: HashMap<Decimal, Decimal>, force_orders: Vec<ForceOrder>, value_limit_ln: Decimal) -> Value {
+    let mut profits_rst: Vec<Vec<Decimal>> = vec![];
+    // 外层遍历信号源
+    for force_order in force_orders {
+        let force_value = if force_order.value > Decimal::ONE {
+            force_order.value.ln()
+        } else if force_order.value < Decimal::NEGATIVE_ONE {
+            -force_order.value.abs().ln()
+        } else {
+            Decimal::ZERO
+        };
+
+        // 小于设定的阈值,就不交易
+        if force_value.abs() < value_limit_ln {
+            continue
+        }
+
+        let mut trade_second = force_order.time / Decimal::ONE_THOUSAND;
+        trade_second.rescale(0);
+
+        let first_trade_price = match processed_trades.get(&trade_second) {
+            None => {
+                force_order.price
+            }
+            Some(price) => {
+                price.clone()
+            }
+        };
+        let mut prev_calc_price = first_trade_price;
+
+        // 开始统计每一秒的收益率
+        let mut profit_list: Vec<Decimal> = vec![];
+        for next_time_int in 1..3601 {
+            let next_time = Decimal::from(next_time_int);
+
+            let calc_price = match processed_trades.get(&(trade_second + next_time)) {
+                None => {
+                    prev_calc_price
+                }
+                Some(trade_price) => {
+                    trade_price.clone()
+                }
+            };
+
+            // 求收益率
+            let mut profit = (calc_price / first_trade_price) - Decimal::ONE;
+            profit.rescale(4);
+            // 负数另考虑
+            if force_value < Decimal::ZERO {
+                profit = -profit;
+            }
+
+            profit_list.push(profit);
+
+            prev_calc_price = calc_price;
+        }
+
+        profits_rst.push(profit_list)
+    }
+
+
+    let mut profit_total_list: Vec<Decimal> = vec![];
+    for index in 0usize..3600usize {
+        let mut total_profit = Decimal::ZERO;
+
+        for profit_list in &profits_rst {
+            total_profit += profit_list[index]
+        }
+
+        profit_total_list.push(total_profit)
+    }
+
+    json!({
+        "profits": profits_rst,
+        "profit_total_list": profit_total_list,
+    })
+}
+
 // 将json转换为trades
 pub fn parse_json_to_trades(trades_json: Value) -> Vec<Trade> {
     let mut rst = vec![];
@@ -436,6 +603,24 @@ pub fn parse_json_to_trades(trades_json: Value) -> Vec<Trade> {
     rst
 }
 
+pub fn parse_json_to_force_orders(force_orders_json: Value) -> Vec<ForceOrder> {
+    let mut rst = vec![];
+
+    for force_order_json in force_orders_json.as_array().unwrap() {
+        let force_order = ForceOrder {
+            time: Decimal::from_str(force_order_json["time"].as_str().unwrap()).unwrap(),
+            symbol: "".to_string(),
+            price: Decimal::from_str(force_order_json["price"].as_str().unwrap()).unwrap(),
+            amount: Decimal::from_str(force_order_json["amount"].as_str().unwrap()).unwrap(),
+            value: Decimal::from_str(force_order_json["value"].as_str().unwrap()).unwrap(),
+        };
+
+        rst.insert(0, force_order)
+    }
+
+    rst
+}
+
 // // 将json转换为简易深度数据
 // pub fn parse_json_to_simple_depths(depths_json: Value) -> Vec<SimpleDepth> {
 //     let mut rst = vec![];

+ 13 - 1
src/server.rs

@@ -9,7 +9,7 @@ use serde_json::Value;
 use tracing::{info};
 use crate::db_connector::get_exchanges_json;
 use crate::depth::generate_simple_depth;
-use crate::msv::generate_msv;
+use crate::msv::{generate_fot, generate_msv};
 use crate::params_utils::{get_array, get_str, parse_str_to_decimal};
 use crate::symbol_filter::get_final_symbols;
 use crate::trades::generate_trades;
@@ -30,6 +30,15 @@ pub struct Trade {
     pub price: Decimal
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct ForceOrder {
+    pub time: Decimal,
+    pub symbol: String,
+    pub price: Decimal,
+    pub amount: Decimal,
+    pub value: Decimal,
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct SimpleDepth {
     pub time: Decimal,
@@ -116,6 +125,9 @@ async fn get_indicator(query: web::Json<Value>) -> impl Responder {
         "depth" => {
             generate_simple_depth(query.into_inner().get("query").unwrap().clone()).await
         },
+        "fot" => {
+            generate_fot(query.into_inner().get("query").unwrap().clone()).await
+        },
         _ => {
             let query_json = query.into_inner().clone();
             let response = Response {