From eb14b154d563b39423994975504b6defdd249c9e Mon Sep 17 00:00:00 2001 From: msincenselee Date: Tue, 26 May 2020 17:38:14 +0800 Subject: [PATCH] =?UTF-8?q?[=E5=A2=9E=E5=BC=BA=E5=8A=9F=E8=83=BD]=20?= =?UTF-8?q?=E5=AE=9E=E6=97=B6=E6=9F=A5=E7=9C=8B=E7=AD=96=E7=95=A5=E5=86=85?= =?UTF-8?q?K=E7=BA=BF=E5=88=87=E7=89=87=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/app/cta_stock/engine.py | 19 +++ vnpy/app/cta_stock/template.py | 5 +- vnpy/app/cta_stock/ui/widget.py | 14 ++ vnpy/app/cta_strategy_pro/engine.py | 23 +++ vnpy/app/cta_strategy_pro/ui/widget.py | 15 ++ vnpy/component/cta_line_bar.py | 200 ++++++++++++++----------- vnpy/gateway/xtp/xtp_gateway.py | 8 +- vnpy/trader/ui/kline/ui_snapshot.py | 14 +- 8 files changed, 202 insertions(+), 96 deletions(-) diff --git a/vnpy/app/cta_stock/engine.py b/vnpy/app/cta_stock/engine.py index 63af14db..426b9b32 100644 --- a/vnpy/app/cta_stock/engine.py +++ b/vnpy/app/cta_stock/engine.py @@ -1221,6 +1221,25 @@ class CtaEngine(BaseEngine): self.write_error(u'保存策略{}数据异常:'.format(strategy_name, str(ex))) self.write_error(traceback.format_exc()) + def get_strategy_snapshot(self, strategy_name): + """实时获取策略的K线切片(比较耗性能)""" + strategy = self.strategies.get(strategy_name, None) + if strategy is None: + return None + + try: + # 5.保存策略切片 + snapshot = strategy.get_klines_snapshot() + if not snapshot: + self.write_log(f'{strategy_name}返回得K线切片数据为空') + return None + return snapshot + + except Exception as ex: + self.write_error(u'获取策略{}切片数据异常:'.format(strategy_name, str(ex))) + self.write_error(traceback.format_exc()) + return None + def save_strategy_snapshot(self, select_name: str = 'ALL'): """ 保存策略K线切片数据 diff --git a/vnpy/app/cta_stock/template.py b/vnpy/app/cta_stock/template.py index a3514069..8bbaf26e 100644 --- a/vnpy/app/cta_stock/template.py +++ b/vnpy/app/cta_stock/template.py @@ -414,8 +414,9 @@ class CtaStockTemplate(CtaTemplate): cancel_seconds = 120 # 撤单时间(秒) # 资金相关 - max_invest_rate = 0.1 # 最大仓位(0~1) - max_invest_margin = 0 # 资金上限 0,不限制 + max_invest_rate = 0.1 # 策略使用账号的最大仓位(0~1) + max_invest_margin = 0 # 策略资金上限, 0,不限制 + max_single_margin = 0 # 策略内,各只股票使用的资金上限 # 是否回测状态 backtesting = False diff --git a/vnpy/app/cta_stock/ui/widget.py b/vnpy/app/cta_stock/ui/widget.py index 47040597..bec1a5a1 100644 --- a/vnpy/app/cta_stock/ui/widget.py +++ b/vnpy/app/cta_stock/ui/widget.py @@ -8,12 +8,14 @@ from vnpy.trader.ui.widget import ( TimeCell, BaseMonitor ) +from vnpy.trader.ui.kline.ui_snapshot import UiSnapshot from ..base import ( APP_NAME, EVENT_CTA_LOG, EVENT_CTA_STOPORDER, EVENT_CTA_STRATEGY ) + from ..engine import CtaEngine @@ -205,6 +207,9 @@ class StrategyManager(QtWidgets.QFrame): save_button = QtWidgets.QPushButton("保存") save_button.clicked.connect(self.save_strategy) + view_button = QtWidgets.QPushButton("K线") + view_button.clicked.connect(self.view_strategy_snapshot) + strategy_name = self._data["strategy_name"] #vt_symbol = self._data["vt_symbol"] class_name = self._data["class_name"] @@ -227,6 +232,7 @@ class StrategyManager(QtWidgets.QFrame): hbox.addWidget(remove_button) hbox.addWidget(reload_button) hbox.addWidget(save_button) + hbox.addWidget(view_button) vbox = QtWidgets.QVBoxLayout() vbox.addWidget(label) @@ -279,8 +285,16 @@ class StrategyManager(QtWidgets.QFrame): self.cta_engine.reload_strategy(self.strategy_name) def save_strategy(self): + """保存策略缓存数据""" self.cta_engine.save_strategy_data(self.strategy_name) + def view_strategy_snapshot(self): + """实时查看策略切片""" + snapshot = self.cta_engine.get_strategy_snapshot(self.strategy_name) + if snapshot is None: + return + ui_snapshot = UiSnapshot() + ui_snapshot.show(snapshot_file="", d=snapshot) class DataMonitor(QtWidgets.QTableWidget): """ diff --git a/vnpy/app/cta_strategy_pro/engine.py b/vnpy/app/cta_strategy_pro/engine.py index 211ab1fe..2841fe2d 100644 --- a/vnpy/app/cta_strategy_pro/engine.py +++ b/vnpy/app/cta_strategy_pro/engine.py @@ -1190,6 +1190,25 @@ class CtaEngine(BaseEngine): self.write_error(u'保存策略{}数据异常:'.format(strategy_name, str(ex))) self.write_error(traceback.format_exc()) + def get_strategy_snapshot(self, strategy_name): + """实时获取策略的K线切片(比较耗性能)""" + strategy = self.strategies.get(strategy_name, None) + if strategy is None: + return None + + try: + # 5.保存策略切片 + snapshot = strategy.get_klines_snapshot() + if not snapshot: + self.write_log(f'{strategy_name}返回得K线切片数据为空') + return None + return snapshot + + except Exception as ex: + self.write_error(u'获取策略{}切片数据异常:'.format(strategy_name, str(ex))) + self.write_error(traceback.format_exc()) + return None + def save_strategy_snapshot(self, select_name: str = 'ALL'): """ 保存策略K线切片数据 @@ -1567,6 +1586,10 @@ class CtaEngine(BaseEngine): holding = self.offset_converter.holdings.get(holding_key, None) if holding is None: continue + if holding.exchange == Exchange.SPD: + continue + if '&' in holding.vt_symbol and (holding.vt_symbol.startswith('SP') or holding.vt_symbol.startswith('STG')): + continue compare_pos[vt_symbol] = OrderedDict( { diff --git a/vnpy/app/cta_strategy_pro/ui/widget.py b/vnpy/app/cta_strategy_pro/ui/widget.py index 935f912d..269bb586 100644 --- a/vnpy/app/cta_strategy_pro/ui/widget.py +++ b/vnpy/app/cta_strategy_pro/ui/widget.py @@ -8,12 +8,14 @@ from vnpy.trader.ui.widget import ( TimeCell, BaseMonitor ) +from vnpy.trader.ui.kline.ui_snapshot import UiSnapshot from ..base import ( APP_NAME, EVENT_CTA_LOG, EVENT_CTA_STOPORDER, EVENT_CTA_STRATEGY ) + from ..engine import CtaEngine @@ -205,6 +207,9 @@ class StrategyManager(QtWidgets.QFrame): save_button = QtWidgets.QPushButton("保存") save_button.clicked.connect(self.save_strategy) + view_button = QtWidgets.QPushButton("K线") + view_button.clicked.connect(self.view_strategy_snapshot) + strategy_name = self._data["strategy_name"] vt_symbol = self._data["vt_symbol"] class_name = self._data["class_name"] @@ -227,6 +232,7 @@ class StrategyManager(QtWidgets.QFrame): hbox.addWidget(remove_button) hbox.addWidget(reload_button) hbox.addWidget(save_button) + hbox.addWidget(view_button) vbox = QtWidgets.QVBoxLayout() vbox.addWidget(label) @@ -279,8 +285,17 @@ class StrategyManager(QtWidgets.QFrame): self.cta_engine.reload_strategy(self.strategy_name) def save_strategy(self): + """保存策略缓存数据""" self.cta_engine.save_strategy_data(self.strategy_name) + self.cta_engine.save_strategy_snapshot(self.strategy_name) + def view_strategy_snapshot(self): + """实时查看策略切片""" + snapshot = self.cta_engine.get_strategy_snapshot(self.strategy_name) + if snapshot is None: + return + ui_snapshot = UiSnapshot() + ui_snapshot.show(snapshot_file="", d=snapshot) class DataMonitor(QtWidgets.QTableWidget): """ diff --git a/vnpy/component/cta_line_bar.py b/vnpy/component/cta_line_bar.py index 7a156f09..ab24cff7 100644 --- a/vnpy/component/cta_line_bar.py +++ b/vnpy/component/cta_line_bar.py @@ -143,11 +143,9 @@ class CtaLineBar(object): # 参数列表,保存了参数的名称 paramList = ['vt_symbol'] - # 参数列表 - def __init__(self, strategy, cb_on_bar, setting=None): - # OnBar事件回调函数 + # on_bar事件回调函数,X周期bar合成完毕时,回调到策略的cb_on_bar接口 self.cb_on_bar = cb_on_bar # 周期变更事件回调函数 @@ -156,27 +154,28 @@ class CtaLineBar(object): # K 线服务的策略 self.strategy = strategy + # 当前商品合约 属性 self.underly_symbol = '' # 商品的短代码 self.price_tick = 1 # 商品的最小价格单位 self.round_n = 4 # round() 小数点的截断数量 + self.is_7x24 = False # 是否7x24小时运行( 一般为数字货币) - self.is_7x24 = False - - # 当前的Tick - self.cur_tick = None - self.last_tick = None - self.cur_datetime = None - self.cur_trading_day = '' - self.cur_price = 0 + # 当前的Tick的信息 + self.cur_tick = None # 当前 onTick()函数接收的 最新的tick + self.last_tick = None # 当前正在合成的 X周期bar 的最后(新)一根tick + self.cur_datetime = None # 当前add_bar()传进来的bar/on_tick()传进来tick 对应的最新时间 + self.cur_trading_day = '' # 当前传入tick/bar对应的交易日 + self.cur_price = 0 # 当前curTick.last_price/add_bar中的bar.close_price传进来的 最新市场价格 # K线保存数据 self.cur_bar = None # K线数据对象,代表最后一根/未走完的bar - self.line_bar = [] # K线缓存数据队列 - self.bar_len = 0 # 当前K线得真实数量 + self.line_bar = [] # K线缓存数据队列(缓存合成完 以及正在合成的bar) + self.bar_len = 0 # 当前K线得真实数量(包含已经合成以及正在合成的bar) self.max_hold_bars = 2000 self.is_first_tick = False # K线的第一条Tick数据 - # (实时运行时,或者addbar小于bar得周期时,不包含最后一根Bar) + # (实时运行时,或者addbar小于bar得周期时,不包含最后一根正在合成的Bar) + # 目标bar合成成功后,才会更新以下序列 self.open_array = np.zeros(self.max_hold_bars) # 与lineBar一致得开仓价清单 self.open_array[:] = np.nan self.high_array = np.zeros(self.max_hold_bars) # 与lineBar一致得最高价清单 @@ -192,14 +191,14 @@ class CtaLineBar(object): self.mid4_array[:] = np.nan self.mid5_array = np.zeros(self.max_hold_bars) # 收盘价*2/开仓价/最高/最低价 的平均价 self.mid5_array[:] = np.nan + # 导出到CSV文件 的目录名 和 要导出的 字段 + self.export_filename = None # 数据要导出的目标文件夹 + self.export_fields = [] # 定义要导出的数据字段 - self.export_filename = None - self.export_fields = [] - - # 创建内部变量 + # 创建本类型bar的内部变量,以及添加所有指标输入参数,到self.paramList列表 self.init_properties() - # 创建初始化指标 + # 初始化定义所有的指标输入参数,以及指标生成的数据 self.init_indicators() # 启动实时得函数 @@ -209,6 +208,7 @@ class CtaLineBar(object): # 注册回调函数 self.cb_dict = {} + self.minute_interval = None # 把各个周期的bar转换为分钟,在first_tick中,用来修正bar为整点分钟周期 if setting: self.set_params(setting) @@ -221,11 +221,13 @@ class CtaLineBar(object): self.minute_interval = 60 elif self.interval == Interval.DAILY: self.minute_interval = 60 * 24 + # 修正精度 if self.price_tick < 1: exponent = decimal.Decimal(str(self.price_tick)) self.round_n = max(abs(exponent.as_tuple().exponent) + 2, 4) self.write_log(f'round_n: {self.round_n}') + # 导入卡尔曼过滤器 if self.para_active_kf: try: @@ -246,37 +248,45 @@ class CtaLineBar(object): self.cb_on_period = cb_func def init_param_list(self): - self.paramList.append('bar_interval') - self.paramList.append('interval') - self.paramList.append('mode') + """初始化添加,本类型bar的内部变量,以及添加所有指标输入参数,到self.paramList列表""" + # ------- 本类型bar的内部变量 --------- + self.paramList.append('name') # K线的名称 + self.paramList.append('bar_interval') # bar的周期数量 + self.paramList.append('interval') # bar的类型 + self.paramList.append('mode') # tick/bar模式 + self.paramList.append('is_7x24') #是否为7X24小时运行的bar(一般为数字货币) + self.paramList.append('price_tick') # 最小跳动,用于处理指数等不一致的价格 + self.paramList.append('underly_symbol') # 短合约, - self.paramList.append('para_pre_len') - self.paramList.append('para_ma1_len') + # ---------- 下方为指标输入参数 --------------- + self.paramList.append('para_pre_len') # 唐其安通道的长度(前高/前低) + + self.paramList.append('para_ma1_len') # 三条均线 self.paramList.append('para_ma2_len') self.paramList.append('para_ma3_len') - self.paramList.append('para_ema1_len') + self.paramList.append('para_ema1_len') # 三条EMA均线 self.paramList.append('para_ema2_len') self.paramList.append('para_ema3_len') self.paramList.append('para_dmi_len') self.paramList.append('para_dmi_max') - self.paramList.append('para_atr1_len') + self.paramList.append('para_atr1_len') # 三个波动率 self.paramList.append('para_atr2_len') self.paramList.append('para_atr3_len') - self.paramList.append('para_vol_len') + self.paramList.append('para_vol_len') # 成交量平均 - self.paramList.append('para_rsi1_len') + self.paramList.append('para_rsi1_len') # 2组 RSI摆动指标 self.paramList.append('para_rsi2_len') - self.paramList.append('para_cmi_len') + self.paramList.append('para_cmi_len') # - self.paramList.append('para_boll_len') - self.paramList.append('para_boll_tb_len') - self.paramList.append('para_boll_std_rate') - self.paramList.append('para_boll2_len') + self.paramList.append('para_boll_len') # 布林通道长度(文华计算方式) + self.paramList.append('para_boll_tb_len') # 布林通道长度(tb计算方式) + self.paramList.append('para_boll_std_rate') # 标准差倍率,一般为2 + self.paramList.append('para_boll2_len') # 第二条布林通道 self.paramList.append('para_boll2_tb_len') self.paramList.append('para_boll2_std_rate') @@ -291,22 +301,22 @@ class CtaLineBar(object): self.paramList.append('para_macd_slow_len') self.paramList.append('para_macd_signal_len') - self.paramList.append('para_active_kf') + self.paramList.append('para_active_kf') # 卡尔曼均线 self.paramList.append('para_sar_step') self.paramList.append('para_sar_limit') - self.paramList.append('para_active_skd') + self.paramList.append('para_active_skd') # 摆动指标 self.paramList.append('para_skd_fast_len') self.paramList.append('para_skd_slow_len') self.paramList.append('para_skd_low') self.paramList.append('para_skd_high') - self.paramList.append('para_active_yb') + self.paramList.append('para_active_yb') # 重心线 self.paramList.append('para_yb_len') self.paramList.append('para_yb_ref') - self.paramList.append('para_golden_n') + self.paramList.append('para_golden_n') # 黄金分割 self.paramList.append('para_active_area') @@ -319,12 +329,7 @@ class CtaLineBar(object): self.paramList.append('para_bd_len') - self.paramList.append('is_7x24') - self.paramList.append('price_tick') - self.paramList.append('underly_symbol') - - self.paramList.append('name') def init_properties(self): """ @@ -361,9 +366,9 @@ class CtaLineBar(object): self.__dict__[key] = state.__dict__[key] def init_indicators(self): - """ 定义所有的指标数据""" + """ 初始化定义所有的指标输入参数,以及指标生成的数据 """ - # 指标参数 + # ------------- 指标输入参数 ------------------ self.para_pre_len = 0 # 20 # 前高前低的周期长度 self.para_ma1_len = 0 # 10 # 第一根MA均线的周期长度 @@ -403,7 +408,7 @@ class CtaLineBar(object): self.para_cci_len = 0 # 计算CCI的K线周期 - self.para_macd_fast_len = 0 # 计算MACD的K线周期 + self.para_macd_fast_len = 0 # 计算MACD的K线周期(26,12,9) self.para_macd_slow_len = 0 # 慢线周期 self.para_macd_signal_len = 0 # 平滑周期 @@ -412,7 +417,7 @@ class CtaLineBar(object): self.para_sar_step = 0 # 抛物线的参数 self.para_sar_limit = 0 # 抛物线参数 - self.para_active_skd = False # 是否激活摆动指标 + self.para_active_skd = False # 是否激活摆动指标 优化的多空动量线 self.para_skd_fast_len = 13 # 摆动指标快线周期1 self.para_skd_slow_len = 8 # 摆动指标慢线周期2 self.para_skd_low = 30 # 摆动指标下限区域 @@ -435,7 +440,7 @@ class CtaLineBar(object): self.para_bd_len = 0 # 波段买卖观测长度 - # K 线的相关计算结果数据 + # --------------- K 线的指标相关计算结果数据 ---------------- self.line_pre_high = [] # K线的前para_pre_len的的最高 self.line_pre_low = [] # K线的前para_pre_len的的最低 @@ -452,13 +457,13 @@ class CtaLineBar(object): self._rt_ma2_atan = None self._rt_ma3_atan = None - self.ma12_count = 0 # ma1 与 ma2 ,金叉/死叉后第几根bar - self.ma13_count = 0 # ma1 与 ma3 ,金叉/死叉后第几根bar - self.ma23_count = 0 # ma2 与 ma3 ,金叉/死叉后第几根bar + self.ma12_count = 0 # ma1 与 ma2 ,金叉/死叉后第几根bar,金叉正数,死叉负数 + self.ma13_count = 0 # ma1 与 ma3 ,金叉/死叉后第几根bar,金叉正数,死叉负数 + self.ma23_count = 0 # ma2 与 ma3 ,金叉/死叉后第几根bar,金叉正数,死叉负数 - self.line_ema1 = [] # K线的EMA1均线,周期是InputEmaLen1,不包含当前bar - self.line_ema2 = [] # K线的EMA2均线,周期是InputEmaLen2,不包含当前bar - self.line_ema3 = [] # K线的EMA3均线,周期是InputEmaLen3,不包含当前bar + self.line_ema1 = [] # K线的EMA1均线,周期是para_ema1_len1,不包含当前bar + self.line_ema2 = [] # K线的EMA2均线,周期是para_ema1_len2,不包含当前bar + self.line_ema3 = [] # K线的EMA3均线,周期是para_ema1_len3,不包含当前bar self._rt_ema1 = None # K线的实时EMA(para_ema1_len) self._rt_ema2 = None # K线的实时EMA(para_ema2_len) @@ -485,9 +490,9 @@ class CtaLineBar(object): self.signal_adx_short = False # 空过滤器条件,做空趋势的判断,ADXR高于前一天,下降动向> inputMM # K线的ATR技术数据 - self.line_atr1 = [] # K线的ATR1,周期为inputAtr1Len - self.line_atr2 = [] # K线的ATR2,周期为inputAtr2Len - self.line_atr3 = [] # K线的ATR3,周期为inputAtr3Len + self.line_atr1 = [] # K线的ATR1,周期为para_atr1_len + self.line_atr2 = [] # K线的ATR2,周期为para_atr2_len + self.line_atr3 = [] # K线的ATR3,周期为para_atr3_len self.cur_atr1 = 0 self.cur_atr2 = 0 @@ -497,22 +502,25 @@ class CtaLineBar(object): self.line_vol_ma = [] # K 线的交易量平均 # K线的RSI计算数据 - self.line_rsi1 = [] # 记录K线对应的RSI数值,只保留inputRsi1Len*8 - self.line_rsi2 = [] # 记录K线对应的RSI数值,只保留inputRsi2Len*8 + self.line_rsi1 = [] # 记录K线对应的RSI数值,只保留para_rsi1_len*8 + self.line_rsi2 = [] # 记录K线对应的RSI数值,只保留para_rsi2_len*8 + self.para_rsi_low = 30 # RSI的最低线 self.para_rsi_high = 70 # RSI的最高线 + self.rsi_top_list = [] # 记录RSI的最高峰,只保留 inputRsiLen个 self.rsi_buttom_list = [] # 记录RSI的最低谷,只保留 inputRsiLen个 self.cur_rsi_top_buttom = {} # 最近的一个波峰/波谷 # K线的CMI计算数据 - self.line_cmi = [] # 记录K线对应的Cmi数值,只保留inputCmiLen*8 + self.line_cmi = [] # 记录K线对应的Cmi数值,只保留para_cmi_len*8 # K线的布林特计算数据 self.line_boll_upper = [] # 上轨 self.line_boll_middle = [] # 中线 self.line_boll_lower = [] # 下轨 self.line_boll_std = [] # 标准差 + self.line_upper_atan = [] self.line_middle_atan = [] self.line_lower_atan = [] @@ -523,17 +531,19 @@ class CtaLineBar(object): self._rt_middle_atan = None self._rt_lower_atan = None - self.cur_upper = 0 # 最后一根K的Boll上轨数值(与MinDiff取整) - self.cur_middle = 0 # 最后一根K的Boll中轨数值(与MinDiff取整) - self.cur_lower = 0 # 最后一根K的Boll下轨数值(与MinDiff取整+1) + self.cur_upper = 0 # 最后一根K的Boll上轨数值(与price_tick取整) + self.cur_middle = 0 # 最后一根K的Boll中轨数值(与price_tick取整) + self.cur_lower = 0 # 最后一根K的Boll下轨数值(与price_tick取整+1) self.line_boll2_upper = [] # 上轨 self.line_boll2_middle = [] # 中线 self.line_boll2_lower = [] # 下轨 self.line_boll2_std = [] # 标准差 + self.line_upper2_atan = [] self.line_middle2_atan = [] self.line_lower2_atan = [] + self._rt_upper2 = None self._rt_middle2 = None self._rt_lower2 = None @@ -541,16 +551,16 @@ class CtaLineBar(object): self._rt_middle2_atan = None self._rt_lower2_atan = None - self.cur_upper2 = 0 # 最后一根K的Boll2上轨数值(与MinDiff取整) - self.cur_middle2 = 0 # 最后一根K的Boll2中轨数值(与MinDiff取整) - self.cur_lower2 = 0 # 最后一根K的Boll2下轨数值(与MinDiff取整+1) + self.cur_upper2 = 0 # 最后一根K的Boll2上轨数值(与price_tick取整) + self.cur_middle2 = 0 # 最后一根K的Boll2中轨数值(与price_tick取整) + self.cur_lower2 = 0 # 最后一根K的Boll2下轨数值(与price_tick取整+1) # K线的KDJ指标计算数据 self.line_k = [] # K为快速指标 self.line_d = [] # D为慢速指标 self.line_j = [] # - self.kdj_top_list = [] # 记录KDJ最高峰,只保留 inputKdjLen个 - self.kdj_buttom_list = [] # 记录KDJ的最低谷,只保留 inputKdjLen个 + self.kdj_top_list = [] # 记录KDJ最高峰,只保留 para_kdj_len个 + self.kdj_buttom_list = [] # 记录KDJ的最低谷,只保留 para_kdj_len个 self.line_rsv = [] # RSV self.cur_kdj_top_buttom = {} # 最近的一个波峰/波谷 self.cur_k = 0 # bar内计算时,最后一个未关闭的bar的实时K值 @@ -620,6 +630,7 @@ class CtaLineBar(object): self.line_skd_sto = [] # 根据RSI演算的STO self.line_sk = [] # 快线 self.line_sd = [] # 慢线 + self.cur_skd_count = 0 # 当前金叉/死叉后累加 self._rt_sk = None # 实时SK值 self._rt_sd = None # 实时SD值 @@ -664,6 +675,7 @@ class CtaLineBar(object): self.line_bd_fast = [] # 波段快线 self.line_bd_slow = [] # 波段慢线 self.cur_bd_count = 0 # 当前波段快线慢线金叉死叉, +金叉计算, - 死叉技术 + self._bd_fast = 0 self._bd_slow = 0 @@ -795,6 +807,7 @@ class CtaLineBar(object): def on_bar(self, bar: BarData): """OnBar事件""" + # 将上一根bar合成完结了,触发本on_bar事件(缓存开高收低等序列,计算各个指标) if not bar.interval: bar.interval = self.interval bar.interval_num = self.bar_interval @@ -804,7 +817,7 @@ class CtaLineBar(object): bar_mid4 = round((2 * bar.close_price + bar.high_price + bar.low_price) / 4, self.round_n) bar_mid5 = round((2 * bar.close_price + bar.open_price + bar.high_price + bar.low_price) / 5, self.round_n) - # 扩展open,close,high,low numpy array列表 + # 扩展open,close,high,low numpy array列表 平移更新序列最新值 self.open_array[:-1] = self.open_array[1:] self.open_array[-1] = bar.open_price @@ -826,7 +839,11 @@ class CtaLineBar(object): self.mid5_array[:-1] = self.mid5_array[1:] self.mid5_array[-1] = bar_mid5 - self.bar_len = len(self.line_bar) + # 计算当前self.line_bar长度,并维持self.line_bar序列在max_hold_bars长度 + self.bar_len = len(self.line_bar) # 当前K线得真实数量(包含已经合成以及正在合成的bar) + if self.bar_len > self.max_hold_bars: + del self.line_bar[0] + self.bar_len = self.bar_len - 1 # 删除了最前面的bar,bar长度少一位 self.__count_pre_high_low() self.__count_ma() @@ -853,9 +870,9 @@ class CtaLineBar(object): self.__count_skdj() self.export_to_csv(bar) - self.rt_executed = False + self.rt_executed = False # 是否 启动实时计算得函数 - # 回调上层调用者 + # 回调上层调用者,将合成的 x分钟bar,回调给策略 def on_bar_x(self, bar: BarData):函数 if self.cb_on_bar: self.cb_on_bar(bar=bar) @@ -890,6 +907,7 @@ class CtaLineBar(object): def export_to_csv(self, bar: BarData): """ 输出到csv文件""" + # 将我们配置在self.export_fields的要输出的 bar信息以及指标信息 ==》输出到csv文件 if self.export_filename is None or len(self.export_fields) == 0: return field_names = [] @@ -911,6 +929,8 @@ class CtaLineBar(object): save_dict[field_name] = 0 else: save_dict[field_name] = list_obj[-1] + elif type_ == 'string': + save_dict[field_name] = getattr(self, str(attr_name), '') else: save_dict[field_name] = getattr(self, str(attr_name), 0) @@ -940,21 +960,21 @@ class CtaLineBar(object): if self.para_ma2_len > 0 and len(self.line_ma2) > 0: msg = msg + u',MA({0}):{1}'.format(self.para_ma2_len, self.line_ma2[-1]) if self.ma12_count == 1: - msg = msg + u'MA{}金叉MA{}'.format(self.para_ma1_len, self.para_ma2_len) + msg = msg + u',MA{}金叉MA{}'.format(self.para_ma1_len, self.para_ma2_len) elif self.ma12_count == -1: - msg = msg + u'MA{}死叉MA{}'.format(self.para_ma1_len, self.para_ma2_len) + msg = msg + u',MA{}死叉MA{}'.format(self.para_ma1_len, self.para_ma2_len) if self.para_ma3_len > 0 and len(self.line_ma3) > 0: msg = msg + u',MA({0}):{1}'.format(self.para_ma3_len, self.line_ma3[-1]) if self.ma13_count == 1: - msg = msg + u'MA{}金叉MA{}'.format(self.para_ma1_len, self.para_ma3_len) + msg = msg + u',MA{}金叉MA{}'.format(self.para_ma1_len, self.para_ma3_len) elif self.ma13_count == -1: - msg = msg + u'MA{}死叉MA{}'.format(self.para_ma1_len, self.para_ma3_len) + msg = msg + u',MA{}死叉MA{}'.format(self.para_ma1_len, self.para_ma3_len) if self.ma23_count == 1: - msg = msg + u'MA{}金叉MA{}'.format(self.para_ma2_len, self.para_ma3_len) + msg = msg + u',MA{}金叉MA{}'.format(self.para_ma2_len, self.para_ma3_len) elif self.ma23_count == -1: - msg = msg + u'MA{}死叉MA{}'.format(self.para_ma2_len, self.para_ma3_len) + msg = msg + u',MA{}死叉MA{}'.format(self.para_ma2_len, self.para_ma3_len) if self.para_ema1_len > 0 and len(self.line_ema1) > 0: msg = msg + u',EMA({0}):{1}'.format(self.para_ema1_len, self.line_ema1[-1]) @@ -1016,7 +1036,7 @@ class CtaLineBar(object): round(self.line_upper_atan[-1], self.round_n) if len(self.line_upper_atan) > 0 else 0, round(self.line_lower_atan[-1], self.round_n) if len(self.line_lower_atan) > 0 else 0) - if (self.para_boll2_len > 0 or self.para_boll2_tb_len > 0) and len(self.line_boll_upper) > 0: + if (self.para_boll2_len > 0 or self.para_boll2_tb_len > 0) and len(self.line_boll2_upper) > 0: msg = msg + u',Boll2({}):std:{},m:{},u:{},l:{}'. \ format(self.para_boll2_len, round(self.line_boll_std[-1], self.round_n), round(self.line_boll2_middle[-1], self.round_n), @@ -1092,6 +1112,7 @@ class CtaLineBar(object): def first_tick(self, tick: TickData): """ K线的第一个Tick数据""" + # 1、当前新合成的line_bar的第一个tick 数据,创建新的cur_bar,并更新其属性 self.cur_bar = BarData( gateway_name=tick.gateway_name, symbol=tick.symbol, @@ -1130,13 +1151,14 @@ class CtaLineBar(object): # K线的日期时间(去除秒)设为第一个Tick的时间 self.cur_bar.datetime = self.cur_bar.datetime.replace(second=0, microsecond=0) self.cur_bar.time = self.cur_bar.datetime.strftime('%H:%M:%S') - self.cur_bar.volume = tick.volume + self.cur_bar.volume = tick.last_volume if self.cur_trading_day != self.cur_bar.trading_day or not self.line_bar: # bar的交易日与记录的当前交易日不一致: self.cur_trading_day = self.cur_bar.trading_day self.is_first_tick = True # 标识该Tick属于该Bar的第一个tick数据 + # 6、将生成的正在合成的self.cur_bar 推入到line_bar队列 self.line_bar.append(self.cur_bar) # 推入到lineBar队列 def generate_bar(self, tick: TickData): @@ -1159,6 +1181,7 @@ class CtaLineBar(object): # 处理日内的间隔时段最后一个tick,如10:15分,11:30分,15:00 和 2:30分 endtick = False if not self.is_7x24: + # 标记日内的间隔时段最后一个tick,如10:15分,11:30分,15:00 和 2:30分 if (tick.datetime.hour == 10 and tick.datetime.minute == 15) \ or (tick.datetime.hour == 11 and tick.datetime.minute == 30) \ or (tick.datetime.hour == 15 and tick.datetime.minute == 00) \ @@ -1235,6 +1258,7 @@ class CtaLineBar(object): # 触发OnBar事件 self.on_bar(lastBar) + # 6、没有产生新bar,更新当前正在合成的lastBar属性 else: # 更新当前最后一个bar self.is_first_tick = False @@ -1244,7 +1268,12 @@ class CtaLineBar(object): lastBar.low_price = min(lastBar.low_price, tick.last_price) lastBar.close_price = tick.last_price lastBar.open_interest = tick.open_interest - lastBar.volume += tick.volume + + # 更新bar的 bar内成交量,老版,将tick的volume,减去上一bar的dayVolume + # volume_change = tick.volume - self.last_tick.volume + # lastbar.volume += max(volume_change, 0) + # 更新 bar内成交量volume 新版根据tick内成交量运算 + lastBar.volume += tick.last_volume # 更新Bar的颜色 if lastBar.close_price > lastBar.open_price: @@ -1267,18 +1296,19 @@ class CtaLineBar(object): return count_len = min(self.para_pre_len, self.bar_len) - # 2.计算前inputPreLen周期内(不包含当前周期)的Bar高点和低点 + # 2.计算前self.para_pre_len周期内的Bar高点和低点(不包含当前周期,因为当前正在合成的bar + # 还未触发on_bar,不会存入开高低收序列) preHigh = max(self.high_array[-count_len:]) preLow = min(self.low_array[-count_len:]) if np.isnan(preHigh) or np.isnan(preLow): return - # 保存 - if len(self.line_pre_high) > self.max_hold_bars: + # 保存前高值到 前高序列 + if len(self.line_pre_high) > self.max_hold_bars: # 维持最大缓存数量 超过则删除最前面 del self.line_pre_high[0] self.line_pre_high.append(preHigh) - # 保存 - if len(self.line_pre_low) > self.max_hold_bars: + # 保存前低值到 前低序列 + if len(self.line_pre_low) > self.max_hold_bars: # 维持最大缓存数量 超过则删除最前面 del self.line_pre_low[0] self.line_pre_low.append(preLow) diff --git a/vnpy/gateway/xtp/xtp_gateway.py b/vnpy/gateway/xtp/xtp_gateway.py index 777b93c1..6bef4c6c 100644 --- a/vnpy/gateway/xtp/xtp_gateway.py +++ b/vnpy/gateway/xtp/xtp_gateway.py @@ -242,14 +242,18 @@ class XtpMdApi(MdApi): self.connect_status: bool = False self.login_status: bool = False - def onDisconnected(self, reason: int) -> None: """""" self.connect_status = False self.login_status = False self.gateway.write_log(f"行情服务器连接断开, 原因{reason}") - n = self.login() + n = self.login( + self.server_ip, + self.server_port, + self.userid, + self.password, + self.protocol) if n: self.session_id = n diff --git a/vnpy/trader/ui/kline/ui_snapshot.py b/vnpy/trader/ui/kline/ui_snapshot.py index 40cdb9b2..a656c2d0 100644 --- a/vnpy/trader/ui/kline/ui_snapshot.py +++ b/vnpy/trader/ui/kline/ui_snapshot.py @@ -21,15 +21,15 @@ class UiSnapshot(object): pass - def show(self, snapshot_file: str): + def show(self, snapshot_file: str, d=None): - if not os.path.exists(snapshot_file): - print(f'{snapshot_file}不存在', file=sys.stderr) - return + if d is None: + if not os.path.exists(snapshot_file): + print(f'{snapshot_file}不存在', file=sys.stderr) + return - d = None - with bz2.BZ2File(snapshot_file, 'rb') as f: - d = pickle.load(f) + with bz2.BZ2File(snapshot_file, 'rb') as f: + d = pickle.load(f) use_zlib = d.get('zlib', False) klines = d.pop('klines', None)