|
@@ -1,12 +1,15 @@
|
|
|
use std::collections::{HashMap};
|
|
use std::collections::{HashMap};
|
|
|
|
|
+use std::str::FromStr;
|
|
|
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
|
|
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
|
|
|
-use anyhow::{Result};
|
|
|
|
|
-use tracing::{warn};
|
|
|
|
|
|
|
+use anyhow::{anyhow, bail, Result};
|
|
|
|
|
+use rust_decimal::Decimal;
|
|
|
|
|
+use serde_json::Value;
|
|
|
|
|
+use tracing::{info, warn};
|
|
|
use crate::utils::response::Response;
|
|
use crate::utils::response::Response;
|
|
|
|
|
|
|
|
pub struct DataManager {
|
|
pub struct DataManager {
|
|
|
- // pub asks_map: HashMap<String, BTreeMap<Decimal, Decimal>>,
|
|
|
|
|
- // pub bids_map: HashMap<String, BTreeMap<Reverse<Decimal>, Decimal>>,
|
|
|
|
|
|
|
+ pub best_ask: Decimal,
|
|
|
|
|
+ pub best_bid: Decimal,
|
|
|
|
|
|
|
|
pub delay_total: AtomicI64,
|
|
pub delay_total: AtomicI64,
|
|
|
pub delay_count: AtomicU64,
|
|
pub delay_count: AtomicU64,
|
|
@@ -14,12 +17,10 @@ pub struct DataManager {
|
|
|
|
|
|
|
|
impl DataManager {
|
|
impl DataManager {
|
|
|
pub fn new() -> Self {
|
|
pub fn new() -> Self {
|
|
|
- // let asks_map: HashMap<String, BTreeMap<Decimal, Decimal>> = HashMap::new();
|
|
|
|
|
- // let bids_map: HashMap<String, BTreeMap<Reverse<Decimal>, Decimal>> = HashMap::new();
|
|
|
|
|
-
|
|
|
|
|
DataManager {
|
|
DataManager {
|
|
|
- // asks_map,
|
|
|
|
|
- // bids_map,
|
|
|
|
|
|
|
+ best_ask: Default::default(),
|
|
|
|
|
+ best_bid: Default::default(),
|
|
|
|
|
+
|
|
|
delay_total: AtomicI64::new(0),
|
|
delay_total: AtomicI64::new(0),
|
|
|
delay_count: AtomicU64::new(0),
|
|
delay_count: AtomicU64::new(0),
|
|
|
}
|
|
}
|
|
@@ -47,32 +48,70 @@ impl DataManager {
|
|
|
self.delay_count.store(0, Ordering::Relaxed); // 原子写
|
|
self.delay_count.store(0, Ordering::Relaxed); // 原子写
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub async fn dispatch_message(&mut self, _response: &Response) -> Result<()> {
|
|
|
|
|
- // // 1. 预解析为通用的 Value
|
|
|
|
|
- // let v = response.data.clone();
|
|
|
|
|
- //
|
|
|
|
|
- // info!("准备分发的消息:{}", serde_json::to_string_pretty(&v)?);
|
|
|
|
|
|
|
+ pub async fn dispatch_message(&mut self, response: &Response) -> Result<()> {
|
|
|
|
|
+ // 预解析为通用的 Value
|
|
|
|
|
+ let v = response.data.clone();
|
|
|
|
|
+
|
|
|
|
|
+ // info!("准备分发的消息:{}, {}", serde_json::to_string_pretty(&v)?, response.label);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 获取 topic_info 字段用于路由消息,在该策略中extended可以用label
|
|
|
|
|
+ let topic_info = &response.label;
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 根据 topic_info 的内容进行分发 (match)
|
|
|
|
|
+ if topic_info.contains("ExtendedBestPrices") {
|
|
|
|
|
+ self.process_best_prices(&v).await?;
|
|
|
|
|
+ } else if topic_info.contains("spot@public.aggre.depth.v3.api.pb") {
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果是未知的 topic,返回一个错误
|
|
|
|
|
+ bail!("Received a message with an unknown topic_info: {}", topic_info);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub async fn process_best_prices(&mut self, value: &Value) -> Result<()> {
|
|
|
|
|
+ // 预先捕获整个 Value 的字符串表示,用于错误报告
|
|
|
|
|
+ let value_str = serde_json::to_string(&value).unwrap_or_else(|_| "无法序列化 JSON Value".to_string());
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试获取 data 字段
|
|
|
|
|
+ let data = value.get("data")
|
|
|
|
|
+ .ok_or_else(|| anyhow!("获取 'data' 字段失败,原始 JSON: {}", value_str))?;
|
|
|
|
|
|
|
|
- // 2. 获取 topic_info 字段用于路由
|
|
|
|
|
- // and_then 确保了 get 返回 Some 时才调用 as_str
|
|
|
|
|
- // context 在任何一步失败时提供错误信息 (字段不存在,或不是字符串)
|
|
|
|
|
- // let topic_info = v
|
|
|
|
|
- // .get("topic_info")
|
|
|
|
|
- // .and_then(Value::as_str)
|
|
|
|
|
- // .context("Message is missing 'topic_info' field or it's not a string")?;
|
|
|
|
|
- //
|
|
|
|
|
- // // 3. 根据 topic_info 的内容进行分发 (match)
|
|
|
|
|
- // if topic_info.contains("spot@public.kline.v3.api.pb") {
|
|
|
|
|
- // // 如果是K线数据,调用 process_kline
|
|
|
|
|
- // self.process_klines(&v).await?;
|
|
|
|
|
- // } else if topic_info.contains("spot@public.aggre.depth.v3.api.pb") {
|
|
|
|
|
- // // 如果是增量深度数据,调用 process_depth_update
|
|
|
|
|
- // self.process_depth_update(&v).await?;
|
|
|
|
|
- // } else {
|
|
|
|
|
- // // 如果是未知的 topic,返回一个错误
|
|
|
|
|
- // bail!("Received a message with an unknown topic_info: {}", topic_info);
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ // 尝试从 data 中获取 "a" (asks) 数组
|
|
|
|
|
+ let asks_array = data.get("a")
|
|
|
|
|
+ .and_then(|v| v.as_array()) // and_then 链式调用,确保只有当 v 存在且是数组时才继续
|
|
|
|
|
+ .ok_or_else(|| anyhow!("获取 'data.a' 数组失败,原始 JSON: {}", value_str))?;
|
|
|
|
|
|
|
|
|
|
+ // 尝试从 data 中获取 "b" (bids) 数组
|
|
|
|
|
+ let bids_array = data.get("b")
|
|
|
|
|
+ .and_then(|v| v.as_array())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("获取 'data.b' 数组失败,原始 JSON: {}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ // 如若有发送asks信息
|
|
|
|
|
+ if asks_array.len() > 0 {
|
|
|
|
|
+ let ask_item = &asks_array[0];
|
|
|
|
|
+ let p = ask_item.get("p")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("获取 'data.a.p' 字符串失败,原始 JSON: {}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ self.best_ask = Decimal::from_str(p)
|
|
|
|
|
+ .map_err(|e| anyhow!("将价格字符串 '{}' 解析为 Decimal 失败: {},原始 JSON: {}", p, e, value_str))?;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如若有发送bids信息
|
|
|
|
|
+ if bids_array.len() > 0 {
|
|
|
|
|
+ let bid_item = &bids_array[0];
|
|
|
|
|
+ let p = bid_item.get("p")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("获取 'data.b.p' 字符串失败,原始 JSON: {}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ self.best_bid = Decimal::from_str(p)
|
|
|
|
|
+ .map_err(|e| anyhow!("将价格字符串 '{}' 解析为 Decimal 失败: {},原始 JSON: {}", p, e, value_str))?;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ info!("{}, {}", self.best_ask, self.best_bid);
|
|
|
|
|
+
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|