diff --git a/vnpy/app/cta_crypto/engine.py b/vnpy/app/cta_crypto/engine.py index 3b88356e..4b1a39eb 100644 --- a/vnpy/app/cta_crypto/engine.py +++ b/vnpy/app/cta_crypto/engine.py @@ -482,7 +482,7 @@ class CtaEngine(BaseEngine): # Check if sending order successful if not vt_orderid: - vt_orderids + return vt_orderids vt_orderids.append(vt_orderid) diff --git a/vnpy/app/cta_stock/engine.py b/vnpy/app/cta_stock/engine.py index 452fc6f2..2d3b0ab1 100644 --- a/vnpy/app/cta_stock/engine.py +++ b/vnpy/app/cta_stock/engine.py @@ -1570,15 +1570,34 @@ class CtaEngine(BaseEngine): else: self.write_log(f'策略缓存文件不存在:{cache_file}') - def get_strategy_snapshot(self, strategy_name): - """实时获取策略的K线切片(比较耗性能)""" + def get_strategy_kline_names(self, strategy_name): + """ + 获取策略实例内的K线名称 + :param strategy_name:策略实例名称 + :return: + """ + info = {} + strategy = self.strategies.get(strategy_name, None) + if strategy is None: + return info + if hasattr(strategy, 'get_klines_info'): + info = strategy.get_klines_info() + return info + + def get_strategy_snapshot(self, strategy_name, include_kline_names=[]): + """ + 实时获取策略的K线切片(比较耗性能) + :param strategy_name: 策略实例 + :param include_kline_names: 指定若干kline名称 + :return: + """ strategy = self.strategies.get(strategy_name, None) if strategy is None: return None try: - # 5.保存策略切片 - snapshot = strategy.get_klines_snapshot() + # 5.获取策略切片 + snapshot = strategy.get_klines_snapshot(include_kline_names) if not snapshot: self.write_log(f'{strategy_name}返回得K线切片数据为空') return None diff --git a/vnpy/app/cta_stock/template.py b/vnpy/app/cta_stock/template.py index 2414ffa3..6ad72cb5 100644 --- a/vnpy/app/cta_stock/template.py +++ b/vnpy/app/cta_stock/template.py @@ -575,6 +575,28 @@ class CtaStockTemplate(CtaTemplate): self.write_error(f'加载缓存K线数据失败:{str(ex)}') return None + def get_klines_info(self): + """ + 返回当前所有kline的信息 + :return: {"股票中文":[kline_name1, kline_name2]} + """ + info = {} + for kline_name in list(self.klines.keys()): + # 策略中如果kline不是按照 vtsymbol_xxxx 的命名方式,需要策略内部自行实现方法 + vt_symbol = kline_name.split('_')[0] + # vt_symbol => 中文名 + cn_name = self.cta_engine.get_name(vt_symbol) + + # 添加到列表 => 排序 + kline_names = info.get(cn_name, []) + kline_names.append(kline_name) + kline_names = sorted(kline_names) + + # 更新 + info[cn_name] = kline_names + + return info + def get_klines_snapshot(self, include_kline_names=[]): """ 返回当前klines的切片数据 @@ -747,6 +769,8 @@ class CtaStockTemplate(CtaTemplate): dist_record['operation'] = 'sell' pos.volume -= trade.volume + self.positions[trade.vt_symbol] = pos + self.save_dist(dist_record) def on_order(self, order: OrderData): @@ -1122,7 +1146,6 @@ class CtaStockTemplate(CtaTemplate): else: self.write_log(f'{vt_symbol} 已委托卖出,{sell_volume},委托价:{sell_price}, 数量:{sell_volume}') - def tns_finish_sell_grid(self, grid:CtaGrid): """ 事务完成卖出网格 diff --git a/vnpy/app/cta_stock/ui/widget.py b/vnpy/app/cta_stock/ui/widget.py index bec1a5a1..53348690 100644 --- a/vnpy/app/cta_stock/ui/widget.py +++ b/vnpy/app/cta_stock/ui/widget.py @@ -290,11 +290,19 @@ class StrategyManager(QtWidgets.QFrame): 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) + kline_info = self.cta_engine.get_strategy_kline_names(self.strategy_name) + + selector = KlineSelectDialog(kline_info,self.strategy_name) + n = selector.exec_() + + if n == selector.Accepted: + klines = selector.get_klines() + if len(klines) > 0: + snapshot = self.cta_engine.get_strategy_snapshot(self.strategy_name,klines) + if snapshot is None: + return + ui_snapshot = UiSnapshot() + ui_snapshot.show(snapshot_file="", d=snapshot) class DataMonitor(QtWidgets.QTableWidget): """ @@ -397,6 +405,72 @@ class LogMonitor(BaseMonitor): super(LogMonitor, self).insert_new_row(data) self.resizeRowToContents(0) +class KlineSelectDialog(QtWidgets.QDialog): + """ + 多K线选择窗口 + """ + def __init__( + self, info: dict, strategy_name:str + ): + """ + 构造函数 + :param info: 所有k线的配置 + :param strategy_name: + """ + super(KlineSelectDialog, self).__init__() + + self.info = info + self.strategy_name = strategy_name + self.t = None + self.select_names = [] + + self.init_ui() + + def init_ui(self): + """""" + form = QtWidgets.QFormLayout() + self.t = QtWidgets.QTableWidget(len(self.info), 2) + + self.t.setHorizontalHeaderLabels(['股票', 'K线']) + row = 0 + for k, v in self.info.items(): + + item = QtWidgets.QTableWidgetItem() + item.setText(k) + self.t.setItem(row, 0, item) + + klines = QtWidgets.QTableWidgetItem() + klines.setText(','.join(v)) + self.t.setItem(row,1, klines) + row +=1 + + # 单选 + self.t.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) + # self.t.cellPressed.conect(self.cell_select) + form.addWidget(self.t) + button = QtWidgets.QPushButton('确定') + button.clicked.connect(self.accept) + form.addRow(button) + self.setLayout(form) + + def cell_select(self,row,col): + try: + content = self.t.item(row,0).text() + self.select_names = self.info.get(content,[]) + except Exception as ex: + pass + + def get_klines(self): + """""" + selectedItems = self.t.selectedItems() + for item in selectedItems: + cur_row = item.row() + content = item.text() + self.select_names = self.info.get(content, []) + if len(self.select_names) > 0: + return self.select_names + return self.select_names + class SettingEditor(QtWidgets.QDialog): """ diff --git a/vnpy/component/base.py b/vnpy/component/base.py index 5cdfd638..17523e0c 100644 --- a/vnpy/component/base.py +++ b/vnpy/component/base.py @@ -7,7 +7,7 @@ from enum import Enum from logging import INFO, ERROR import json import numpy as np -import datetime +from datetime import datetime from vnpy.trader.constant import Direction # noqa diff --git a/vnpy/data/tdx/tdx_stock_data.py b/vnpy/data/tdx/tdx_stock_data.py index f293544c..2ef2c9ff 100644 --- a/vnpy/data/tdx/tdx_stock_data.py +++ b/vnpy/data/tdx/tdx_stock_data.py @@ -201,7 +201,13 @@ class TdxStockData(object): self.connection_status = True except Exception as ex: - self.write_log(u'连接服务器tdx异常:{},{}'.format(str(ex), traceback.format_exc())) + self.write_log(u'连接服务器{}tdx异常:{},{}'.format(self.best_ip,str(ex), traceback.format_exc())) + cur_ip = self.best_ip.get('ip',None) + if cur_ip is not None and cur_ip not in self.exclude_ips: + self.write_log(f'排除{cur_ip}') + self.exclude_ips.append(cur_ip) + self.best_ip = {} + return def disconnect(self): diff --git a/vnpy/trader/constant.py b/vnpy/trader/constant.py index d897940b..7ba1b4ca 100644 --- a/vnpy/trader/constant.py +++ b/vnpy/trader/constant.py @@ -163,6 +163,7 @@ class Interval(Enum): HOUR = "1h" DAILY = "d" WEEKLY = "w" + MONTHLY = 'M' RENKO = 'renko' diff --git a/vnpy/trader/ui/widget.py b/vnpy/trader/ui/widget.py index 53320985..dba95511 100644 --- a/vnpy/trader/ui/widget.py +++ b/vnpy/trader/ui/widget.py @@ -397,6 +397,7 @@ class TradeMonitor(BaseMonitor): "tradeid": {"display": "成交号 ", "cell": BaseCell, "update": False}, "orderid": {"display": "委托号", "cell": BaseCell, "update": False}, "symbol": {"display": "代码", "cell": BaseCell, "update": False}, + "name": {"display": "名称", "cell": BaseCell, "update": False}, "exchange": {"display": "交易所", "cell": EnumCell, "update": False}, "direction": {"display": "方向", "cell": DirectionCell, "update": False}, "offset": {"display": "开平", "cell": EnumCell, "update": False}, @@ -419,6 +420,7 @@ class OrderMonitor(BaseMonitor): headers: Dict[str, dict] = { "orderid": {"display": "委托号", "cell": BaseCell, "update": False}, "symbol": {"display": "代码", "cell": BaseCell, "update": False}, + "name": {"display": "名称", "cell": BaseCell, "update": False}, "exchange": {"display": "交易所", "cell": EnumCell, "update": False}, "type": {"display": "类型", "cell": EnumCell, "update": False}, "direction": {"display": "方向", "cell": DirectionCell, "update": False},