|
|
@@ -34,73 +34,99 @@ pub fn export_html(export_info: Vec<ExportExchangeTickerInfo>, start_at: &str, e
|
|
|
// 创建 Handlebars 实例
|
|
|
let mut handlebars = Handlebars::new();
|
|
|
|
|
|
- let mut name_list: Vec<String> = vec![];
|
|
|
- let series_info: Vec<SeriesInfo> = export_info.iter().map(|export_exchange_ticker_info| {
|
|
|
- name_list.push(format!("'{}'", export_exchange_ticker_info.name));
|
|
|
- let mut ticker_info = vec![];
|
|
|
- for trades in export_exchange_ticker_info.ticker_info.clone() {
|
|
|
- // 赋值原ticker数据
|
|
|
- ticker_info.push(trades.clone());
|
|
|
|
|
|
- // 计算交易量差
|
|
|
- let mut volume = Decimal::ONE;
|
|
|
- let mut short_volume = Decimal::ONE;
|
|
|
- let mut long_volume = Decimal::ONE;
|
|
|
- let mut sum_volume = Decimal::ONE;
|
|
|
- let recall_ticker_info: Vec<Trades> = export_exchange_ticker_info.recall_ticker_info.iter().filter(|recall_trades| {
|
|
|
- let recall_create_time = Decimal::from_str(&recall_trades.create_time).unwrap();
|
|
|
- let create_time = Decimal::from_str(&trades.create_time).unwrap();
|
|
|
- let recall_time = Decimal::from_i64(config.recall_time).unwrap();
|
|
|
- recall_create_time <= create_time && create_time - recall_create_time <= recall_time
|
|
|
- }).cloned().collect();
|
|
|
- for recall_trades in recall_ticker_info.clone() {
|
|
|
- let size = Decimal::from_str(&recall_trades.size).unwrap();
|
|
|
- volume += size;
|
|
|
- sum_volume += size.abs();
|
|
|
- if size > dec!(0) { long_volume += size } else { short_volume += size.abs() }
|
|
|
- };
|
|
|
- if long_volume / sum_volume >= config.long_volume_rate || short_volume / sum_volume >= config.short_volume_rate {
|
|
|
- // 新增订单流主动性数据
|
|
|
- let max_price = Decimal::from_str(&export_exchange_ticker_info.max_price.clone()).unwrap() * dec!(1.005);
|
|
|
- ticker_info.push(Trades {
|
|
|
- id: Uuid::new_v4().to_string()[0..8].to_string(),
|
|
|
- data_type: "recall".to_string(),
|
|
|
- symbol: trades.symbol,
|
|
|
- create_time: trades.create_time,
|
|
|
- size: volume.to_string(),
|
|
|
- price: max_price.to_string(),
|
|
|
- });
|
|
|
+ let mut max_price = Decimal::ONE;
|
|
|
+ for item in export_info.clone() {
|
|
|
+ let exchange_max_price = Decimal::from_str(&item.max_price).unwrap();
|
|
|
+ max_price = if max_price < exchange_max_price { exchange_max_price } else { max_price };
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut initiative_info: Vec<Trades> = export_info.iter().flat_map(|exchange| exchange.ticker_info.clone()).collect();
|
|
|
+ initiative_info.sort_by(|a, b| a.create_time.cmp(&b.create_time));
|
|
|
+
|
|
|
+ let mut recall_initiative_info: Vec<Trades> = export_info.iter().flat_map(|exchange| exchange.recall_ticker_info.clone()).collect();
|
|
|
+ recall_initiative_info.sort_by(|a, b| a.create_time.cmp(&b.create_time));
|
|
|
+
|
|
|
+ let mut ticker_info = vec![];
|
|
|
+ let mut last_bool = "";
|
|
|
+ for trades in initiative_info {
|
|
|
+ // 计算交易量差
|
|
|
+ let mut volume = Decimal::ONE;
|
|
|
+ let mut short_volume = Decimal::ONE;
|
|
|
+ let mut long_volume = Decimal::ONE;
|
|
|
+ let mut sum_volume = Decimal::ONE;
|
|
|
+ let recall_ticker_info: Vec<Trades> = recall_initiative_info.iter().filter(|recall_trades| {
|
|
|
+ let recall_create_time = Decimal::from_str(&recall_trades.create_time).unwrap();
|
|
|
+ let create_time = Decimal::from_str(&trades.create_time).unwrap();
|
|
|
+ let recall_time = Decimal::from_i64(config.recall_time).unwrap();
|
|
|
+ recall_create_time <= create_time && create_time - recall_create_time <= recall_time
|
|
|
+ }).cloned().collect();
|
|
|
+ for recall_trades in recall_ticker_info.clone() {
|
|
|
+ let size = Decimal::from_str(&recall_trades.size).unwrap();
|
|
|
+ volume += size;
|
|
|
+ sum_volume += size.abs();
|
|
|
+ if size > dec!(0) { long_volume += size } else { short_volume += size.abs() }
|
|
|
+ };
|
|
|
+ let long_volume_bool = long_volume / sum_volume >= config.long_volume_rate;
|
|
|
+ let short_volume_bool = short_volume / sum_volume >= config.short_volume_rate;
|
|
|
+
|
|
|
+ if (long_volume_bool && last_bool != "long") || (short_volume_bool && last_bool != "short") || (!long_volume_bool && !short_volume_bool && last_bool != "none") {
|
|
|
+ // 新增订单流主动性数据
|
|
|
+ let max_price = max_price * dec!(1.005);
|
|
|
+ if long_volume_bool {
|
|
|
+ last_bool = "long";
|
|
|
}
|
|
|
- }
|
|
|
- // 对订单流主动性数据(recall)去重
|
|
|
- let mut ticker_set = std::collections::HashSet::new();
|
|
|
- let mut end_ticker_info = vec![];
|
|
|
- for trades in ticker_info {
|
|
|
- if ticker_set.insert((trades.data_type.clone(), trades.create_time.clone())) {
|
|
|
- end_ticker_info.push(trades.clone());
|
|
|
+ if short_volume_bool {
|
|
|
+ last_bool = "short";
|
|
|
+ }
|
|
|
+ if !long_volume_bool && !short_volume_bool {
|
|
|
+ last_bool = "none";
|
|
|
}
|
|
|
+ ticker_info.push(Trades {
|
|
|
+ id: Uuid::new_v4().to_string()[0..8].to_string(),
|
|
|
+ data_type: last_bool.to_string(),
|
|
|
+ symbol: trades.symbol,
|
|
|
+ create_time: trades.create_time,
|
|
|
+ size: volume.to_string(),
|
|
|
+ price: max_price.to_string(),
|
|
|
+ });
|
|
|
}
|
|
|
- SeriesInfo {
|
|
|
- name: export_exchange_ticker_info.name.clone(),
|
|
|
- data: end_ticker_info.clone(),
|
|
|
+ }
|
|
|
+ // 对订单流主动性数据(recall)去重
|
|
|
+ let mut ticker_set = std::collections::HashSet::new();
|
|
|
+ let mut end_ticker_info = vec![];
|
|
|
+ for trades in ticker_info {
|
|
|
+ if ticker_set.insert((trades.data_type.clone(), trades.create_time.clone())) {
|
|
|
+ end_ticker_info.push(trades.clone());
|
|
|
}
|
|
|
- }).collect();
|
|
|
+ }
|
|
|
+ let initiative_info = SeriesInfo {
|
|
|
+ name: "主动性".to_string(),
|
|
|
+ data: end_ticker_info.clone(),
|
|
|
+ };
|
|
|
|
|
|
let start_time_d = Decimal::from_str(start_at).unwrap() * dec!(1000) + dec!(8) * dec!(3600000);
|
|
|
let end_time_d = Decimal::from_str(end_at).unwrap() * dec!(1000) + dec!(8) * dec!(3600000);
|
|
|
let start_time = NaiveDateTime::from_timestamp_millis(start_time_d.to_i64().unwrap()).unwrap().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
|
|
let end_time = NaiveDateTime::from_timestamp_millis(end_time_d.to_i64().unwrap()).unwrap().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
|
|
|
|
|
- // let series_info: Vec<_> = export_info.iter().map(|item| {
|
|
|
- // SeriesInfo {
|
|
|
- // name: item.name.clone(),
|
|
|
- // data: item.ticker_info.clone(),
|
|
|
- // }
|
|
|
- // }).collect();
|
|
|
-
|
|
|
+ let mut name_list: Vec<String> = vec![];
|
|
|
+ let mut legend_list: Vec<String> = vec![];
|
|
|
+ let mut series_info: Vec<SeriesInfo> = vec![];
|
|
|
+ for item in export_info.clone() {
|
|
|
+ name_list.push(format!("'{}'", item.name));
|
|
|
+ series_info.push(SeriesInfo {
|
|
|
+ name: item.name.clone(),
|
|
|
+ data: item.ticker_info.clone(),
|
|
|
+ })
|
|
|
+ }
|
|
|
+ series_info.push(initiative_info);
|
|
|
+ for item in series_info.clone() {
|
|
|
+ legend_list.push(format!("'{}'", item.name));
|
|
|
+ }
|
|
|
let data = serde_json::json!({
|
|
|
"chart_title": format!("{} Ticker数据", name_list.join("、").replace("'","")),
|
|
|
- "legend_data": format!("[{}]", name_list.join(", ")),
|
|
|
+ "legend_data": format!("[{}]", legend_list.join(", ")),
|
|
|
"series_info": series_info.clone(),
|
|
|
"symbol": config.symbol.to_uppercase(),
|
|
|
"start_at": start_time,
|
|
|
@@ -234,10 +260,10 @@ pub fn export_html(export_info: Vec<ExportExchangeTickerInfo>, start_at: &str, e
|
|
|
{
|
|
|
value: [{{create_time}},{{price}},{{size}},'{{data_type}}'],
|
|
|
symbolRotate: '{{size}}' > 0 ? '0' : '180',
|
|
|
- symbol: '{{data_type}}' == 'recall' ? 'circle' : 'triangle',
|
|
|
+ symbol: '{{data_type}}' == 'ticker' ? 'triangle' : 'circle',
|
|
|
itemStyle: {
|
|
|
- color: '{{data_type}}' == 'recall' ? 'white' : exchangeColor['{{name}}'.toLocaleLowerCase()],
|
|
|
- borderColor: {{size}} > 0 ? 'green' : 'red',
|
|
|
+ color: '{{data_type}}' == 'ticker' ? exchangeColor['{{name}}'.toLocaleLowerCase()] : 'white',
|
|
|
+ borderColor: '{{data_type}}' == 'long' ? 'green' : '{{data_type}}' == 'short' ? 'red' : '{{data_type}}' == 'none' ? 'black' : {{size}} > 0 ? 'green' : 'red',
|
|
|
borderWidth: 1,
|
|
|
}
|
|
|
},
|