[增强功能] 实时查看策略内K线切片功能

This commit is contained in:
msincenselee 2020-05-26 17:38:14 +08:00
parent 1c2c21812f
commit eb14b154d5
8 changed files with 202 additions and 96 deletions

View File

@ -1221,6 +1221,25 @@ class CtaEngine(BaseEngine):
self.write_error(u'保存策略{}数据异常:'.format(strategy_name, str(ex))) self.write_error(u'保存策略{}数据异常:'.format(strategy_name, str(ex)))
self.write_error(traceback.format_exc()) 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'): def save_strategy_snapshot(self, select_name: str = 'ALL'):
""" """
保存策略K线切片数据 保存策略K线切片数据

View File

@ -414,8 +414,9 @@ class CtaStockTemplate(CtaTemplate):
cancel_seconds = 120 # 撤单时间(秒) cancel_seconds = 120 # 撤单时间(秒)
# 资金相关 # 资金相关
max_invest_rate = 0.1 # 最大仓位(0~1) max_invest_rate = 0.1 # 策略使用账号的最大仓位(0~1)
max_invest_margin = 0 # 资金上限 0不限制 max_invest_margin = 0 # 策略资金上限, 0不限制
max_single_margin = 0 # 策略内,各只股票使用的资金上限
# 是否回测状态 # 是否回测状态
backtesting = False backtesting = False

View File

@ -8,12 +8,14 @@ from vnpy.trader.ui.widget import (
TimeCell, TimeCell,
BaseMonitor BaseMonitor
) )
from vnpy.trader.ui.kline.ui_snapshot import UiSnapshot
from ..base import ( from ..base import (
APP_NAME, APP_NAME,
EVENT_CTA_LOG, EVENT_CTA_LOG,
EVENT_CTA_STOPORDER, EVENT_CTA_STOPORDER,
EVENT_CTA_STRATEGY EVENT_CTA_STRATEGY
) )
from ..engine import CtaEngine from ..engine import CtaEngine
@ -205,6 +207,9 @@ class StrategyManager(QtWidgets.QFrame):
save_button = QtWidgets.QPushButton("保存") save_button = QtWidgets.QPushButton("保存")
save_button.clicked.connect(self.save_strategy) 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"] strategy_name = self._data["strategy_name"]
#vt_symbol = self._data["vt_symbol"] #vt_symbol = self._data["vt_symbol"]
class_name = self._data["class_name"] class_name = self._data["class_name"]
@ -227,6 +232,7 @@ class StrategyManager(QtWidgets.QFrame):
hbox.addWidget(remove_button) hbox.addWidget(remove_button)
hbox.addWidget(reload_button) hbox.addWidget(reload_button)
hbox.addWidget(save_button) hbox.addWidget(save_button)
hbox.addWidget(view_button)
vbox = QtWidgets.QVBoxLayout() vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(label) vbox.addWidget(label)
@ -279,8 +285,16 @@ class StrategyManager(QtWidgets.QFrame):
self.cta_engine.reload_strategy(self.strategy_name) self.cta_engine.reload_strategy(self.strategy_name)
def save_strategy(self): def save_strategy(self):
"""保存策略缓存数据"""
self.cta_engine.save_strategy_data(self.strategy_name) 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): class DataMonitor(QtWidgets.QTableWidget):
""" """

View File

@ -1190,6 +1190,25 @@ class CtaEngine(BaseEngine):
self.write_error(u'保存策略{}数据异常:'.format(strategy_name, str(ex))) self.write_error(u'保存策略{}数据异常:'.format(strategy_name, str(ex)))
self.write_error(traceback.format_exc()) 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'): def save_strategy_snapshot(self, select_name: str = 'ALL'):
""" """
保存策略K线切片数据 保存策略K线切片数据
@ -1567,6 +1586,10 @@ class CtaEngine(BaseEngine):
holding = self.offset_converter.holdings.get(holding_key, None) holding = self.offset_converter.holdings.get(holding_key, None)
if holding is None: if holding is None:
continue 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( compare_pos[vt_symbol] = OrderedDict(
{ {

View File

@ -8,12 +8,14 @@ from vnpy.trader.ui.widget import (
TimeCell, TimeCell,
BaseMonitor BaseMonitor
) )
from vnpy.trader.ui.kline.ui_snapshot import UiSnapshot
from ..base import ( from ..base import (
APP_NAME, APP_NAME,
EVENT_CTA_LOG, EVENT_CTA_LOG,
EVENT_CTA_STOPORDER, EVENT_CTA_STOPORDER,
EVENT_CTA_STRATEGY EVENT_CTA_STRATEGY
) )
from ..engine import CtaEngine from ..engine import CtaEngine
@ -205,6 +207,9 @@ class StrategyManager(QtWidgets.QFrame):
save_button = QtWidgets.QPushButton("保存") save_button = QtWidgets.QPushButton("保存")
save_button.clicked.connect(self.save_strategy) 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"] strategy_name = self._data["strategy_name"]
vt_symbol = self._data["vt_symbol"] vt_symbol = self._data["vt_symbol"]
class_name = self._data["class_name"] class_name = self._data["class_name"]
@ -227,6 +232,7 @@ class StrategyManager(QtWidgets.QFrame):
hbox.addWidget(remove_button) hbox.addWidget(remove_button)
hbox.addWidget(reload_button) hbox.addWidget(reload_button)
hbox.addWidget(save_button) hbox.addWidget(save_button)
hbox.addWidget(view_button)
vbox = QtWidgets.QVBoxLayout() vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(label) vbox.addWidget(label)
@ -279,8 +285,17 @@ class StrategyManager(QtWidgets.QFrame):
self.cta_engine.reload_strategy(self.strategy_name) self.cta_engine.reload_strategy(self.strategy_name)
def save_strategy(self): def save_strategy(self):
"""保存策略缓存数据"""
self.cta_engine.save_strategy_data(self.strategy_name) 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): class DataMonitor(QtWidgets.QTableWidget):
""" """

View File

@ -143,11 +143,9 @@ class CtaLineBar(object):
# 参数列表,保存了参数的名称 # 参数列表,保存了参数的名称
paramList = ['vt_symbol'] paramList = ['vt_symbol']
# 参数列表
def __init__(self, strategy, cb_on_bar, setting=None): def __init__(self, strategy, cb_on_bar, setting=None):
# OnBar事件回调函数 # on_bar事件回调函数,X周期bar合成完毕时回调到策略的cb_on_bar接口
self.cb_on_bar = cb_on_bar self.cb_on_bar = cb_on_bar
# 周期变更事件回调函数 # 周期变更事件回调函数
@ -156,27 +154,28 @@ class CtaLineBar(object):
# K 线服务的策略 # K 线服务的策略
self.strategy = strategy self.strategy = strategy
# 当前商品合约 属性
self.underly_symbol = '' # 商品的短代码 self.underly_symbol = '' # 商品的短代码
self.price_tick = 1 # 商品的最小价格单位 self.price_tick = 1 # 商品的最小价格单位
self.round_n = 4 # round() 小数点的截断数量 self.round_n = 4 # round() 小数点的截断数量
self.is_7x24 = False # 是否7x24小时运行 一般为数字货币)
self.is_7x24 = False # 当前的Tick的信息
self.cur_tick = None # 当前 onTick()函数接收的 最新的tick
# 当前的Tick self.last_tick = None # 当前正在合成的 X周期bar 的最后(新)一根tick
self.cur_tick = None self.cur_datetime = None # 当前add_bar()传进来的bar/on_tick()传进来tick 对应的最新时间
self.last_tick = None self.cur_trading_day = '' # 当前传入tick/bar对应的交易日
self.cur_datetime = None self.cur_price = 0 # 当前curTick.last_price/add_bar中的bar.close_price传进来的 最新市场价格
self.cur_trading_day = ''
self.cur_price = 0
# K线保存数据 # K线保存数据
self.cur_bar = None # K线数据对象代表最后一根/未走完的bar self.cur_bar = None # K线数据对象代表最后一根/未走完的bar
self.line_bar = [] # K线缓存数据队列 self.line_bar = [] # K线缓存数据队列(缓存合成完 以及正在合成的bar)
self.bar_len = 0 # 当前K线得真实数量 self.bar_len = 0 # 当前K线得真实数量(包含已经合成以及正在合成的bar)
self.max_hold_bars = 2000 self.max_hold_bars = 2000
self.is_first_tick = False # K线的第一条Tick数据 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.zeros(self.max_hold_bars) # 与lineBar一致得开仓价清单
self.open_array[:] = np.nan self.open_array[:] = np.nan
self.high_array = np.zeros(self.max_hold_bars) # 与lineBar一致得最高价清单 self.high_array = np.zeros(self.max_hold_bars) # 与lineBar一致得最高价清单
@ -192,14 +191,14 @@ class CtaLineBar(object):
self.mid4_array[:] = np.nan self.mid4_array[:] = np.nan
self.mid5_array = np.zeros(self.max_hold_bars) # 收盘价*2/开仓价/最高/最低价 的平均价 self.mid5_array = np.zeros(self.max_hold_bars) # 收盘价*2/开仓价/最高/最低价 的平均价
self.mid5_array[:] = np.nan self.mid5_array[:] = np.nan
# 导出到CSV文件 的目录名 和 要导出的 字段
self.export_filename = None # 数据要导出的目标文件夹
self.export_fields = [] # 定义要导出的数据字段
self.export_filename = None # 创建本类型bar的内部变量以及添加所有指标输入参数到self.paramList列表
self.export_fields = []
# 创建内部变量
self.init_properties() self.init_properties()
# 创建初始化指标 # 初始化定义所有的指标输入参数,以及指标生成的数据
self.init_indicators() self.init_indicators()
# 启动实时得函数 # 启动实时得函数
@ -209,6 +208,7 @@ class CtaLineBar(object):
# 注册回调函数 # 注册回调函数
self.cb_dict = {} self.cb_dict = {}
self.minute_interval = None # 把各个周期的bar转换为分钟在first_tick中用来修正bar为整点分钟周期
if setting: if setting:
self.set_params(setting) self.set_params(setting)
@ -221,11 +221,13 @@ class CtaLineBar(object):
self.minute_interval = 60 self.minute_interval = 60
elif self.interval == Interval.DAILY: elif self.interval == Interval.DAILY:
self.minute_interval = 60 * 24 self.minute_interval = 60 * 24
# 修正精度 # 修正精度
if self.price_tick < 1: if self.price_tick < 1:
exponent = decimal.Decimal(str(self.price_tick)) exponent = decimal.Decimal(str(self.price_tick))
self.round_n = max(abs(exponent.as_tuple().exponent) + 2, 4) self.round_n = max(abs(exponent.as_tuple().exponent) + 2, 4)
self.write_log(f'round_n: {self.round_n}') self.write_log(f'round_n: {self.round_n}')
# 导入卡尔曼过滤器 # 导入卡尔曼过滤器
if self.para_active_kf: if self.para_active_kf:
try: try:
@ -246,37 +248,45 @@ class CtaLineBar(object):
self.cb_on_period = cb_func self.cb_on_period = cb_func
def init_param_list(self): def init_param_list(self):
self.paramList.append('bar_interval') """初始化添加本类型bar的内部变量以及添加所有指标输入参数到self.paramList列表"""
self.paramList.append('interval') # ------- 本类型bar的内部变量 ---------
self.paramList.append('mode') 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_ma2_len')
self.paramList.append('para_ma3_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_ema2_len')
self.paramList.append('para_ema3_len') self.paramList.append('para_ema3_len')
self.paramList.append('para_dmi_len') self.paramList.append('para_dmi_len')
self.paramList.append('para_dmi_max') 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_atr2_len')
self.paramList.append('para_atr3_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_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_len') # 布林通道长度(文华计算方式)
self.paramList.append('para_boll_tb_len') self.paramList.append('para_boll_tb_len') # 布林通道长度tb计算方式
self.paramList.append('para_boll_std_rate') self.paramList.append('para_boll_std_rate') # 标准差倍率一般为2
self.paramList.append('para_boll2_len') self.paramList.append('para_boll2_len') # 第二条布林通道
self.paramList.append('para_boll2_tb_len') self.paramList.append('para_boll2_tb_len')
self.paramList.append('para_boll2_std_rate') 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_slow_len')
self.paramList.append('para_macd_signal_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_step')
self.paramList.append('para_sar_limit') 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_fast_len')
self.paramList.append('para_skd_slow_len') self.paramList.append('para_skd_slow_len')
self.paramList.append('para_skd_low') self.paramList.append('para_skd_low')
self.paramList.append('para_skd_high') 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_len')
self.paramList.append('para_yb_ref') self.paramList.append('para_yb_ref')
self.paramList.append('para_golden_n') self.paramList.append('para_golden_n') # 黄金分割
self.paramList.append('para_active_area') self.paramList.append('para_active_area')
@ -319,12 +329,7 @@ class CtaLineBar(object):
self.paramList.append('para_bd_len') 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): def init_properties(self):
""" """
@ -361,9 +366,9 @@ class CtaLineBar(object):
self.__dict__[key] = state.__dict__[key] self.__dict__[key] = state.__dict__[key]
def init_indicators(self): def init_indicators(self):
""" 定义所有的指标数据""" """ 初始化定义所有的指标输入参数,以及指标生成的数据 """
# 指标参数 # ------------- 指标输入参数 ------------------
self.para_pre_len = 0 # 20 # 前高前低的周期长度 self.para_pre_len = 0 # 20 # 前高前低的周期长度
self.para_ma1_len = 0 # 10 # 第一根MA均线的周期长度 self.para_ma1_len = 0 # 10 # 第一根MA均线的周期长度
@ -403,7 +408,7 @@ class CtaLineBar(object):
self.para_cci_len = 0 # 计算CCI的K线周期 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_slow_len = 0 # 慢线周期
self.para_macd_signal_len = 0 # 平滑周期 self.para_macd_signal_len = 0 # 平滑周期
@ -412,7 +417,7 @@ class CtaLineBar(object):
self.para_sar_step = 0 # 抛物线的参数 self.para_sar_step = 0 # 抛物线的参数
self.para_sar_limit = 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_fast_len = 13 # 摆动指标快线周期1
self.para_skd_slow_len = 8 # 摆动指标慢线周期2 self.para_skd_slow_len = 8 # 摆动指标慢线周期2
self.para_skd_low = 30 # 摆动指标下限区域 self.para_skd_low = 30 # 摆动指标下限区域
@ -435,7 +440,7 @@ class CtaLineBar(object):
self.para_bd_len = 0 # 波段买卖观测长度 self.para_bd_len = 0 # 波段买卖观测长度
# K 线的相关计算结果数据 # --------------- K 线的指标相关计算结果数据 ----------------
self.line_pre_high = [] # K线的前para_pre_len的的最高 self.line_pre_high = [] # K线的前para_pre_len的的最高
self.line_pre_low = [] # 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_ma2_atan = None
self._rt_ma3_atan = None self._rt_ma3_atan = None
self.ma12_count = 0 # ma1 与 ma2 ,金叉/死叉后第几根bar self.ma12_count = 0 # ma1 与 ma2 ,金叉/死叉后第几根bar,金叉正数,死叉负数
self.ma13_count = 0 # ma1 与 ma3 ,金叉/死叉后第几根bar self.ma13_count = 0 # ma1 与 ma3 ,金叉/死叉后第几根bar,金叉正数,死叉负数
self.ma23_count = 0 # ma2 与 ma3 ,金叉/死叉后第几根bar self.ma23_count = 0 # ma2 与 ma3 ,金叉/死叉后第几根bar,金叉正数,死叉负数
self.line_ema1 = [] # K线的EMA1均线周期是InputEmaLen1不包含当前bar self.line_ema1 = [] # K线的EMA1均线周期是para_ema1_len1不包含当前bar
self.line_ema2 = [] # K线的EMA2均线周期是InputEmaLen2不包含当前bar self.line_ema2 = [] # K线的EMA2均线周期是para_ema1_len2不包含当前bar
self.line_ema3 = [] # K线的EMA3均线周期是InputEmaLen3不包含当前bar self.line_ema3 = [] # K线的EMA3均线周期是para_ema1_len3不包含当前bar
self._rt_ema1 = None # K线的实时EMA(para_ema1_len) self._rt_ema1 = None # K线的实时EMA(para_ema1_len)
self._rt_ema2 = None # K线的实时EMA(para_ema2_len) self._rt_ema2 = None # K线的实时EMA(para_ema2_len)
@ -485,9 +490,9 @@ class CtaLineBar(object):
self.signal_adx_short = False # 空过滤器条件,做空趋势的判断ADXR高于前一天下降动向> inputMM self.signal_adx_short = False # 空过滤器条件,做空趋势的判断ADXR高于前一天下降动向> inputMM
# K线的ATR技术数据 # K线的ATR技术数据
self.line_atr1 = [] # K线的ATR1,周期为inputAtr1Len self.line_atr1 = [] # K线的ATR1,周期为para_atr1_len
self.line_atr2 = [] # K线的ATR2,周期为inputAtr2Len self.line_atr2 = [] # K线的ATR2,周期为para_atr2_len
self.line_atr3 = [] # K线的ATR3,周期为inputAtr3Len self.line_atr3 = [] # K线的ATR3,周期为para_atr3_len
self.cur_atr1 = 0 self.cur_atr1 = 0
self.cur_atr2 = 0 self.cur_atr2 = 0
@ -497,22 +502,25 @@ class CtaLineBar(object):
self.line_vol_ma = [] # K 线的交易量平均 self.line_vol_ma = [] # K 线的交易量平均
# K线的RSI计算数据 # K线的RSI计算数据
self.line_rsi1 = [] # 记录K线对应的RSI数值只保留inputRsi1Len*8 self.line_rsi1 = [] # 记录K线对应的RSI数值只保留para_rsi1_len*8
self.line_rsi2 = [] # 记录K线对应的RSI数值只保留inputRsi2Len*8 self.line_rsi2 = [] # 记录K线对应的RSI数值只保留para_rsi2_len*8
self.para_rsi_low = 30 # RSI的最低线 self.para_rsi_low = 30 # RSI的最低线
self.para_rsi_high = 70 # RSI的最高线 self.para_rsi_high = 70 # RSI的最高线
self.rsi_top_list = [] # 记录RSI的最高峰只保留 inputRsiLen个 self.rsi_top_list = [] # 记录RSI的最高峰只保留 inputRsiLen个
self.rsi_buttom_list = [] # 记录RSI的最低谷只保留 inputRsiLen个 self.rsi_buttom_list = [] # 记录RSI的最低谷只保留 inputRsiLen个
self.cur_rsi_top_buttom = {} # 最近的一个波峰/波谷 self.cur_rsi_top_buttom = {} # 最近的一个波峰/波谷
# K线的CMI计算数据 # K线的CMI计算数据
self.line_cmi = [] # 记录K线对应的Cmi数值只保留inputCmiLen*8 self.line_cmi = [] # 记录K线对应的Cmi数值只保留para_cmi_len*8
# K线的布林特计算数据 # K线的布林特计算数据
self.line_boll_upper = [] # 上轨 self.line_boll_upper = [] # 上轨
self.line_boll_middle = [] # 中线 self.line_boll_middle = [] # 中线
self.line_boll_lower = [] # 下轨 self.line_boll_lower = [] # 下轨
self.line_boll_std = [] # 标准差 self.line_boll_std = [] # 标准差
self.line_upper_atan = [] self.line_upper_atan = []
self.line_middle_atan = [] self.line_middle_atan = []
self.line_lower_atan = [] self.line_lower_atan = []
@ -523,17 +531,19 @@ class CtaLineBar(object):
self._rt_middle_atan = None self._rt_middle_atan = None
self._rt_lower_atan = None self._rt_lower_atan = None
self.cur_upper = 0 # 最后一根K的Boll上轨数值MinDiff取整) self.cur_upper = 0 # 最后一根K的Boll上轨数值price_tick取整)
self.cur_middle = 0 # 最后一根K的Boll中轨数值MinDiff取整) self.cur_middle = 0 # 最后一根K的Boll中轨数值price_tick取整)
self.cur_lower = 0 # 最后一根K的Boll下轨数值MinDiff取整+1 self.cur_lower = 0 # 最后一根K的Boll下轨数值price_tick取整+1
self.line_boll2_upper = [] # 上轨 self.line_boll2_upper = [] # 上轨
self.line_boll2_middle = [] # 中线 self.line_boll2_middle = [] # 中线
self.line_boll2_lower = [] # 下轨 self.line_boll2_lower = [] # 下轨
self.line_boll2_std = [] # 标准差 self.line_boll2_std = [] # 标准差
self.line_upper2_atan = [] self.line_upper2_atan = []
self.line_middle2_atan = [] self.line_middle2_atan = []
self.line_lower2_atan = [] self.line_lower2_atan = []
self._rt_upper2 = None self._rt_upper2 = None
self._rt_middle2 = None self._rt_middle2 = None
self._rt_lower2 = None self._rt_lower2 = None
@ -541,16 +551,16 @@ class CtaLineBar(object):
self._rt_middle2_atan = None self._rt_middle2_atan = None
self._rt_lower2_atan = None self._rt_lower2_atan = None
self.cur_upper2 = 0 # 最后一根K的Boll2上轨数值MinDiff取整) self.cur_upper2 = 0 # 最后一根K的Boll2上轨数值price_tick取整)
self.cur_middle2 = 0 # 最后一根K的Boll2中轨数值MinDiff取整) self.cur_middle2 = 0 # 最后一根K的Boll2中轨数值price_tick取整)
self.cur_lower2 = 0 # 最后一根K的Boll2下轨数值MinDiff取整+1 self.cur_lower2 = 0 # 最后一根K的Boll2下轨数值price_tick取整+1
# K线的KDJ指标计算数据 # K线的KDJ指标计算数据
self.line_k = [] # K为快速指标 self.line_k = [] # K为快速指标
self.line_d = [] # D为慢速指标 self.line_d = [] # D为慢速指标
self.line_j = [] # self.line_j = [] #
self.kdj_top_list = [] # 记录KDJ最高峰只保留 inputKdjLen个 self.kdj_top_list = [] # 记录KDJ最高峰只保留 para_kdj_len个
self.kdj_buttom_list = [] # 记录KDJ的最低谷只保留 inputKdjLen个 self.kdj_buttom_list = [] # 记录KDJ的最低谷只保留 para_kdj_len个
self.line_rsv = [] # RSV self.line_rsv = [] # RSV
self.cur_kdj_top_buttom = {} # 最近的一个波峰/波谷 self.cur_kdj_top_buttom = {} # 最近的一个波峰/波谷
self.cur_k = 0 # bar内计算时最后一个未关闭的bar的实时K值 self.cur_k = 0 # bar内计算时最后一个未关闭的bar的实时K值
@ -620,6 +630,7 @@ class CtaLineBar(object):
self.line_skd_sto = [] # 根据RSI演算的STO self.line_skd_sto = [] # 根据RSI演算的STO
self.line_sk = [] # 快线 self.line_sk = [] # 快线
self.line_sd = [] # 慢线 self.line_sd = [] # 慢线
self.cur_skd_count = 0 # 当前金叉/死叉后累加 self.cur_skd_count = 0 # 当前金叉/死叉后累加
self._rt_sk = None # 实时SK值 self._rt_sk = None # 实时SK值
self._rt_sd = None # 实时SD值 self._rt_sd = None # 实时SD值
@ -664,6 +675,7 @@ class CtaLineBar(object):
self.line_bd_fast = [] # 波段快线 self.line_bd_fast = [] # 波段快线
self.line_bd_slow = [] # 波段慢线 self.line_bd_slow = [] # 波段慢线
self.cur_bd_count = 0 # 当前波段快线慢线金叉死叉, +金叉计算, - 死叉技术 self.cur_bd_count = 0 # 当前波段快线慢线金叉死叉, +金叉计算, - 死叉技术
self._bd_fast = 0 self._bd_fast = 0
self._bd_slow = 0 self._bd_slow = 0
@ -795,6 +807,7 @@ class CtaLineBar(object):
def on_bar(self, bar: BarData): def on_bar(self, bar: BarData):
"""OnBar事件""" """OnBar事件"""
# 将上一根bar合成完结了触发本on_bar事件(缓存开高收低等序列,计算各个指标)
if not bar.interval: if not bar.interval:
bar.interval = self.interval bar.interval = self.interval
bar.interval_num = self.bar_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_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) 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] = self.open_array[1:]
self.open_array[-1] = bar.open_price 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] = self.mid5_array[1:]
self.mid5_array[-1] = bar_mid5 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 # 删除了最前面的barbar长度少一位
self.__count_pre_high_low() self.__count_pre_high_low()
self.__count_ma() self.__count_ma()
@ -853,9 +870,9 @@ class CtaLineBar(object):
self.__count_skdj() self.__count_skdj()
self.export_to_csv(bar) 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: if self.cb_on_bar:
self.cb_on_bar(bar=bar) self.cb_on_bar(bar=bar)
@ -890,6 +907,7 @@ class CtaLineBar(object):
def export_to_csv(self, bar: BarData): def export_to_csv(self, bar: BarData):
""" 输出到csv文件""" """ 输出到csv文件"""
# 将我们配置在self.export_fields的要输出的 bar信息以及指标信息 ==》输出到csv文件
if self.export_filename is None or len(self.export_fields) == 0: if self.export_filename is None or len(self.export_fields) == 0:
return return
field_names = [] field_names = []
@ -911,6 +929,8 @@ class CtaLineBar(object):
save_dict[field_name] = 0 save_dict[field_name] = 0
else: else:
save_dict[field_name] = list_obj[-1] save_dict[field_name] = list_obj[-1]
elif type_ == 'string':
save_dict[field_name] = getattr(self, str(attr_name), '')
else: else:
save_dict[field_name] = getattr(self, str(attr_name), 0) 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: 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]) msg = msg + u',MA({0}):{1}'.format(self.para_ma2_len, self.line_ma2[-1])
if self.ma12_count == 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: 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: 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]) msg = msg + u',MA({0}):{1}'.format(self.para_ma3_len, self.line_ma3[-1])
if self.ma13_count == 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: 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: 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: 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: 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]) 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_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) 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:{}'. \ msg = msg + u',Boll2({}):std:{},m:{},u:{},l:{}'. \
format(self.para_boll2_len, round(self.line_boll_std[-1], self.round_n), format(self.para_boll2_len, round(self.line_boll_std[-1], self.round_n),
round(self.line_boll2_middle[-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): def first_tick(self, tick: TickData):
""" K线的第一个Tick数据""" """ K线的第一个Tick数据"""
# 1、当前新合成的line_bar的第一个tick 数据创建新的cur_bar并更新其属性
self.cur_bar = BarData( self.cur_bar = BarData(
gateway_name=tick.gateway_name, gateway_name=tick.gateway_name,
symbol=tick.symbol, symbol=tick.symbol,
@ -1130,13 +1151,14 @@ class CtaLineBar(object):
# K线的日期时间去除秒设为第一个Tick的时间 # K线的日期时间去除秒设为第一个Tick的时间
self.cur_bar.datetime = self.cur_bar.datetime.replace(second=0, microsecond=0) 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.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: if self.cur_trading_day != self.cur_bar.trading_day or not self.line_bar:
# bar的交易日与记录的当前交易日不一致 # bar的交易日与记录的当前交易日不一致
self.cur_trading_day = self.cur_bar.trading_day self.cur_trading_day = self.cur_bar.trading_day
self.is_first_tick = True # 标识该Tick属于该Bar的第一个tick数据 self.is_first_tick = True # 标识该Tick属于该Bar的第一个tick数据
# 6、将生成的正在合成的self.cur_bar 推入到line_bar队列
self.line_bar.append(self.cur_bar) # 推入到lineBar队列 self.line_bar.append(self.cur_bar) # 推入到lineBar队列
def generate_bar(self, tick: TickData): def generate_bar(self, tick: TickData):
@ -1159,6 +1181,7 @@ class CtaLineBar(object):
# 处理日内的间隔时段最后一个tick如10:15分11:30分15:00 和 2:30分 # 处理日内的间隔时段最后一个tick如10:15分11:30分15:00 和 2:30分
endtick = False endtick = False
if not self.is_7x24: if not self.is_7x24:
# 标记日内的间隔时段最后一个tick如10:15分11:30分15:00 和 2:30分
if (tick.datetime.hour == 10 and tick.datetime.minute == 15) \ if (tick.datetime.hour == 10 and tick.datetime.minute == 15) \
or (tick.datetime.hour == 11 and tick.datetime.minute == 30) \ or (tick.datetime.hour == 11 and tick.datetime.minute == 30) \
or (tick.datetime.hour == 15 and tick.datetime.minute == 00) \ or (tick.datetime.hour == 15 and tick.datetime.minute == 00) \
@ -1235,6 +1258,7 @@ class CtaLineBar(object):
# 触发OnBar事件 # 触发OnBar事件
self.on_bar(lastBar) self.on_bar(lastBar)
# 6、没有产生新bar更新当前正在合成的lastBar属性
else: else:
# 更新当前最后一个bar # 更新当前最后一个bar
self.is_first_tick = False self.is_first_tick = False
@ -1244,7 +1268,12 @@ class CtaLineBar(object):
lastBar.low_price = min(lastBar.low_price, tick.last_price) lastBar.low_price = min(lastBar.low_price, tick.last_price)
lastBar.close_price = tick.last_price lastBar.close_price = tick.last_price
lastBar.open_interest = tick.open_interest 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的颜色 # 更新Bar的颜色
if lastBar.close_price > lastBar.open_price: if lastBar.close_price > lastBar.open_price:
@ -1267,18 +1296,19 @@ class CtaLineBar(object):
return return
count_len = min(self.para_pre_len, self.bar_len) 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:]) preHigh = max(self.high_array[-count_len:])
preLow = min(self.low_array[-count_len:]) preLow = min(self.low_array[-count_len:])
if np.isnan(preHigh) or np.isnan(preLow): if np.isnan(preHigh) or np.isnan(preLow):
return 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] del self.line_pre_high[0]
self.line_pre_high.append(preHigh) 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] del self.line_pre_low[0]
self.line_pre_low.append(preLow) self.line_pre_low.append(preLow)

View File

@ -242,14 +242,18 @@ class XtpMdApi(MdApi):
self.connect_status: bool = False self.connect_status: bool = False
self.login_status: bool = False self.login_status: bool = False
def onDisconnected(self, reason: int) -> None: def onDisconnected(self, reason: int) -> None:
"""""" """"""
self.connect_status = False self.connect_status = False
self.login_status = False self.login_status = False
self.gateway.write_log(f"行情服务器连接断开, 原因{reason}") 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: if n:
self.session_id = n self.session_id = n

View File

@ -21,13 +21,13 @@ class UiSnapshot(object):
pass pass
def show(self, snapshot_file: str): def show(self, snapshot_file: str, d=None):
if d is None:
if not os.path.exists(snapshot_file): if not os.path.exists(snapshot_file):
print(f'{snapshot_file}不存在', file=sys.stderr) print(f'{snapshot_file}不存在', file=sys.stderr)
return return
d = None
with bz2.BZ2File(snapshot_file, 'rb') as f: with bz2.BZ2File(snapshot_file, 'rb') as f:
d = pickle.load(f) d = pickle.load(f)