diff --git a/.gitignore b/.gitignore index 6c09c8e3..2601a9f6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ Release/ # Python编译文件 *.pyc +*.pyo # WingIDE文件 *.wpr diff --git a/examples/CtaBacktesting/backtesting.ipynb b/examples/CtaBacktesting/backtesting.ipynb index 0540c100..d4f7a29b 100644 --- a/examples/CtaBacktesting/backtesting.ipynb +++ b/examples/CtaBacktesting/backtesting.ipynb @@ -79,13 +79,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "2017-07-06 23:15:17.471000\t开始载入数据\n", - "2017-07-06 23:15:17.485000\t载入完成,数据量:0\n", - "2017-07-06 23:15:17.485000\t开始回测\n", - "2017-07-06 23:15:17.485000\t策略初始化完成\n", - "2017-07-06 23:15:17.485000\t策略启动完成\n", - "2017-07-06 23:15:17.485000\t开始回放数据\n", - "2017-07-06 23:15:17.486000\t数据回放结束\n" + "2017-07-10 22:55:05.124000\t开始载入数据\n", + "2017-07-10 22:55:05.288000\t载入完成,数据量:331890\n", + "2017-07-10 22:55:05.288000\t开始回测\n", + "2017-07-10 22:55:05.310000\t策略初始化完成\n", + "2017-07-10 22:55:05.310000\t策略启动完成\n", + "2017-07-10 22:55:05.310000\t开始回放数据\n", + "2017-07-10 22:55:29.343000\t数据回放结束\n" ] } ], @@ -105,27 +105,27 @@ "name": "stdout", "output_type": "stream", "text": [ - "2017-06-02 16:09:14.068000\t计算回测结果\n", - "2017-06-02 16:09:14.134000\t------------------------------\n", - "2017-06-02 16:09:14.134000\t第一笔交易:\t2012-01-11 10:18:00\n", - "2017-06-02 16:09:14.134000\t最后一笔交易:\t2017-03-20 09:31:00\n", - "2017-06-02 16:09:14.134000\t总交易次数:\t3,749.0\n", - "2017-06-02 16:09:14.134000\t总盈亏:\t683,717.31\n", - "2017-06-02 16:09:14.134000\t最大回撤: \t-215,542.46\n", - "2017-06-02 16:09:14.134000\t平均每笔盈利:\t182.37\n", - "2017-06-02 16:09:14.134000\t平均每笔滑点:\t120.0\n", - "2017-06-02 16:09:14.134000\t平均每笔佣金:\t57.75\n", - "2017-06-02 16:09:14.134000\t胜率\t\t37.24%\n", - "2017-06-02 16:09:14.134000\t盈利交易平均值\t8,539.01\n", - "2017-06-02 16:09:14.134000\t亏损交易平均值\t-4,775.5\n", - "2017-06-02 16:09:14.134000\t盈亏比:\t1.79\n" + "2017-07-10 22:59:09.996000\t计算回测结果\n", + "2017-07-10 22:59:10.061000\t------------------------------\n", + "2017-07-10 22:59:10.061000\t第一笔交易:\t2012-01-11 10:18:00\n", + "2017-07-10 22:59:10.061000\t最后一笔交易:\t2017-03-20 09:31:00\n", + "2017-07-10 22:59:10.061000\t总交易次数:\t3,749.0\n", + "2017-07-10 22:59:10.061000\t总盈亏:\t683,717.31\n", + "2017-07-10 22:59:10.061000\t最大回撤: \t-215,542.46\n", + "2017-07-10 22:59:10.061000\t平均每笔盈利:\t182.37\n", + "2017-07-10 22:59:10.061000\t平均每笔滑点:\t120.0\n", + "2017-07-10 22:59:10.061000\t平均每笔佣金:\t57.75\n", + "2017-07-10 22:59:10.061000\t胜率\t\t37.24%\n", + "2017-07-10 22:59:10.061000\t盈利交易平均值\t8,539.01\n", + "2017-07-10 22:59:10.061000\t亏损交易平均值\t-4,775.5\n", + "2017-07-10 22:59:10.061000\t盈亏比:\t1.79\n" ] }, { "data": { "image/png": "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\nAElFTkSuQmCC\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -169,46 +169,11 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": { "collapsed": false }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2017-06-02 16:13:08.744000\t------------------------------\n", - "2017-06-02 16:13:08.744000\t优化结果:\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 20, 'atrLength': 20}: 957797.864155\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 25, 'atrLength': 20}: 957797.864155\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 30, 'atrLength': 20}: 957797.864155\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 20, 'atrLength': 18}: 883361.518728\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 25, 'atrLength': 18}: 883361.518728\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 30, 'atrLength': 18}: 883361.518728\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 20, 'atrLength': 16}: 790520.582818\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 25, 'atrLength': 16}: 790520.582818\n", - "2017-06-02 16:13:08.744000\t{'rsiLength': 5, 'atrMa': 30, 'atrLength': 16}: 790520.582818\n", - "2017-06-02 16:13:08.745000\t{'rsiLength': 5, 'atrMa': 20, 'atrLength': 14}: 768931.704634\n", - "2017-06-02 16:13:08.745000\t{'rsiLength': 5, 'atrMa': 25, 'atrLength': 14}: 768931.704634\n", - "2017-06-02 16:13:08.745000\t{'rsiLength': 5, 'atrMa': 30, 'atrLength': 14}: 768931.704634\n", - "2017-06-02 16:13:08.745000\t{'rsiLength': 5, 'atrMa': 20, 'atrLength': 12}: 758396.03149\n", - "2017-06-02 16:13:08.745000\t{'rsiLength': 5, 'atrMa': 25, 'atrLength': 12}: 758396.03149\n", - "2017-06-02 16:13:08.745000\t{'rsiLength': 5, 'atrMa': 30, 'atrLength': 12}: 758396.03149\n" - ] - }, - { - "ename": "NameError", - "evalue": "name 'start' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 9\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mtime\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 10\u001b[0m \u001b[0mengine\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrunParallelOptimization\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mAtrRsiStrategy\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msetting\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 11\u001b[1;33m \u001b[1;32mprint\u001b[0m \u001b[1;34mu'耗时:%s'\u001b[0m \u001b[1;33m%\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtime\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m-\u001b[0m\u001b[0mstart\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;31mNameError\u001b[0m: name 'start' is not defined" - ] - } - ], + "outputs": [], "source": [ "# 优化配置\n", "setting = OptimizationSetting() # 新建一个优化任务设置对象\n", diff --git a/vnpy/trader/app/ctaStrategy/ctaBacktesting.py b/vnpy/trader/app/ctaStrategy/ctaBacktesting.py index c3d63f6f..b2216a71 100644 --- a/vnpy/trader/app/ctaStrategy/ctaBacktesting.py +++ b/vnpy/trader/app/ctaStrategy/ctaBacktesting.py @@ -11,13 +11,14 @@ from collections import OrderedDict from itertools import product import multiprocessing import pymongo +import copy from vnpy.trader.vtGlobal import globalSetting from vnpy.trader.vtObject import VtTickData, VtBarData from vnpy.trader.vtConstant import * from vnpy.trader.vtGateway import VtOrderData, VtTradeData -from vnpy.trader.app.ctaStrategy.ctaBase import * +from .ctaBase import * ######################################################################## @@ -34,19 +35,15 @@ class BacktestingEngine(object): #---------------------------------------------------------------------- def __init__(self): """Constructor""" - # 本地停止单编号计数 - self.stopOrderCount = 0 - # stopOrderID = STOPORDERPREFIX + str(stopOrderCount) + # 本地停止单 + self.stopOrderCount = 0 # 编号计数:stopOrderID = STOPORDERPREFIX + str(stopOrderCount) - # 本地停止单字典 - # key为stopOrderID,value为stopOrder对象 + # 本地停止单字典, key为stopOrderID,value为stopOrder对象 self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除 self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除 - # 引擎类型为回测 - self.engineType = ENGINETYPE_BACKTESTING + self.engineType = ENGINETYPE_BACKTESTING # 引擎类型为回测 - # 回测相关 self.strategy = None # 回测策略 self.mode = self.BAR_MODE # 回测模式,默认为K线 @@ -62,10 +59,7 @@ class BacktestingEngine(object): self.dbClient = None # 数据库客户端 self.dbCursor = None # 数据库指针 - #self.historyData = [] # 历史数据的列表,回测用 self.initData = [] # 初始化用的数据 - #self.backtestingData = [] # 回测用的数据 - self.dbName = '' # 回测数据库名 self.symbol = '' # 回测集合名 @@ -73,9 +67,9 @@ class BacktestingEngine(object): self.dataEndDate = None # 回测数据结束日期,datetime对象 self.strategyStartDate = None # 策略启动日期(即前面的数据用于初始化),datetime对象 + self.limitOrderCount = 0 # 限价单编号 self.limitOrderDict = OrderedDict() # 限价单字典 self.workingLimitOrderDict = OrderedDict() # 活动限价单字典,用于进行撮合用 - self.limitOrderCount = 0 # 限价单编号 self.tradeCount = 0 # 成交编号 self.tradeDict = OrderedDict() # 成交字典 @@ -87,6 +81,31 @@ class BacktestingEngine(object): self.bar = None self.dt = None # 最新的时间 + # 日线回测结果计算用 + self.dailyResultDict = OrderedDict() + + #------------------------------------------------ + # 通用功能 + #------------------------------------------------ + + #---------------------------------------------------------------------- + def roundToPriceTick(self, price): + """取整价格到合约最小价格变动""" + if not self.priceTick: + return price + + newPrice = round(price/self.priceTick, 0) * self.priceTick + return newPrice + + #---------------------------------------------------------------------- + def output(self, content): + """输出内容""" + print str(datetime.now()) + "\t" + content + + #------------------------------------------------ + # 参数设置相关 + #------------------------------------------------ + #---------------------------------------------------------------------- def setStartDate(self, startDate='20100416', initDays=10): """设置回测的启动日期""" @@ -120,6 +139,30 @@ class BacktestingEngine(object): self.dbName = dbName self.symbol = symbol + #---------------------------------------------------------------------- + def setSlippage(self, slippage): + """设置滑点点数""" + self.slippage = slippage + + #---------------------------------------------------------------------- + def setSize(self, size): + """设置合约大小""" + self.size = size + + #---------------------------------------------------------------------- + def setRate(self, rate): + """设置佣金比例""" + self.rate = rate + + #---------------------------------------------------------------------- + def setPriceTick(self, priceTick): + """设置价格最小变动""" + self.priceTick = priceTick + + #------------------------------------------------ + # 数据回放相关 + #------------------------------------------------ + #---------------------------------------------------------------------- def loadHistoryData(self): """载入历史数据""" @@ -196,19 +239,25 @@ class BacktestingEngine(object): """新的K线""" self.bar = bar self.dt = bar.datetime + self.crossLimitOrder() # 先撮合限价单 self.crossStopOrder() # 再撮合停止单 self.strategy.onBar(bar) # 推送K线到策略中 + + self.updateDailyClose(bar.datetime, bar.closePrice) #---------------------------------------------------------------------- def newTick(self, tick): """新的Tick""" self.tick = tick self.dt = tick.datetime + self.crossLimitOrder() self.crossStopOrder() self.strategy.onTick(tick) + self.updateDailyClose(tick.datetime, tick.lastPrice) + #---------------------------------------------------------------------- def initStrategy(self, strategyClass, setting=None): """ @@ -217,96 +266,7 @@ class BacktestingEngine(object): """ self.strategy = strategyClass(self, setting) self.strategy.name = self.strategy.className - - #---------------------------------------------------------------------- - def sendOrder(self, vtSymbol, orderType, price, volume, strategy): - """发单""" - self.limitOrderCount += 1 - orderID = str(self.limitOrderCount) - - order = VtOrderData() - order.vtSymbol = vtSymbol - order.price = self.roundToPriceTick(price) - order.totalVolume = volume - order.orderID = orderID - order.vtOrderID = orderID - order.orderTime = str(self.dt) - - # CTA委托类型映射 - if orderType == CTAORDER_BUY: - order.direction = DIRECTION_LONG - order.offset = OFFSET_OPEN - elif orderType == CTAORDER_SELL: - order.direction = DIRECTION_SHORT - order.offset = OFFSET_CLOSE - elif orderType == CTAORDER_SHORT: - order.direction = DIRECTION_SHORT - order.offset = OFFSET_OPEN - elif orderType == CTAORDER_COVER: - order.direction = DIRECTION_LONG - order.offset = OFFSET_CLOSE - - # 保存到限价单字典中 - self.workingLimitOrderDict[orderID] = order - self.limitOrderDict[orderID] = order - - return orderID - #---------------------------------------------------------------------- - def cancelOrder(self, vtOrderID): - """撤单""" - if vtOrderID in self.workingLimitOrderDict: - order = self.workingLimitOrderDict[vtOrderID] - order.status = STATUS_CANCELLED - order.cancelTime = str(self.dt) - del self.workingLimitOrderDict[vtOrderID] - - #---------------------------------------------------------------------- - def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy): - """发停止单(本地实现)""" - self.stopOrderCount += 1 - stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount) - - so = StopOrder() - so.vtSymbol = vtSymbol - so.price = self.roundToPriceTick(price) - so.volume = volume - so.strategy = strategy - so.status = STOPORDER_WAITING - so.stopOrderID = stopOrderID - - if orderType == CTAORDER_BUY: - so.direction = DIRECTION_LONG - so.offset = OFFSET_OPEN - elif orderType == CTAORDER_SELL: - so.direction = DIRECTION_SHORT - so.offset = OFFSET_CLOSE - elif orderType == CTAORDER_SHORT: - so.direction = DIRECTION_SHORT - so.offset = OFFSET_OPEN - elif orderType == CTAORDER_COVER: - so.direction = DIRECTION_LONG - so.offset = OFFSET_CLOSE - - # 保存stopOrder对象到字典中 - self.stopOrderDict[stopOrderID] = so - self.workingStopOrderDict[stopOrderID] = so - - # 推送停止单初始更新 - self.strategy.onStopOrder(so) - - return stopOrderID - - #---------------------------------------------------------------------- - def cancelStopOrder(self, stopOrderID): - """撤销停止单""" - # 检查停止单是否存在 - if stopOrderID in self.workingStopOrderDict: - so = self.workingStopOrderDict[stopOrderID] - so.status = STOPORDER_CANCELLED - del self.workingStopOrderDict[stopOrderID] - self.strategy.onStopOrder(so) - #---------------------------------------------------------------------- def crossLimitOrder(self): """基于最新数据撮合限价单""" @@ -451,7 +411,105 @@ class BacktestingEngine(object): self.strategy.onStopOrder(so) self.strategy.onOrder(order) self.strategy.onTrade(trade) - + + #------------------------------------------------ + # 策略接口相关 + #------------------------------------------------ + + #---------------------------------------------------------------------- + def sendOrder(self, vtSymbol, orderType, price, volume, strategy): + """发单""" + self.limitOrderCount += 1 + orderID = str(self.limitOrderCount) + + order = VtOrderData() + order.vtSymbol = vtSymbol + order.price = self.roundToPriceTick(price) + order.totalVolume = volume + order.orderID = orderID + order.vtOrderID = orderID + order.orderTime = str(self.dt) + + # CTA委托类型映射 + if orderType == CTAORDER_BUY: + order.direction = DIRECTION_LONG + order.offset = OFFSET_OPEN + elif orderType == CTAORDER_SELL: + order.direction = DIRECTION_SHORT + order.offset = OFFSET_CLOSE + elif orderType == CTAORDER_SHORT: + order.direction = DIRECTION_SHORT + order.offset = OFFSET_OPEN + elif orderType == CTAORDER_COVER: + order.direction = DIRECTION_LONG + order.offset = OFFSET_CLOSE + + # 保存到限价单字典中 + self.workingLimitOrderDict[orderID] = order + self.limitOrderDict[orderID] = order + + return orderID + + #---------------------------------------------------------------------- + def cancelOrder(self, vtOrderID): + """撤单""" + if vtOrderID in self.workingLimitOrderDict: + order = self.workingLimitOrderDict[vtOrderID] + order.status = STATUS_CANCELLED + order.cancelTime = str(self.dt) + del self.workingLimitOrderDict[vtOrderID] + + #---------------------------------------------------------------------- + def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy): + """发停止单(本地实现)""" + self.stopOrderCount += 1 + stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount) + + so = StopOrder() + so.vtSymbol = vtSymbol + so.price = self.roundToPriceTick(price) + so.volume = volume + so.strategy = strategy + so.status = STOPORDER_WAITING + so.stopOrderID = stopOrderID + + if orderType == CTAORDER_BUY: + so.direction = DIRECTION_LONG + so.offset = OFFSET_OPEN + elif orderType == CTAORDER_SELL: + so.direction = DIRECTION_SHORT + so.offset = OFFSET_CLOSE + elif orderType == CTAORDER_SHORT: + so.direction = DIRECTION_SHORT + so.offset = OFFSET_OPEN + elif orderType == CTAORDER_COVER: + so.direction = DIRECTION_LONG + so.offset = OFFSET_CLOSE + + # 保存stopOrder对象到字典中 + self.stopOrderDict[stopOrderID] = so + self.workingStopOrderDict[stopOrderID] = so + + # 推送停止单初始更新 + self.strategy.onStopOrder(so) + + return stopOrderID + + #---------------------------------------------------------------------- + def cancelStopOrder(self, stopOrderID): + """撤销停止单""" + # 检查停止单是否存在 + if stopOrderID in self.workingStopOrderDict: + so = self.workingStopOrderDict[stopOrderID] + so.status = STOPORDER_CANCELLED + del self.workingStopOrderDict[stopOrderID] + self.strategy.onStopOrder(so) + + #---------------------------------------------------------------------- + def putStrategyEvent(self, name): + """发送策略更新事件,回测中忽略""" + pass + #---------------------------------------------------------------------- def insertData(self, dbName, collectionName, data): """考虑到回测中不允许向数据库插入数据,防止实盘交易中的一些代码出错""" @@ -473,10 +531,10 @@ class BacktestingEngine(object): log = str(self.dt) + ' ' + content self.logList.append(log) - #---------------------------------------------------------------------- - def output(self, content): - """输出内容""" - print str(datetime.now()) + "\t" + content + + #------------------------------------------------ + # 结果计算相关 + #------------------------------------------------ #---------------------------------------------------------------------- def calculateBacktestingResult(self): @@ -495,6 +553,10 @@ class BacktestingEngine(object): posList = [0] # 每笔成交后的持仓情况 for trade in self.tradeDict.values(): + # 复制成交对象,因为下面的开平仓交易配对涉及到对成交数量的修改 + # 若不进行复制直接操作,则计算完后所有成交的数量会变成0 + trade = copy.copy(trade) + # 多头交易 if trade.direction == DIRECTION_LONG: # 如果尚无空头交易 @@ -727,30 +789,22 @@ class BacktestingEngine(object): plt.show() #---------------------------------------------------------------------- - def putStrategyEvent(self, name): - """发送策略更新事件,回测中忽略""" - pass - - #---------------------------------------------------------------------- - def setSlippage(self, slippage): - """设置滑点点数""" - self.slippage = slippage + def clearBacktestingResult(self): + """清空之前回测的结果""" + # 清空限价单相关 + self.limitOrderCount = 0 + self.limitOrderDict.clear() + self.workingLimitOrderDict.clear() - #---------------------------------------------------------------------- - def setSize(self, size): - """设置合约大小""" - self.size = size + # 清空停止单相关 + self.stopOrderCount = 0 + self.stopOrderDict.clear() + self.workingStopOrderDict.clear() - #---------------------------------------------------------------------- - def setRate(self, rate): - """设置佣金比例""" - self.rate = rate + # 清空成交相关 + self.tradeCount = 0 + self.tradeDict.clear() - #---------------------------------------------------------------------- - def setPriceTick(self, priceTick): - """设置价格最小变动""" - self.priceTick = priceTick - #---------------------------------------------------------------------- def runOptimization(self, strategyClass, optimizationSetting): """优化参数""" @@ -785,23 +839,6 @@ class BacktestingEngine(object): self.output(u'%s: %s' %(result[0], result[1])) return result - #---------------------------------------------------------------------- - def clearBacktestingResult(self): - """清空之前回测的结果""" - # 清空限价单相关 - self.limitOrderCount = 0 - self.limitOrderDict.clear() - self.workingLimitOrderDict.clear() - - # 清空停止单相关 - self.stopOrderCount = 0 - self.stopOrderDict.clear() - self.workingStopOrderDict.clear() - - # 清空成交相关 - self.tradeCount = 0 - self.tradeDict.clear() - #---------------------------------------------------------------------- def runParallelOptimization(self, strategyClass, optimizationSetting): """并行优化参数""" @@ -833,17 +870,17 @@ class BacktestingEngine(object): self.output(u'优化结果:') for result in resultList: self.output(u'%s: %s' %(result[0], result[1])) - + #---------------------------------------------------------------------- - def roundToPriceTick(self, price): - """取整价格到合约最小价格变动""" - if not self.priceTick: - return price - - newPrice = round(price/self.priceTick, 0) * self.priceTick - return newPrice - + def updateDailyClose(self, dt, price): + """更新每日收盘价""" + date = dt.date() + if date not in self.dailyResultDict: + self.dailyResultDict[date] = DailyResult(date, price) + else: + self.dailyResultDict[date].closePrice = price + ######################################################################## class TradingResult(object): @@ -868,6 +905,45 @@ class TradingResult(object): - self.commission - self.slippage) # 净盈亏 +######################################################################## +class DailyResult(object): + """每日交易的结果""" + + #---------------------------------------------------------------------- + def __init__(self, date, closePrice): + """Constructor""" + self.date = date # 日期 + self.closePrice = closePrice # 当日收盘价 + self.previousCloase = 0 # 昨日收盘价 + + self.tradeList = [] # 成交列表 + + self.openPosition = 0 # 开盘时的持仓 + self.closePosition = 0 # 收盘时的持仓 + + self.tradingPnl = 0 # 交易盈亏 + self.positionPnl = 0 # 持仓盈亏 + + #---------------------------------------------------------------------- + def calculatePnl(self): + """计算盈亏""" + # 持仓部分 + self.positionPnl = self.openPosition * (self.closePrice - self.previousCloase) + self.closePosition = self.openPosition + + # 交易部分 + for trade in self.tradeList: + if trade.direction == DIRECTION_LONG: + posChange = trade.volume + else: + posChange = -trade.volume + + self.tradingPnl += posChange * (self.closePrice - trade.price) + self.closePosition += posChange + + + + ######################################################################## class OptimizationSetting(object): diff --git a/vnpy/trader/vtGlobal.py b/vnpy/trader/vtGlobal.py index 0b4bc72c..f5a3761f 100644 --- a/vnpy/trader/vtGlobal.py +++ b/vnpy/trader/vtGlobal.py @@ -9,14 +9,14 @@ import traceback import json from vtFunction import getJsonPath -globalSetting = {} # 全局配置字典 - settingFileName = "VT_setting.json" settingFilePath = getJsonPath(settingFileName, __file__) +globalSetting = {} # 全局配置字典 + try: - f = file(settingFileName) + f = file(settingFilePath) globalSetting = json.load(f) except: traceback.print_exc()