|
@@ -2,13 +2,17 @@ use std::collections::{BTreeMap, HashMap};
|
|
|
use std::ops::Div;
|
|
use std::ops::Div;
|
|
|
use std::str::FromStr;
|
|
use std::str::FromStr;
|
|
|
use std::thread;
|
|
use std::thread;
|
|
|
|
|
+use std::thread::{sleep};
|
|
|
use std::time::Duration;
|
|
use std::time::Duration;
|
|
|
use chrono::{Timelike, Utc};
|
|
use chrono::{Timelike, Utc};
|
|
|
use rust_decimal::Decimal;
|
|
use rust_decimal::Decimal;
|
|
|
use rust_decimal_macros::dec;
|
|
use rust_decimal_macros::dec;
|
|
|
use global::public_params::{ASK_PRICE_INDEX, BID_PRICE_INDEX, LENGTH};
|
|
use global::public_params::{ASK_PRICE_INDEX, BID_PRICE_INDEX, LENGTH};
|
|
|
-use standard::{SpecialDepth, SpecialTicker, Ticker};
|
|
|
|
|
-use crate::model::{OrderCommand, OrderInfo, Position, TraderMsg};
|
|
|
|
|
|
|
+use standard::{Market, Platform, Position, PositionModeEnum, SpecialDepth, SpecialTicker, Ticker};
|
|
|
|
|
+use standard::exchange::Exchange;
|
|
|
|
|
+use standard::exchange::ExchangeEnum::GateSwap;
|
|
|
|
|
+
|
|
|
|
|
+use crate::model::{LocalPosition, OrderCommand, OrderInfo, TraderMsg};
|
|
|
use crate::params::Params;
|
|
use crate::params::Params;
|
|
|
use crate::predictor::Predictor;
|
|
use crate::predictor::Predictor;
|
|
|
use crate::strategy::Strategy;
|
|
use crate::strategy::Strategy;
|
|
@@ -44,9 +48,9 @@ pub struct Quant {
|
|
|
// 本地币保证金
|
|
// 本地币保证金
|
|
|
pub local_coin: Decimal,
|
|
pub local_coin: Decimal,
|
|
|
// 仓位信息
|
|
// 仓位信息
|
|
|
- pub local_position: Position,
|
|
|
|
|
|
|
+ pub local_position: LocalPosition,
|
|
|
// 仓位信息-自订单
|
|
// 仓位信息-自订单
|
|
|
- pub local_position_by_orders: Position,
|
|
|
|
|
|
|
+ pub local_position_by_orders: LocalPosition,
|
|
|
//
|
|
//
|
|
|
pub local_buy_amount: Decimal,
|
|
pub local_buy_amount: Decimal,
|
|
|
pub local_sell_amount: Decimal,
|
|
pub local_sell_amount: Decimal,
|
|
@@ -81,6 +85,7 @@ pub struct Quant {
|
|
|
pub trade_name: String,
|
|
pub trade_name: String,
|
|
|
pub ready: i8,
|
|
pub ready: i8,
|
|
|
pub predictor: Predictor,
|
|
pub predictor: Predictor,
|
|
|
|
|
+ pub market: Market
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
struct MarketData{
|
|
struct MarketData{
|
|
@@ -95,7 +100,7 @@ impl Quant {
|
|
|
let mut quant_obj = Quant {
|
|
let mut quant_obj = Quant {
|
|
|
params: params.clone(),
|
|
params: params.clone(),
|
|
|
start_time: 0,
|
|
start_time: 0,
|
|
|
- symbol,
|
|
|
|
|
|
|
+ symbol: symbol.clone(),
|
|
|
base: pairs[0].to_string(),
|
|
base: pairs[0].to_string(),
|
|
|
quote: pairs[1].to_string(),
|
|
quote: pairs[1].to_string(),
|
|
|
hold_coin: clip(params.hold_coin, dec!(0.0), dec!(10000.0)),
|
|
hold_coin: clip(params.hold_coin, dec!(0.0), dec!(10000.0)),
|
|
@@ -107,13 +112,13 @@ impl Quant {
|
|
|
local_profit: Default::default(),
|
|
local_profit: Default::default(),
|
|
|
local_cash: Default::default(),
|
|
local_cash: Default::default(),
|
|
|
local_coin: Default::default(),
|
|
local_coin: Default::default(),
|
|
|
- local_position: Position{
|
|
|
|
|
|
|
+ local_position: LocalPosition{
|
|
|
long_pos: Default::default(),
|
|
long_pos: Default::default(),
|
|
|
short_pos: Default::default(),
|
|
short_pos: Default::default(),
|
|
|
long_avg: Default::default(),
|
|
long_avg: Default::default(),
|
|
|
short_avg: Default::default(),
|
|
short_avg: Default::default(),
|
|
|
},
|
|
},
|
|
|
- local_position_by_orders: Position{
|
|
|
|
|
|
|
+ local_position_by_orders: LocalPosition{
|
|
|
long_pos: Default::default(),
|
|
long_pos: Default::default(),
|
|
|
short_pos: Default::default(),
|
|
short_pos: Default::default(),
|
|
|
long_avg: Default::default(),
|
|
long_avg: Default::default(),
|
|
@@ -152,6 +157,20 @@ impl Quant {
|
|
|
alpha: vec![],
|
|
alpha: vec![],
|
|
|
gamma: Default::default(),
|
|
gamma: Default::default(),
|
|
|
avg_spread_list: vec![],
|
|
avg_spread_list: vec![],
|
|
|
|
|
+ },
|
|
|
|
|
+ market: Market{
|
|
|
|
|
+ symbol,
|
|
|
|
|
+ base_asset: "".to_string(),
|
|
|
|
|
+ quote_asset: "".to_string(),
|
|
|
|
|
+ tick_size: Default::default(),
|
|
|
|
|
+ amount_size: Default::default(),
|
|
|
|
|
+ price_precision: Default::default(),
|
|
|
|
|
+ amount_precision: Default::default(),
|
|
|
|
|
+ min_qty: Default::default(),
|
|
|
|
|
+ max_qty: Default::default(),
|
|
|
|
|
+ min_notional: Default::default(),
|
|
|
|
|
+ max_notional: Default::default(),
|
|
|
|
|
+ ct_val: Default::default(),
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
for i in 0..=params.ref_exchange.len()-1 {
|
|
for i in 0..=params.ref_exchange.len()-1 {
|
|
@@ -416,6 +435,18 @@ impl Quant {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ pub fn update_equity(&mut self, data: Decimal){
|
|
|
|
|
+ /*
|
|
|
|
|
+ 更新保证金信息
|
|
|
|
|
+ 合约一直更新
|
|
|
|
|
+ 现货只有当出现异常时更新
|
|
|
|
|
+ */
|
|
|
|
|
+ if self.exchange.contains("spot"){
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ self.local_cash = data * self.used_pct
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 检测初始数据是否齐全
|
|
// 检测初始数据是否齐全
|
|
|
pub fn check_ready(&mut self){
|
|
pub fn check_ready(&mut self){
|
|
|
// 检查 ticker 行情
|
|
// 检查 ticker 行情
|
|
@@ -519,10 +550,48 @@ impl Quant {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub fn update_position(&mut self, data: Position){
|
|
|
|
|
|
|
+ pub fn update_exit(&mut self, data: String){
|
|
|
|
|
+ // 底层触发停机
|
|
|
|
|
+ self.exit_msg = data;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ pub fn stop(&mut self){
|
|
|
|
|
+ self.mode_signal = 80;
|
|
|
|
|
+ // 等strategy onExit 彻底执行完毕 进入沉默状态之后 再进入exit 否则可能导致多处同时操作订单
|
|
|
|
|
+ // 尽量减少大仓位直接take平
|
|
|
|
|
+ // TODO:发起停机 self.loop.create_task(self.exit(delay=10))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub fn exit(self, delay: i8){
|
|
|
|
|
+ println!("预约退出操作 delay: {}", delay);
|
|
|
|
|
+ if delay > 0{
|
|
|
|
|
+ sleep(Duration::from_secs(delay as u64));
|
|
|
|
|
+ }
|
|
|
|
|
+ println!("开始退出操作");
|
|
|
|
|
+ println!("为避免api失效导致遗漏仓位 建议人工复查");
|
|
|
|
|
+ // TODO: 检查仓位 await self.rest.check_position(hold_coin=self.hold_coin)
|
|
|
|
|
+ // stop flag
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub fn update_position(&mut self, data: Vec<Position>){
|
|
|
|
|
+ if data.is_empty(){
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ let mut position = LocalPosition::new();
|
|
|
|
|
+ for pos in data {
|
|
|
|
|
+ if pos.position_mode == PositionModeEnum::Long {
|
|
|
|
|
+ position.long_pos = pos.amount;
|
|
|
|
|
+ position.long_avg = pos.price;
|
|
|
|
|
+ } else if pos.position_mode == PositionModeEnum::Short {
|
|
|
|
|
+ position.short_pos = pos.amount;
|
|
|
|
|
+ position.short_avg = pos.price;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
// 更新仓位信息
|
|
// 更新仓位信息
|
|
|
- if data != self.local_position {
|
|
|
|
|
- self.local_position = data;
|
|
|
|
|
|
|
+ if position != self.local_position {
|
|
|
|
|
+ self.local_position = position;
|
|
|
println!("更新本地仓位:{:?}", self.local_position);
|
|
println!("更新本地仓位:{:?}", self.local_position);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -647,39 +716,64 @@ impl Quant {
|
|
|
return market;
|
|
return market;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- pub fn before_trade(&mut self){
|
|
|
|
|
|
|
+ pub async fn get_exchange_info(&mut self, platform: &Box<dyn Platform + Send + Sync>){
|
|
|
|
|
+ match platform.get_market().await {
|
|
|
|
|
+ Ok(val)=> {
|
|
|
|
|
+ self.market = val
|
|
|
|
|
+ },
|
|
|
|
|
+ Err(e) => {
|
|
|
|
|
+ println!("获取市场信息错误: {:?}", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub async fn get_equity(&mut self, platform: &Box<dyn Platform + Send + Sync>){
|
|
|
|
|
+ match platform.get_account().await {
|
|
|
|
|
+ Ok(val)=> {
|
|
|
|
|
+ self.update_equity(val.balance);
|
|
|
|
|
+ },
|
|
|
|
|
+ Err(e) => {
|
|
|
|
|
+ println!("获取账户信息错误: {:?}", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub async fn before_trade(&mut self){
|
|
|
// 启动ws
|
|
// 启动ws
|
|
|
// 启动交易
|
|
// 启动交易
|
|
|
//TODO: 启动交易盘
|
|
//TODO: 启动交易盘
|
|
|
- // 启动交易盘 self.loop.create_task(self.ws.run(is_auth=1, sub_trade=_sub_trade, sub_fast=0))
|
|
|
|
|
|
|
+ let mut exchange_params = BTreeMap::new();
|
|
|
|
|
+ exchange_params.insert("access_key".to_string(), "your_access_key".to_string());
|
|
|
|
|
+ exchange_params.insert("access_key".to_string(), "your_secret_key".to_string());
|
|
|
|
|
+ let platform:Box<dyn Platform+Send+Sync> = Exchange::new(GateSwap, self.symbol.clone(), false, exchange_params);
|
|
|
|
|
+
|
|
|
for i in &self.ref_name{
|
|
for i in &self.ref_name{
|
|
|
// TODO: 启动参考ws 参考盘口使用fast行情性能消耗更大 使用普通行情可以节省性能
|
|
// TODO: 启动参考ws 参考盘口使用fast行情性能消耗更大 使用普通行情可以节省性能
|
|
|
// self.loop.create_task(self.ws_ref[i].run(is_auth=0, sub_trade=0, sub_fast=_sub_fast))
|
|
// self.loop.create_task(self.ws_ref[i].run(is_auth=0, sub_trade=0, sub_fast=_sub_fast))
|
|
|
}
|
|
}
|
|
|
- thread::sleep(Duration::from_secs(1));
|
|
|
|
|
- // TODO:买入平台币操作 await self.rest.buy_token()
|
|
|
|
|
- // TODO: 清空挂单和仓位 await self.rest.check_position(hold_coin=self.hold_coin)
|
|
|
|
|
- // TODO: 获取市场信息 await self.rest.before_trade()
|
|
|
|
|
- // TODO: 获取价格信息 ticker = await self.rest.get_ticker()
|
|
|
|
|
- let ticker = SpecialTicker{
|
|
|
|
|
- sell: Default::default(),
|
|
|
|
|
- buy: Default::default(),
|
|
|
|
|
- mid_price: Default::default(),
|
|
|
|
|
- };
|
|
|
|
|
- let mp = ticker.mid_price;
|
|
|
|
|
- // TODO:获取账户信息 await self.rest.get_equity()
|
|
|
|
|
- // TODO:初始资金 start_cash = self.rest.cash_value * self.used_pct start_coin = self.rest.coin_value * self.used_pct
|
|
|
|
|
- let start_cash = Decimal::ZERO;
|
|
|
|
|
- let start_coin = Decimal::ZERO;
|
|
|
|
|
|
|
+ sleep(Duration::from_secs(1));
|
|
|
|
|
+ // 买入平台币操作 await self.rest.buy_token()
|
|
|
|
|
+ // 获取市场信息
|
|
|
|
|
+ self.get_exchange_info(&platform).await;
|
|
|
|
|
+ // 获取价格信息
|
|
|
|
|
+ let ticker = platform.get_ticker().await.expect("获取价格信息异常!");
|
|
|
|
|
+ let mp = (ticker.buy + ticker.sell) / Decimal::TWO;
|
|
|
|
|
+ // 获取账户信息
|
|
|
|
|
+ self.get_equity(&platform).await;
|
|
|
|
|
+ // 初始资金
|
|
|
|
|
+ let start_cash = self.local_cash.clone();
|
|
|
|
|
+ let start_coin = self.local_cash.clone();
|
|
|
if start_cash.is_zero() && start_coin.is_zero(){
|
|
if start_cash.is_zero() && start_coin.is_zero(){
|
|
|
self.exit_msg = format!("{}{}{}{}","初始为零 cash: ", start_cash, " coin: ", start_coin);
|
|
self.exit_msg = format!("{}{}{}{}","初始为零 cash: ", start_cash, " coin: ", start_coin);
|
|
|
// TODO: 停止程序 self.stop()
|
|
// TODO: 停止程序 self.stop()
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- // self.logger.info(f"初始cash: {start_cash} 初始coin: {start_coin}")
|
|
|
|
|
|
|
+ println!("初始cash: {start_cash} 初始coin: {start_coin}");
|
|
|
// 初始化策略基础信息
|
|
// 初始化策略基础信息
|
|
|
if mp <= dec!(0){
|
|
if mp <= dec!(0){
|
|
|
self.exit_msg = format!("{}{}","初始价格获取错误: ", mp);
|
|
self.exit_msg = format!("{}{}","初始价格获取错误: ", mp);
|
|
|
|
|
+ // TODO: 停止程序 self.stop()
|
|
|
|
|
+ return;
|
|
|
} else {
|
|
} else {
|
|
|
println!("初始价格为 {}", mp);
|
|
println!("初始价格为 {}", mp);
|
|
|
}
|
|
}
|
|
@@ -690,10 +784,16 @@ impl Quant {
|
|
|
self.strategy.max_equity = self.strategy.start_equity.clone();
|
|
self.strategy.max_equity = self.strategy.start_equity.clone();
|
|
|
self.strategy.equity = self.strategy.start_equity.clone();
|
|
self.strategy.equity = self.strategy.start_equity.clone();
|
|
|
self.strategy.total_amount = self.strategy.equity * self.strategy.lever_rate / self.strategy.mp;
|
|
self.strategy.total_amount = self.strategy.equity * self.strategy.lever_rate / self.strategy.mp;
|
|
|
- // TODO: 获取数量精度 self.strategy.stepSize = self.rest.stepSize if self.rest.stepSize < 1.0 else int(self.rest.stepSize)
|
|
|
|
|
- self.strategy.step_size = Decimal::ZERO;
|
|
|
|
|
- // TODO: 获取价格精度 self.rest.tickSize if self.rest.tickSize < 1.0 else int(self.rest.tickSize)
|
|
|
|
|
- self.strategy.tick_size = Decimal::ZERO;
|
|
|
|
|
|
|
+ // 获取数量精度
|
|
|
|
|
+ self.strategy.step_size = self.market.amount_size.clone();
|
|
|
|
|
+ if self.strategy.step_size < Decimal::ONE{
|
|
|
|
|
+ self.strategy.step_size = self.strategy.step_size.trunc();
|
|
|
|
|
+ }
|
|
|
|
|
+ // 获取价格精度 self.rest.tickSize if self.rest.tickSize < 1.0 else int(self.rest.tickSize)
|
|
|
|
|
+ self.strategy.tick_size = self.market.tick_size.clone();
|
|
|
|
|
+ if self.strategy.tick_size < Decimal::ONE{
|
|
|
|
|
+ self.strategy.tick_size = self.strategy.tick_size.trunc();
|
|
|
|
|
+ }
|
|
|
if self.strategy.step_size.is_zero() || self.strategy.tick_size.is_zero(){
|
|
if self.strategy.step_size.is_zero() || self.strategy.tick_size.is_zero(){
|
|
|
self.exit_msg = format!("{}{}{}{}","交易精度未正常获取 step_size: ", self.strategy.step_size, " tick_size:", self.strategy.tick_size);
|
|
self.exit_msg = format!("{}{}{}{}","交易精度未正常获取 step_size: ", self.strategy.step_size, " tick_size:", self.strategy.tick_size);
|
|
|
// TODO: 停止程序 self.stop()
|
|
// TODO: 停止程序 self.stop()
|
|
@@ -795,4 +895,6 @@ mod tests {
|
|
|
println!("long_one_hand_amount:{:?}, short_one_hand_amount: {:?}", long_one_hand_amount, short_one_hand_amount);
|
|
println!("long_one_hand_amount:{:?}, short_one_hand_amount: {:?}", long_one_hand_amount, short_one_hand_amount);
|
|
|
println!("{:?},{:?},{:?},{:?},{:?}", short_one_hand_value, mp, step_size, (short_one_hand_value/mp/step_size), ((short_one_hand_value/mp)/step_size).floor())
|
|
println!("{:?},{:?},{:?},{:?},{:?}", short_one_hand_value, mp, step_size, (short_one_hand_value/mp/step_size), ((short_one_hand_value/mp)/step_size).floor())
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
}
|
|
}
|