use std::collections::BTreeMap; use std::str::FromStr; use actix_web::{HttpResponse}; use chrono::Utc; use rust_decimal::Decimal; use rust_decimal::prelude::ToPrimitive; use rust_decimal_macros::dec; use serde_json::{json, Value}; use crate::db_connector::get_trades_json; use crate::params_utils::{get_str, parse_str_to_decimal}; use crate::server::{Response, Trade}; pub fn symbol_fix(symbol: &str) -> String { let mut fixed = symbol.to_uppercase(); if !fixed.contains("_USDT") { fixed = format!("{}_USDT", fixed); } fixed } // 将trades_json转换为指标 pub async fn generate_msv(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 mills_back = match parse_str_to_decimal(query_value.clone(), "mills_back") { Ok(t) => { t } 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 end_time = Utc::now().timestamp_millis(); let start_time = end_time - minute_time_range * 60 * 1000; let db_response = get_trades_json( exchange.as_str(), symbol.as_str(), start_time, end_time, ).await; // 对数据库返回的数据进行容错处理 if db_response.code == 200 { // 数据本地化处理 let trades = parse_json_to_trades(db_response.data); // 指标生成 let indicator = generate_msv_by_trades(trades, mills_back); // 返回数据 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(&db_response).unwrap(); HttpResponse::Ok().content_type("application/json").body(json_string) } } // 将trades转换为具体指标 pub fn generate_msv_by_trades(mut trades: Vec, mills_back: Decimal) -> Value { let mut amplitude_map: BTreeMap = BTreeMap::new(); const GAMMA: Decimal = dec!(0.5); // 每一个元素都遍历一遍 trades.sort_by(|a, b| Decimal::from_str(a.id.as_str()).unwrap().cmp(&Decimal::from_str(b.id.as_str()).unwrap())); for (index, trade) in trades.iter().enumerate() { // 该元素向前遍历range毫秒 let mut range_index = if index == 0 { 0 } else { index }; // 计算区间的预定价格 let mut ref_price = trade.price; loop { // 第0个就不搞 if range_index == 0 { break; } let flag_trade = trades.get(range_index).unwrap(); let range_time = trade.time - flag_trade.time; // 判断该ticker是否是range ms以外 if range_time > mills_back { break; } ref_price = ref_price * GAMMA + flag_trade.price * (Decimal::ONE - GAMMA); range_index -= 1; } // 逻辑计算层 // 取离当前点最远的点进行测量 let last_price = trade.price; // 不是初始值,以及不是0波动 if index != 0 { let mut rate = Decimal::ONE_HUNDRED * (last_price - ref_price) / ref_price; rate.rescale(2); // 去除小数位之后,可以忽略一些太小的波动,减少图表生成压力 if rate.eq(&Decimal::ZERO) { continue } amplitude_map.insert(trade.time, rate); } } let x: Vec = amplitude_map.keys().cloned().collect(); let y: Vec = amplitude_map.values().cloned().collect(); let total_size = trades.len(); let result_size = x.len(); json!({ "x": x, "y": y, "total_size": total_size, "result_size": result_size, }) } // 将json转换为trades pub fn parse_json_to_trades(trades_json: Value) -> Vec { let mut rst = vec![]; for trade_json in trades_json.as_array().unwrap() { let arr = trade_json.as_array().unwrap(); rst.push(Trade { id: arr[0].as_str().unwrap().to_string(), time: Decimal::from_str(arr[1].as_str().unwrap()).unwrap(), size: Decimal::from_str(arr[2].as_str().unwrap()).unwrap(), price: Decimal::from_str(arr[3].as_str().unwrap()).unwrap(), }); } rst }