Browse Source

逻辑写完了,上线测试。

skyffire 1 năm trước cách đây
mục cha
commit
40d8a2dc54
4 tập tin đã thay đổi với 158 bổ sung38 xóa
  1. 2 2
      README.MD
  2. 1 0
      src/main.rs
  3. 121 0
      src/rank.rs
  4. 34 36
      src/server.rs

+ 2 - 2
README.MD

@@ -16,7 +16,7 @@
             "symbol": "BTC_USDT",           // 币对
             "score": "205",                 // 总评分
             "msv_score": "157",             // 波动分数
-            "liquity_score": "33",          // 交易量分数
+            "liquidity_score": "33",        // 交易量分数
             "frequency_score": "15",        // 交易频次分数
             "msv_abs_total": "17.2",        // Sum(Abs(波动率))
             "msv_abs_max": "1.2",           // 最大Abs(波动率)
@@ -24,7 +24,7 @@
             "coverted_open_base": "0.007",  // 转换后的基础开仓值(0.007代表0.7%)
             "epr_total": "7.2",             // Sum(预期利润)
             "effective_count": "10",        // 有效波动次数
-            "liquity_avg": "3000",          // 平均行情推动量
+            "liquidity_avg": "3000",        // 平均行情推动量
         },
         ...
     ],

+ 1 - 0
src/main.rs

@@ -4,6 +4,7 @@ mod server;
 mod listener_tools;
 mod gate_usdt_swap_data_listener;
 mod msv;
+mod rank;
 
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};

+ 121 - 0
src/rank.rs

@@ -0,0 +1,121 @@
+use std::collections::HashMap;
+use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+use tokio::sync::MutexGuard;
+use crate::msv::Indicators;
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Rank {
+    symbol: String,
+    score: Decimal,
+    msv_score: Decimal,
+    liquidity_score: Decimal,
+    frequency_score: Decimal,
+    msv_abs_total: Decimal,
+    msv_abs_max: Decimal,
+    msv_abs_avg: Decimal,
+    coverted_open_base: Decimal,
+    epr_total: Decimal,
+    effective_count: Decimal,
+    liquidity_avg: Decimal,
+}
+
+const TWENTY: Decimal = dec!(20);
+const SIXTY: Decimal = dec!(60);
+const TWO_HUNDRED: Decimal = dec!(200);
+const TEN_THOUSAND: Decimal = dec!(10000);
+
+// 根据最终的msv计算排行榜
+pub fn generate_rank_by_indicator_map(indicator_map: &MutexGuard<HashMap<String, Indicators>>) -> Value {
+    let mut rank_list: Vec<Rank> = vec![];
+
+    for (key, indicators) in indicator_map.iter() {
+        let symbol = key.clone();
+
+        // ============== msv相关数据的计算 =================
+        let mut msv_abs_total = Decimal::ZERO;
+        let mut msv_abs_max = Decimal::ZERO;
+        let mut effective_count = Decimal::ZERO;
+        for value in indicators.msv.iter() {
+            let msv_abs_value = value[1].abs();
+
+            if msv_abs_value <= Decimal::ZERO {
+                continue
+            }
+
+            effective_count += Decimal::ONE;
+            msv_abs_total += msv_abs_value;
+
+            if msv_abs_value > msv_abs_max {
+                msv_abs_max = msv_abs_value
+            }
+        }
+        let mut msv_abs_avg = if effective_count == Decimal::ZERO {
+            Decimal::ZERO
+        } else {
+            msv_abs_total / effective_count
+        };
+        msv_abs_avg.rescale(6);
+        let mut coverted_open_base = (msv_abs_max + msv_abs_avg) / TWO_HUNDRED;
+        coverted_open_base.rescale(8);
+
+        // ============== epr相关数据的计算 =================
+        let mut epr_total = Decimal::ZERO;
+        for value in indicators.eprs.iter() {
+            epr_total += value[1];
+        }
+
+        // ============== liq相关数据的计算 =================
+        let mut liquidity_total = Decimal::ZERO;
+        for value in indicators.liqs.iter() {
+            liquidity_total += value[1] * Decimal::ONE_THOUSAND;
+        }
+        let mut liquidity_avg = if effective_count == Decimal::ZERO {
+            Decimal::ZERO
+        } else {
+            liquidity_total / effective_count
+        };
+        liquidity_avg.rescale(0);
+
+        // msv_score计算规则
+        let mut msv_score = if msv_abs_total > Decimal::ZERO {
+            effective_count * (epr_total / msv_abs_total) * SIXTY
+        } else {
+            Decimal::ZERO
+        };
+        msv_score.rescale(2);
+
+        // liquidity_score
+        let mut liquidity_score = (liquidity_avg / TEN_THOUSAND) * TWENTY;
+        liquidity_score.rescale(2);
+
+        // frequency_score计算规则
+        let mut frequency_score = (effective_count / Decimal::ONE_HUNDRED) * TWENTY;
+        frequency_score.rescale(2);
+
+        let score = msv_score + liquidity_score + frequency_score;
+        let rank = Rank {
+            symbol,
+            score,
+            msv_score,
+            liquidity_score,
+            frequency_score,
+            msv_abs_total,
+            msv_abs_max,
+            msv_abs_avg,
+            coverted_open_base,
+            epr_total,
+            effective_count,
+            liquidity_avg,
+        };
+
+        rank_list.push(rank);
+    }
+
+    // 按 score 倒序排序
+    rank_list.sort_by(|a, b| b.score.cmp(&a.score));
+
+    return serde_json::to_value(&rank_list).unwrap();
+}

+ 34 - 36
src/server.rs

@@ -4,7 +4,7 @@ use actix_web::{web, App, HttpResponse, HttpServer, Responder, get};
 use serde::{Deserialize, Serialize};
 use serde_json::{json, Value};
 use tracing::{info};
-use crate::gate_usdt_swap_data_listener;
+use crate::{gate_usdt_swap_data_listener, rank};
 
 // 定义用于反序列化查询参数的结构体
 #[derive(Serialize, Deserialize, Clone)]
@@ -32,40 +32,8 @@ pub struct Response {
 
 #[get("/rk/get_rank_list")]
 async fn get_rank_list(query: web::Query<RankQuery>) -> impl Responder {
-    if query.validate() {
-        let exchange = query.exchange.clone().unwrap().clone();
-        let indicators = match exchange.as_str() {
-            "gate_usdt_swap" => {
-                gate_usdt_swap_data_listener::INDICATOR_MAP.lock().await
-            },
-            _ => {
-                let response = Response {
-                    query: serde_json::to_value(&query.into_inner()).unwrap(),
-                    msg: Some("查询内容有误,exchange当前仅支持:[gate_usdt_swap]".to_string()),
-                    code: 500,
-                    data: Value::Null,
-                };
-
-                let json_string = serde_json::to_string(&response).unwrap();
-                return HttpResponse::Ok().content_type("application/json").body(json_string)
-            }
-        };
-
-        let count = indicators.get("BTC_USDT").unwrap().total_size;
-        let rst = json!({
-            "count": count
-        });
-
-        let response = Response {
-            query: serde_json::to_value(&query.into_inner()).unwrap(),
-            msg: Some("查询成功".to_string()),
-            code: 200,
-            data: rst,
-        };
-
-        let json_string = serde_json::to_string(&response).unwrap();
-        HttpResponse::Ok().content_type("application/json").body(json_string)
-    } else {
+    // ============================= 参数校验部分 =========================================
+    if !query.validate() {
         let response = Response {
             query: serde_json::to_value(&query.into_inner()).unwrap(),
             msg: Some("查询内容有误,必须传递的参数:[exchange]".to_string()),
@@ -74,8 +42,38 @@ async fn get_rank_list(query: web::Query<RankQuery>) -> impl Responder {
         };
 
         let json_string = serde_json::to_string(&response).unwrap();
-        HttpResponse::Ok().content_type("application/json").body(json_string)
+        return HttpResponse::Ok().content_type("application/json").body(json_string);
     }
+    let exchange = query.exchange.clone().unwrap().clone();
+    let indicators = match exchange.as_str() {
+        "gate_usdt_swap" => {
+            gate_usdt_swap_data_listener::INDICATOR_MAP.lock().await
+        },
+        _ => {
+            let response = Response {
+                query: serde_json::to_value(&query.into_inner()).unwrap(),
+                msg: Some("查询内容有误,exchange当前仅支持:[gate_usdt_swap]".to_string()),
+                code: 500,
+                data: Value::Null,
+            };
+
+            let json_string = serde_json::to_string(&response).unwrap();
+            return HttpResponse::Ok().content_type("application/json").body(json_string)
+        }
+    };
+
+    // 逻辑执行部分
+    let rst = rank::generate_rank_by_indicator_map(&indicators);
+
+    let response = Response {
+        query: serde_json::to_value(&query.into_inner()).unwrap(),
+        msg: Some("查询成功".to_string()),
+        code: 200,
+        data: rst,
+    };
+
+    let json_string = serde_json::to_string(&response).unwrap();
+    HttpResponse::Ok().content_type("application/json").body(json_string)
 }
 
 #[get("/rk/get_exchanges")]