|
@@ -0,0 +1,251 @@
|
|
|
|
|
+use std::collections::{HashMap};
|
|
|
|
|
+use chrono::{Timelike, Utc};
|
|
|
|
|
+use rust_decimal::Decimal;
|
|
|
|
|
+use rust_decimal_macros::dec;
|
|
|
|
|
+use standard::{Ticker};
|
|
|
|
|
+use crate::model::{Position, TraderMsg};
|
|
|
|
|
+use crate::params::Params;
|
|
|
|
|
+use crate::utils::{clip, LENGTH};
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Debug)]
|
|
|
|
|
+pub struct Quant {
|
|
|
|
|
+ // 启动时间
|
|
|
|
|
+ pub start_time: i64,
|
|
|
|
|
+ // 币对
|
|
|
|
|
+ pub symbol: String,
|
|
|
|
|
+ // 基础货币
|
|
|
|
|
+ pub base: String,
|
|
|
|
|
+ // 报价货币
|
|
|
|
|
+ pub quote: String,
|
|
|
|
|
+ // 现货底仓
|
|
|
|
|
+ pub hold_coin: Decimal,
|
|
|
|
|
+ // 本地挂单表
|
|
|
|
|
+ pub local_orders: HashMap<String, HashMap<String, String>>,
|
|
|
|
|
+ // 本地订单缓存队列
|
|
|
|
|
+ pub local_orders_backup: HashMap<String, HashMap<String, String>>,
|
|
|
|
|
+ // 本地订单缓存cid队列
|
|
|
|
|
+ pub local_orders_backup_cid: Vec<String>,
|
|
|
|
|
+ // 本地已处理cid缓存队列
|
|
|
|
|
+ pub handled_orders_cid: Vec<String>,
|
|
|
|
|
+ // 本地利润值
|
|
|
|
|
+ pub local_profit: Decimal,
|
|
|
|
|
+ // 本地U保证金
|
|
|
|
|
+ pub local_cash: Decimal,
|
|
|
|
|
+ // 本地币保证金
|
|
|
|
|
+ pub local_coin: Decimal,
|
|
|
|
|
+ // 仓位信息
|
|
|
|
|
+ pub local_position: Position,
|
|
|
|
|
+ // 仓位信息-自订单
|
|
|
|
|
+ pub local_position_by_orders: Position,
|
|
|
|
|
+ //
|
|
|
|
|
+ pub local_buy_amount: Decimal,
|
|
|
|
|
+ pub local_sell_amount: Decimal,
|
|
|
|
|
+ pub local_buy_value: Decimal,
|
|
|
|
|
+ pub local_sell_value: Decimal,
|
|
|
|
|
+ pub local_cancel_log: HashMap<String, i64>,
|
|
|
|
|
+ pub interval: Decimal,
|
|
|
|
|
+ pub exchange: String,
|
|
|
|
|
+ pub trade_msg: TraderMsg,
|
|
|
|
|
+ pub exit_msg: String,
|
|
|
|
|
+ // 仓位检查结果序列
|
|
|
|
|
+ pub position_check_series: Vec<i8>,
|
|
|
|
|
+ // 止损大小
|
|
|
|
|
+ pub stop_loss: Decimal,
|
|
|
|
|
+ // 资金使用率
|
|
|
|
|
+ pub used_pct: Decimal,
|
|
|
|
|
+ // 启停信号 0 表示运行 大于1开始倒计时 1时停机
|
|
|
|
|
+ pub mode_signal: i8,
|
|
|
|
|
+ // 交易盘口订单流更新时间
|
|
|
|
|
+ pub trade_order_update_time: u32,
|
|
|
|
|
+ // onTick触发时间记录
|
|
|
|
|
+ pub on_tick_event_time: u32,
|
|
|
|
|
+ // 盘口ticker信息
|
|
|
|
|
+ pub tickers: HashMap<String, Ticker>,
|
|
|
|
|
+ // 盘口 depth信息
|
|
|
|
|
+ pub depths: HashMap<String, Vec<Decimal>>,
|
|
|
|
|
+ // 行情更新延迟监控(风控)
|
|
|
|
|
+ pub market_update_time: HashMap<String, u32>,
|
|
|
|
|
+ pub market_update_interval: HashMap<String, Decimal>,
|
|
|
|
|
+ pub ref_num: i8,
|
|
|
|
|
+ pub ref_name: Vec<String>,
|
|
|
|
|
+ pub trade_name: String,
|
|
|
|
|
+ pub ready: i8
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+struct MarketData{
|
|
|
|
|
+ data: Vec<Decimal>,
|
|
|
|
|
+ name: String
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl Quant {
|
|
|
|
|
+ pub fn new(params: Params) -> Quant{
|
|
|
|
|
+ let symbol = params.pair.clone();
|
|
|
|
|
+ let pairs: Vec<&str> = params.pair.split('_').collect();
|
|
|
|
|
+ let mut quant_obj = Quant {
|
|
|
|
|
+ start_time: 0,
|
|
|
|
|
+ symbol: symbol,
|
|
|
|
|
+ base: pairs[0].to_string(),
|
|
|
|
|
+ quote: pairs[1].to_string(),
|
|
|
|
|
+ hold_coin: clip(params.hold_coin, dec!(0.0), dec!(10000.0)),
|
|
|
|
|
+ local_orders: Default::default(),
|
|
|
|
|
+ local_orders_backup: Default::default(),
|
|
|
|
|
+ local_orders_backup_cid: vec![],
|
|
|
|
|
+ handled_orders_cid: vec![],
|
|
|
|
|
+ local_profit: Default::default(),
|
|
|
|
|
+ local_cash: Default::default(),
|
|
|
|
|
+ local_coin: Default::default(),
|
|
|
|
|
+ local_position: Position{
|
|
|
|
|
+ long_pos: Default::default(),
|
|
|
|
|
+ short_pos: Default::default(),
|
|
|
|
|
+ long_avg: Default::default(),
|
|
|
|
|
+ short_avg: Default::default(),
|
|
|
|
|
+ },
|
|
|
|
|
+ local_position_by_orders: Position{
|
|
|
|
|
+ long_pos: Default::default(),
|
|
|
|
|
+ short_pos: Default::default(),
|
|
|
|
|
+ long_avg: Default::default(),
|
|
|
|
|
+ short_avg: Default::default(),
|
|
|
|
|
+ },
|
|
|
|
|
+ local_buy_amount: Default::default(),
|
|
|
|
|
+ local_sell_amount: Default::default(),
|
|
|
|
|
+ local_buy_value: Default::default(),
|
|
|
|
|
+ local_sell_value: Default::default(),
|
|
|
|
|
+ local_cancel_log: Default::default(),
|
|
|
|
|
+ interval: params.interval,
|
|
|
|
|
+ exchange: params.exchange,
|
|
|
|
|
+ trade_msg: TraderMsg::new(),
|
|
|
|
|
+ exit_msg: "正常退出".to_string(),
|
|
|
|
|
+ position_check_series: vec![],
|
|
|
|
|
+ stop_loss: params.stop_loss,
|
|
|
|
|
+ used_pct: params.used_pct,
|
|
|
|
|
+ mode_signal: 0,
|
|
|
|
|
+ trade_order_update_time: Utc::now().nanosecond(),
|
|
|
|
|
+ on_tick_event_time: Utc::now().nanosecond(),
|
|
|
|
|
+ tickers: Default::default(),
|
|
|
|
|
+ depths: Default::default(),
|
|
|
|
|
+ market_update_time: Default::default(),
|
|
|
|
|
+ market_update_interval: Default::default(),
|
|
|
|
|
+ ref_num: params.ref_exchange.len() as i8,
|
|
|
|
|
+ ref_name: Default::default(),
|
|
|
|
|
+ trade_name: "".to_string(),
|
|
|
|
|
+ ready: 0
|
|
|
|
|
+ };
|
|
|
|
|
+ for i in 0..=params.ref_exchange.len()-1 {
|
|
|
|
|
+ // 拼接不会消耗原字符串
|
|
|
|
|
+ let tickers_key: String = format!("{}{}{}{}", params.ref_exchange[i], "@" , params.ref_pair[i], "@ref");
|
|
|
|
|
+ let ref_name_element = tickers_key.clone();
|
|
|
|
|
+ let depths_key: String = tickers_key.clone();
|
|
|
|
|
+ let market_update_time_key = tickers_key.clone();
|
|
|
|
|
+ let market_update_interval_key = tickers_key.clone();
|
|
|
|
|
+
|
|
|
|
|
+ quant_obj.tickers.insert(tickers_key, Ticker {
|
|
|
|
|
+ time: 0,
|
|
|
|
|
+ high: Default::default(),
|
|
|
|
|
+ low: Default::default(),
|
|
|
|
|
+ sell: Default::default(),
|
|
|
|
|
+ buy: Default::default(),
|
|
|
|
|
+ last: Default::default(),
|
|
|
|
|
+ volume: Default::default(),
|
|
|
|
|
+ });
|
|
|
|
|
+ quant_obj.ref_name.push(ref_name_element);
|
|
|
|
|
+ quant_obj.depths.insert(depths_key, Vec::new());
|
|
|
|
|
+ quant_obj.market_update_time.insert(market_update_time_key, 0);
|
|
|
|
|
+ quant_obj.market_update_interval.insert(market_update_interval_key, dec!(0));
|
|
|
|
|
+ }
|
|
|
|
|
+ return quant_obj;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检测初始数据是否齐全
|
|
|
|
|
+ pub fn check_ready(mut self){
|
|
|
|
|
+ // 检查 ticker 行情
|
|
|
|
|
+ for i in &self.ref_name{
|
|
|
|
|
+ if self.tickers.is_empty() || !self.tickers.contains_key(i) {
|
|
|
|
|
+ println!("参考盘口ticker未准备好");
|
|
|
|
|
+ return;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if self.tickers.get(i).unwrap().buy == dec!(0) || self.tickers.get(i).unwrap().sell == dec!(0){
|
|
|
|
|
+ println!("参考盘口ticker未准备好");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if self.tickers.contains_key(&self.trade_name){
|
|
|
|
|
+ if self.tickers.get(&self.trade_name).unwrap().buy == dec!(0) || self.tickers.get(&self.trade_name).unwrap().sell == dec!(0) {
|
|
|
|
|
+ println!("参考盘口ticker未准备好");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ println!("交易盘口ticker未准备好");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 检查 market 行情
|
|
|
|
|
+ let all_market:Vec<Decimal> = get_all_market_data();
|
|
|
|
|
+ if all_market.len() != LENGTH*(1usize+self.ref_num as usize){
|
|
|
|
|
+ println!("聚合行情未准备好");
|
|
|
|
|
+ return;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ println!("聚合行情准备就绪");
|
|
|
|
|
+ self.trade_msg.market = all_market;
|
|
|
|
|
+ //
|
|
|
|
|
+ // TODO: 模型计算 (self.Predictor.onTime(all_market))
|
|
|
|
|
+ //
|
|
|
|
|
+ }
|
|
|
|
|
+ self.ready = 1
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub fn _update_depth(&mut self){
|
|
|
|
|
+ // 要从回调传入的深度信息中获取data.name
|
|
|
|
|
+ let name = "".to_string();
|
|
|
|
|
+ let market_update_interval_key = name.clone();
|
|
|
|
|
+ let market_update_time_key = name.clone();
|
|
|
|
|
+ let depths1_key = name.clone();
|
|
|
|
|
+ let depths2_key = name.clone();
|
|
|
|
|
+
|
|
|
|
|
+ let now_time = Utc::now().nanosecond();
|
|
|
|
|
+ if self.market_update_time.contains_key(&name) && *self.market_update_time.get(&name).unwrap() != 0u32{
|
|
|
|
|
+ let interval = Decimal::from(now_time - self.market_update_time.get(&name).unwrap());
|
|
|
|
|
+ if *self.market_update_interval.get(&name).unwrap() == dec!(0){
|
|
|
|
|
+ self.market_update_interval.insert(market_update_interval_key, interval);
|
|
|
|
|
+ }else{
|
|
|
|
|
+ let value = self.market_update_interval.get(&name).unwrap();
|
|
|
|
|
+ self.market_update_interval.insert(market_update_interval_key, value*dec!(0.999) + interval*dec!(0.001));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ self.market_update_time.insert(market_update_time_key, now_time);
|
|
|
|
|
+ // 初始化depths
|
|
|
|
|
+ if self.depths.get(&name).unwrap().is_empty() {
|
|
|
|
|
+ // 要从回调传入的深度信息中获取data.data
|
|
|
|
|
+ self.depths.insert(depths1_key, vec![]);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 判断是否需要触发ondepth
|
|
|
|
|
+ // 是否是交易盘口
|
|
|
|
|
+ if name == self.trade_name{
|
|
|
|
|
+ // 更新depths
|
|
|
|
|
+ self.depths.insert(depths2_key, vec![]);
|
|
|
|
|
+ // 允许交易
|
|
|
|
|
+ if self.mode_signal == 0 && self.ready == 1 {
|
|
|
|
|
+ // TODO: 聚合行情处理 self.on_agg_market()
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if name == self.ref_name[self.strategy.ref_index] { // 判断是否为当前跟踪的盘口
|
|
|
|
|
+ // 判断是否需要触发ontick 对行情进行过滤
|
|
|
|
|
+ // 过滤条件 价格变化很大 时间间隔很长
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+// 获取深度信息
|
|
|
|
|
+pub fn get_all_market_data() -> Vec<Decimal>{
|
|
|
|
|
+ return Vec::new();
|
|
|
|
|
+}
|
|
|
|
|
+#[cfg(test)]
|
|
|
|
|
+mod tests {
|
|
|
|
|
+ use crate::params::Params;
|
|
|
|
|
+ use crate::quant::Quant;
|
|
|
|
|
+
|
|
|
|
|
+ #[tokio::test]
|
|
|
|
|
+ async fn test_new_exchange() {
|
|
|
|
|
+ let _params = Params::new("config.toml").unwrap();
|
|
|
|
|
+ let quant: Quant = Quant::new(_params);
|
|
|
|
|
+ println!("属性:{:?}", quant);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|