From 28152c2852dbebe37d54bb0c746d539ac8ad5040 Mon Sep 17 00:00:00 2001 From: msincenselee Date: Fri, 21 Jun 2019 23:27:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnpy/trader/uiBasicWidget.py | 58 +++++++++------ vnpy/trader/uiMainWindow.py | 11 +-- vnpy/trader/util_gpid.py | 13 +++- vnpy/trader/util_monitor.py | 1 + vnpy/trader/vtClient.py | 39 +++++++++- vnpy/trader/vtFunction.py | 6 +- vnpy/trader/vtGateway.py | 4 +- vnpy/trader/vtObject.py | 135 ++++++++++++++++++++++++++++++++++- 8 files changed, 228 insertions(+), 39 deletions(-) diff --git a/vnpy/trader/uiBasicWidget.py b/vnpy/trader/uiBasicWidget.py index b26d2635..503443b3 100644 --- a/vnpy/trader/uiBasicWidget.py +++ b/vnpy/trader/uiBasicWidget.py @@ -741,6 +741,8 @@ class TradingWidget(QtWidgets.QFrame): EXCHANGE_CZCE, EXCHANGE_SSE, EXCHANGE_SZSE, + EXCHANGE_XSHG, + EXCHANGE_XSHE, EXCHANGE_INE, EXCHANGE_SGE, EXCHANGE_HKEX, @@ -984,22 +986,27 @@ class TradingWidget(QtWidgets.QFrame): currency = self.comboCurrency.currentText() productClass = self.comboProductClass.currentText() gatewayName = self.comboGateway.currentText() - - # 查询合约 - if exchange: - vtSymbol = '.'.join([symbol, exchange]) - contract = self.mainEngine.getContract(vtSymbol) - else: - vtSymbol = symbol - contract = self.mainEngine.getContract(symbol) - - if contract: - vtSymbol = contract.vtSymbol - if len(gatewayName)==0 and len(contract.gatewayName) > 0: - gatewayName = contract.gatewayName - self.lineName.setText(contract.name) - exchange = contract.exchange # 保证有交易所代码 - + + try: + # 查询合约 + if exchange: + vtSymbol = '.'.join([symbol, exchange]) + contract = self.mainEngine.getContract(vtSymbol) + else: + vtSymbol = symbol + contract = self.mainEngine.getContract(symbol) + + if contract: + vtSymbol = contract.vtSymbol + if len(gatewayName)==0 and contract.gatewayName is not None and len(contract.gatewayName) > 0: + gatewayName = contract.gatewayName + self.lineName.setText(contract.name) + exchange = contract.exchange # 保证有交易所代码 + + except Exception as ex: + print(u'获取合约{}异常:{},{}'.format(symbol,str(ex),traceback.format_exc()),file=sys.stderr) + return + # 清空价格数量 self.spinPrice.setValue(0) #self.spinVolume.setValue(0) @@ -1103,6 +1110,8 @@ class TradingWidget(QtWidgets.QFrame): else: self.labelReturn.setText('') + self.comboExchange.setCurrentText(tick.exchange) + #---------------------------------------------------------------------- def connectSignal(self): """连接Signal""" @@ -1244,8 +1253,15 @@ class TradingWidget(QtWidgets.QFrame): return if tick.vtSymbol: + # 更新交易组件的显示合约 - self.lineSymbol.setText(tick.vtSymbol) + if '.' in tick.vtSymbol: + symbol_pairs = tick.vtSymbol.split('.') + if symbol_pairs[-1] != 'SPD': + self.lineSymbol.setText(symbol_pairs[0]) + self.comboExchange.setCurrentText(symbol_pairs[-1]) + else: + self.lineSymbol.setText(tick.vtSymbol) self.updateSymbol() # 自动填写信息 @@ -1302,8 +1318,7 @@ class ContractMonitor(BasicMonitor): d = {'.'.join([contract.exchange, contract.symbol]):contract for contract in l} l2 = d.keys() #l2.sort(reverse=True) - l2 = sorted(l2, reverse=True) - + l2 = sorted(l2,reverse=True) self.setRowCount(len(l2)) row = 0 @@ -1358,7 +1373,7 @@ class ContractMonitor(BasicMonitor): class ContractManager(QtWidgets.QWidget): """合约管理组件""" - # ---------------------------------------------------------------------- + #---------------------------------------------------------------------- def __init__(self, mainEngine, parent=None): """Constructor""" super(ContractManager, self).__init__(parent=parent) @@ -1367,7 +1382,7 @@ class ContractManager(QtWidgets.QWidget): self.initUi() - # ---------------------------------------------------------------------- + #---------------------------------------------------------------------- def initUi(self): """初始化界面""" self.setWindowTitle(vtText.CONTRACT_SEARCH) @@ -1426,6 +1441,7 @@ class WorkingOrderMonitor(OrderMonitor): row = self.row(cell) self.hideRow(row) + ######################################################################## class SettingEditor(QtWidgets.QWidget): """配置编辑器""" diff --git a/vnpy/trader/uiMainWindow.py b/vnpy/trader/uiMainWindow.py index 607e0518..9980c8af 100644 --- a/vnpy/trader/uiMainWindow.py +++ b/vnpy/trader/uiMainWindow.py @@ -348,14 +348,15 @@ class MainWindow(QtWidgets.QMainWindow): return openAppFunction - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- def test(self): """测试按钮用的函数""" # 有需要使用手动触发的测试函数可以写在这里 self.mainEngine.qryStatus() + self.mainEngine.saveData() pass - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- def openAbout(self): """打开关于""" try: @@ -424,7 +425,7 @@ class MainWindow(QtWidgets.QMainWindow): except: pass - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- def closeEvent(self, event): """关闭事件""" reply = QtWidgets.QMessageBox.question(self, vtText.EXIT, @@ -441,7 +442,7 @@ class MainWindow(QtWidgets.QMainWindow): else: event.ignore() - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- def createDock(self, widgetClass, widgetName, widgetArea): """创建停靠组件""" widget = widgetClass(self.mainEngine, self.eventEngine) @@ -459,7 +460,7 @@ class MainWindow(QtWidgets.QMainWindow): settings.setValue('state', self.saveState()) settings.setValue('geometry', self.saveGeometry()) - #---------------------------------------------------------------------- + # ---------------------------------------------------------------------- def loadWindowSettings(self, settingName): """载入窗口设置""" settings = QtCore.QSettings('vn.trader', settingName) diff --git a/vnpy/trader/util_gpid.py b/vnpy/trader/util_gpid.py index 3ee1042b..734d27dd 100644 --- a/vnpy/trader/util_gpid.py +++ b/vnpy/trader/util_gpid.py @@ -6,6 +6,9 @@ import sys import platform import psutil +# changelog +# 记录gpid,修改为pid + run_path = os.path.abspath(os.path.join(os.getcwd(), 'logs')) if not os.path.isdir(run_path): os.mkdir(run_path) @@ -68,19 +71,23 @@ if _status(): print( u'another service is already running...') exit(0) -def _save_gpid(): +def _save_gpid(log=True): plat = str(platform.system()) if plat == 'Windows': gpid = os.getpid() else: # unix gpid = os.getpgrp() if USE_GPID else os.getpid() - print( 'gpid={}'.format(gpid)) + if log: + print( 'gpid={}'.format(gpid)) with open(gpid_file, 'w') as f: f.write(str(gpid)) + if log: + print(u'wrote gpid file:{}'.format(gpid_file)) - print(u'wrote gpid file:{}'.format(gpid_file)) +def update_gpid(): + _save_gpid(log=False) _save_gpid() diff --git a/vnpy/trader/util_monitor.py b/vnpy/trader/util_monitor.py index 042f8e80..d0f5e7b1 100644 --- a/vnpy/trader/util_monitor.py +++ b/vnpy/trader/util_monitor.py @@ -274,6 +274,7 @@ class PositionMonitor(BasicMonitor): d['ydPosition'] = {'chinese': u'昨持仓', 'cellType': ""} d['frozen'] = {'chinese': u'冻结量', 'cellType': ""} d['price'] = {'chinese': u'价格', 'cellType': ""} + d['positionProfit'] = {'chinese': u'持仓盈亏', 'cellType': ""} d['gatewayName'] = {'chinese': u'接口', 'cellType': ""} self.setHeaderDict(d) self.createLogger(EVENT_POSITION) diff --git a/vnpy/trader/vtClient.py b/vnpy/trader/vtClient.py index d827ce11..921a0cdb 100644 --- a/vnpy/trader/vtClient.py +++ b/vnpy/trader/vtClient.py @@ -176,10 +176,45 @@ class ClientEngine(object): self.client.initStrategy(name, force=force) def startStrategy(self,name): - self.client.startStrategy(name) + if hasattr(self.client,'startStrategy'): + self.writeLog(u'启动服务端策略:{}'.format(name)) + self.client.startStrategy(name) + else: + self.writeLog(u'RPC客户端没有startStrategy得方法') + print(u'RPC客户端没有startStrategy得方法',file=sys.stderr) def stopStrategy(self,name): - self.client.stopStrategy(name) + if hasattr(self.client,'stopStrategy'): + self.writeLog(u'停止运行服务端策略:{}'.format(name)) + self.client.stopStrategy(name) + else: + self.writeLog(u'RPC客户端没有stopStrategy得方法') + print(u'RPC客户端没有stopStrategy得方法',file=sys.stderr) + + def removeStrategy(self,name): + if hasattr(self.client,'removeStrategy'): + self.writeLog(u'移除服务端策略:{}'.format(name)) + self.client.removeStrategy(name) + else: + self.writeLog(u'RPC客户端没有removeStrategy得方法') + print(u'RPC客户端没有removeStrategy得方法',file=sys.stderr) + + def addStrategy(self,cta_setting): + if hasattr(self.client,'addStrategy'): + self.writeLog(u'添加服务端策略:{}'.format(cta_setting.get('name'))) + self.client.addStrategy(cta_setting) + else: + self.writeLog(u'RPC客户端没有addStrategy的方法') + print(u'RPC客户端没有addStrategy的方法', file=sys.stderr) + + def forceClosePos(self,name): + if hasattr(self.client,'forceClosePos'): + self.writeLog(u'调用服务端策略强制清除仓位:{}'.format(name)) + self.client.forceClosePos(name) + else: + self.writeLog(u'RPC客户端没有forceClosePos得方法') + print(u'RPC客户端没有forceClosePos得方法', file=sys.stderr) + #---------------------------------------------------------------------- def main(): """客户端主程序入口""" diff --git a/vnpy/trader/vtFunction.py b/vnpy/trader/vtFunction.py index f1933c5b..f6cd988c 100644 --- a/vnpy/trader/vtFunction.py +++ b/vnpy/trader/vtFunction.py @@ -101,13 +101,13 @@ def roundToVolumeTick(volumeTick,volume): if volumeTick == 0: return volume # 取整 - newVolume = volume - volume % volumeTick + newVolume = round(volume / volumeTick, 0) * volumeTick if isinstance(volumeTick,float): - v_exponent = decimal.Decimal(str(newVolume)) + v_exponent = decimal.Decimal(str(volume)) vt_exponent = decimal.Decimal(str(volumeTick)) if abs(v_exponent.as_tuple().exponent) > abs(vt_exponent.as_tuple().exponent): - newVolume = round(newVolume, ndigits=abs(vt_exponent.as_tuple().exponent)) + newVolume = round(volume, ndigits=abs(vt_exponent.as_tuple().exponent)) newVolume = float(str(newVolume)) return newVolume diff --git a/vnpy/trader/vtGateway.py b/vnpy/trader/vtGateway.py index 6e7f5764..4ff7c542 100644 --- a/vnpy/trader/vtGateway.py +++ b/vnpy/trader/vtGateway.py @@ -36,7 +36,7 @@ class VtGateway(object): event1.dict_['data'] = tick self.eventEngine.put(event1) - if tick.lastPrice is not None or tick.lastPrice != 0: + if tick.lastPrice is not None and tick.lastPrice != 0: self.symbol_price_dict.update({tick.vtSymbol: tick.lastPrice}) elif tick.askPrice1 is not None and tick.bidPrice1 is not None: self.symbol_price_dict.update({tick.vtSymbol: (tick.askPrice1 + tick.bidPrice1)/2}) @@ -147,7 +147,7 @@ class VtGateway(object): filename = os.path.abspath(os.path.join(path, 'Gateway')) - print(u'create logger:{}'.format(filename)) + #print(u'create logger:{}'.format(filename)) self.logger = setup_logger(filename=filename, name='vtGateway', debug=True) # ---------------------------------------------------------------------- diff --git a/vnpy/trader/vtObject.py b/vnpy/trader/vtObject.py index ba5ec3b0..6ba5eb55 100644 --- a/vnpy/trader/vtObject.py +++ b/vnpy/trader/vtObject.py @@ -5,7 +5,10 @@ from datetime import datetime from vnpy.trader.vtConstant import (EMPTY_STRING, EMPTY_UNICODE, - EMPTY_FLOAT, EMPTY_INT) + EMPTY_FLOAT, EMPTY_INT, DIRECTION_LONG, DIRECTION_SHORT, + OFFSET_OPEN, OFFSET_CLOSE, + OFFSET_CLOSETODAY, OFFSET_CLOSEYESTERDAY) + from vnpy.trader.language import constant ######################################################################## @@ -40,7 +43,7 @@ class VtTickData(VtBaseData): self.preOpenInterest = EMPTY_INT # 昨持仓量 self.openInterest = EMPTY_INT # 持仓量 self.time = EMPTY_STRING # 时间 11:20:56.5 - self.date = EMPTY_STRING # 日期 20151009 + self.date = EMPTY_STRING # 日期 2015-10-09 self.tradingDay = EMPTY_STRING # 交易日期 # 常规行情 @@ -96,7 +99,7 @@ class VtTickData(VtBaseData): tick.lastVolume = lastVolume tick.openInterest = openInterest tick.datetime = datetime.now() - tick.date = tick.datetime.strftime('%Y%m%d') + tick.date = tick.datetime.strftime('%Y-%m-%d') tick.time = tick.datetime.strftime('%H:%M:%S') tick.openPrice = openPrice @@ -158,6 +161,9 @@ class VtTradeData(VtBaseData): self.price = EMPTY_FLOAT # 成交价格 self.volume = EMPTY_FLOAT # 成交数量 self.tradeTime = EMPTY_STRING # 成交时间 + self.dt = None # 成交时间(datetime类型) + + self.strategy = EMPTY_STRING # 策略实例名 #---------------------------------------------------------------------- @staticmethod @@ -473,6 +479,8 @@ class VtSubscribeReq(object): self.symbol = EMPTY_STRING # 代码 self.exchange = EMPTY_STRING # 交易所 + self.is_bar = False # True:订阅1分钟bar行情;False:订阅tick行情, + # 以下为IB相关 self.productClass = EMPTY_UNICODE # 合约类型 self.currency = EMPTY_STRING # 合约货币 @@ -556,3 +564,124 @@ class VtSingleton(type): cls._instances[cls] = super(VtSingleton, cls).__call__(*args, **kwargs) return cls._instances[cls] + + +######################################################################## +class PositionBuffer(object): + """持仓缓存信息(本地维护的持仓数据)""" + + # ---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.vtSymbol = EMPTY_STRING + + # 多头 + self.longPosition = EMPTY_INT + self.longToday = EMPTY_INT + self.longYd = EMPTY_INT + + self.longPrice = EMPTY_FLOAT + self.longProfit = EMPTY_FLOAT + self.longFrozen = EMPTY_FLOAT + + # 空头 + self.shortPosition = EMPTY_INT + self.shortToday = EMPTY_INT + self.shortYd = EMPTY_INT + + self.shortPrice = EMPTY_FLOAT + self.shortProfit = EMPTY_FLOAT + + self.shortFrozen = EMPTY_FLOAT + + self.frozen = EMPTY_FLOAT + + # ---------------------------------------------------------------------- + def toStr(self): + """更新显示信息""" + str = u'long:{},yd:{},td:{}, short:{},yd:{},td:{}, fz:{};' \ + .format(self.longPosition, self.longYd, self.longToday, + self.shortPosition, self.shortYd, self.shortToday, self.frozen) + return str + + # ---------------------------------------------------------------------- + def updatePositionData(self, pos): + """更新持仓数据""" + if pos.direction == DIRECTION_SHORT: + self.shortPosition = pos.position # >=0 + self.shortYd = pos.ydPosition # >=0 + self.shortToday = self.shortPosition - self.shortYd # >=0 + + self.shortPrice = pos.price + self.shortProfit = pos.positionProfit + self.shortFrozen = pos.frozen + + else: + self.longPosition = pos.position # >=0 + self.longYd = pos.ydPosition # >=0 + self.longToday = self.longPosition - self.longYd # >=0 + + self.longPrice = pos.price + self.longProfit = pos.positionProfit + self.longFrozen = pos.frozen + + self.frozen = self.shortFrozen + self.longFrozen + + # ---------------------------------------------------------------------- + def updateTradeData(self, trade): + """更新成交数据""" + + if trade.direction == DIRECTION_SHORT: + # 空头和多头相同 + if trade.offset == OFFSET_OPEN: # 开仓 + self.shortPosition += trade.volume + self.shortToday += trade.volume # 增加的是今仓 + + elif trade.offset == OFFSET_CLOSETODAY: # 平今 + self.longPosition -= trade.volume + self.longToday -= trade.volume # 减少今仓 + + elif trade.offset == OFFSET_CLOSEYESTERDAY: # 平昨 + self.longPosition -= trade.volume + self.longYd -= trade.volume # 减少昨仓 + + else: # 平仓 + self.longPosition -= trade.volume + self.longToday -= trade.volume # 优先减今仓 + if self.longToday < 0: + self.longYd += self.longToday + self.longToday = 0 + + if self.longPosition <= 0: + self.longPosition = 0 + if self.longToday <= 0: + self.longToday = 0 + if self.longYd <= 0: + self.longYd = 0 + else: + # 多方开仓,则对应多头的持仓和今仓增加 + if trade.offset == OFFSET_OPEN: + self.longPosition += trade.volume + self.longToday += trade.volume + # 多方平今,对应空头的持仓和今仓减少 + elif trade.offset == OFFSET_CLOSETODAY: + self.shortPosition -= trade.volume + self.shortToday -= trade.volume + + elif trade.offset == OFFSET_CLOSEYESTERDAY: + self.shortPosition -= trade.volume + self.shortYd -= trade.volume + else: + self.shortPosition -= trade.volume + self.shortToday -= trade.volume + if self.shortToday <= 0: + self.shortYd += self.shortToday + self.shortToday = 0 + + if self.shortPosition <= 0: + self.shortPosition = 0 + if self.shortToday <= 0: + self.shortToday = 0 + if self.shortYd <= 0: + self.shortYd = 0 + # 多方平昨,对应空头的持仓和昨仓减少 \ No newline at end of file