修改回测模型,支持多次逐步加仓

This commit is contained in:
msincenselee 2016-10-16 17:25:34 +08:00
parent f79a63d19d
commit 5a3ad183a0

View File

@ -22,6 +22,7 @@ from vtConstant import *
from vtGateway import VtOrderData, VtTradeData from vtGateway import VtOrderData, VtTradeData
from vtFunction import loadMongoSetting from vtFunction import loadMongoSetting
import logging import logging
import copy
######################################################################## ########################################################################
class BacktestingEngine(object): class BacktestingEngine(object):
@ -86,6 +87,8 @@ class BacktestingEngine(object):
self.bar = None self.bar = None
self.dt = None # 最新的时间 self.dt = None # 最新的时间
self.gatewayName = u'BackTest' self.gatewayName = u'BackTest'
self.usageCompounding = False # 是否使用简单复利
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def setStartDate(self, startDate='20100416', initDays=10): def setStartDate(self, startDate='20100416', initDays=10):
@ -903,6 +906,12 @@ class BacktestingEngine(object):
def calculateBacktestingResult(self): def calculateBacktestingResult(self):
""" """
计算回测结果 计算回测结果
Modified by Incense Lee
增加了支持逐步加仓的计算
例如前面共有6次开仓1手开仓+5次加仓每次1手平仓只有1次六手那么交易次数是6次开仓+平仓
暂不支持每次加仓数目不一致的核对因为比较复杂
增加组合的支持
""" """
self.output(u'计算回测结果') self.output(u'计算回测结果')
@ -911,7 +920,9 @@ class BacktestingEngine(object):
longTrade = [] # 未平仓的多头交易 longTrade = [] # 未平仓的多头交易
shortTrade = [] # 未平仓的空头交易 shortTrade = [] # 未平仓的空头交易
i = 0 i = 1
tradeUnit = 1
longid = EMPTY_STRING longid = EMPTY_STRING
shortid = EMPTY_STRING shortid = EMPTY_STRING
@ -920,9 +931,6 @@ class BacktestingEngine(object):
trade = self.tradeDict[tradeid] trade = self.tradeDict[tradeid]
if tradeid == '127':
pass
# 多头交易 # 多头交易
if trade.direction == DIRECTION_LONG: if trade.direction == DIRECTION_LONG:
# 如果尚无空头交易 # 如果尚无空头交易
@ -931,18 +939,46 @@ class BacktestingEngine(object):
longid = tradeid longid = tradeid
# 当前多头交易为平空 # 当前多头交易为平空
else: else:
entryTrade = shortTrade.pop(0) gId = i # 交易组(多个平仓数为一组)
gt = 1 # 组合的交易次数
gr = None # 组合的交易结果
result = TradingResult(entryTrade.price, trade.price, -trade.volume, if trade.volume >tradeUnit:
self.rate, self.slippage, self.size) self.writeCtaLog(u'平仓数{0},组合编号:{1}'.format(trade.volume,gId))
gt = int(trade.volume/tradeUnit)
resultDict[trade.dt] = result for tv in range(gt):
i = i+1 entryTrade = shortTrade.pop(0)
self.writeCtaLog(u'{6}.{7}.开空{0},short:{1},{8}.平空{2},cover:{3},vol:{4},净盈亏:{5}' result = TradingResult(entryTrade.price, trade.price, -tradeUnit,
.format(entryTrade.tradeTime, entryTrade.price, self.rate, self.slippage, self.size, groupId=gId)
trade.tradeTime,trade.price, trade.volume,result.pnl,
i,shortid,tradeid)) if tv == 0:
if gt==1:
resultDict[entryTrade.dt] = result
else:
gr = copy.deepcopy(result)
else:
gr.turnover = gr.turnover + result.turnover
gr.commission = gr.commission + result.commission
gr.slippage = gr.slippage + result.slippage
gr.pnl = gr.pnl + result.pnl
if tv == gt -1:
gr.volume = trade.volume
resultDict[entryTrade.dt] = gr
self.writeCtaLog(u'{9}@{6} [{7}:开空{0},short:{1}]-[{8}:平空{2},cover:{3},vol:{4}],净盈亏:{5}'
.format(entryTrade.tradeTime, entryTrade.price,
trade.tradeTime, trade.price, tradeUnit, result.pnl,
i, shortid, tradeid,gId))
i = i+1
if type(gr) != type(None):
self.writeCtaLog(u'组合净盈亏:{0}'.format(gr.pnl))
self.writeCtaLog(u'-------------')
# 空头交易 # 空头交易
else: else:
@ -952,17 +988,47 @@ class BacktestingEngine(object):
shortid = tradeid shortid = tradeid
# 当前空头交易为平多 # 当前空头交易为平多
else: else:
entryTrade = longTrade.pop(0) gId = i # 交易组(多个平仓数为一组)
gt = 1 # 组合的交易次数
gr = None # 组合的交易结果
result = TradingResult(entryTrade.price, trade.price, trade.volume, if trade.volume >tradeUnit:
self.rate, self.slippage, self.size) self.writeCtaLog(u'平仓数{0},组合编号:{1}'.format(trade.volume,gId))
resultDict[trade.dt] = result gt = int(trade.volume/tradeUnit)
i = i+1 for tv in range(gt):
self.writeCtaLog(u'{6}.{7}开多{0},buy:{1},{8}.平多{2},sell:{3},vol:{4},净盈亏:{5}'
.format(entryTrade.tradeTime, entryTrade.price, entryTrade = longTrade.pop(0)
trade.tradeTime,trade.price, trade.volume,result.pnl,
i,longid,tradeid)) result = TradingResult(entryTrade.price, trade.price, tradeUnit,
self.rate, self.slippage, self.size, groupId= gId)
if tv == 0:
if gt==1:
resultDict[entryTrade.dt] = result
else:
gr = copy.deepcopy(result)
else:
gr.turnover = gr.turnover + result.turnover
gr.commission = gr.commission + result.commission
gr.slippage = gr.slippage + result.slippage
gr.pnl = gr.pnl + result.pnl
if tv == gt -1:
gr.volume = trade.volume
resultDict[entryTrade.dt] = gr
self.writeCtaLog(u'{9}@{6} [{7}:开多{0},buy:{1}]-[{8}.平多{2},sell:{3},vol:{4}],净盈亏:{5}'
.format(entryTrade.tradeTime, entryTrade.price,
trade.tradeTime,trade.price, tradeUnit, result.pnl,
i, longid, tradeid, gId))
i = i+1
if type(gr) != type(None):
self.writeCtaLog(u'组合净盈亏:{0}'.format(gr.pnl))
self.writeCtaLog(u'-------------')
# 检查是否有交易 # 检查是否有交易
if not resultDict: if not resultDict:
@ -970,9 +1036,15 @@ class BacktestingEngine(object):
return {} return {}
# 然后基于每笔交易的结果,我们可以计算具体的盈亏曲线和最大回撤等 # 然后基于每笔交易的结果,我们可以计算具体的盈亏曲线和最大回撤等
capital = 0 # 资金 initCapital = 40000 # 期初资金
maxCapital = 0 # 资金最高净值 capital = initCapital # 资金
maxCapital = initCapital # 资金最高净值
drawdown = 0 # 回撤 drawdown = 0 # 回撤
maxPnl = 0 # 最高盈利
minPnl = 0 # 最大亏损
maxVolume = 1 # 最大仓位数
compounding = 1 # 简单的复利基数如果资金是期初资金的x倍就扩大开仓比例,例如3w开1手6w开2手12w开4手)
wins = 0
totalResult = 0 # 总成交数量 totalResult = 0 # 总成交数量
totalTurnover = 0 # 总成交金额(合约面值) totalTurnover = 0 # 总成交金额(合约面值)
@ -983,27 +1055,42 @@ class BacktestingEngine(object):
pnlList = [] # 每笔盈亏序列 pnlList = [] # 每笔盈亏序列
capitalList = [] # 盈亏汇总的时间序列 capitalList = [] # 盈亏汇总的时间序列
drawdownList = [] # 回撤的时间序列 drawdownList = [] # 回撤的时间序列
drawdownRateList = [] # 最大回撤比例的时间序列
for time, result in resultDict.items(): for time, result in resultDict.items():
capital += result.pnl
# 是否使用简单复利
if self.usageCompounding:
compounding = int(capital/initCapital)
if result.pnl > 0:
wins += 1
capital += result.pnl*compounding
maxCapital = max(capital, maxCapital) maxCapital = max(capital, maxCapital)
maxVolume = max(maxVolume, result.volume*compounding)
drawdown = capital - maxCapital drawdown = capital - maxCapital
drawdownRate = round(float(drawdown*100/maxCapital),4)
pnlList.append(result.pnl) pnlList.append(result.pnl*compounding)
timeList.append(time) timeList.append(time)
capitalList.append(capital) capitalList.append(capital)
drawdownList.append(drawdown) drawdownList.append(drawdown)
drawdownRateList.append(drawdownRate)
totalResult += 1 totalResult += 1
totalTurnover += result.turnover totalTurnover += result.turnover*compounding
totalCommission += result.commission totalCommission += result.commission*compounding
totalSlippage += result.slippage totalSlippage += result.slippage*compounding
# 返回回测结果 # 返回回测结果
d = {} d = {}
d['capital'] = capital d['initCapital'] = initCapital
d['capital'] = capital - initCapital
d['maxCapital'] = maxCapital d['maxCapital'] = maxCapital
d['drawdown'] = drawdown d['drawdown'] = drawdown
d['maxPnl'] = max(pnlList)
d['minPnl'] = min(pnlList)
d['maxVolume'] = maxVolume
d['totalResult'] = totalResult d['totalResult'] = totalResult
d['totalTurnover'] = totalTurnover d['totalTurnover'] = totalTurnover
d['totalCommission'] = totalCommission d['totalCommission'] = totalCommission
@ -1012,6 +1099,8 @@ class BacktestingEngine(object):
d['pnlList'] = pnlList d['pnlList'] = pnlList
d['capitalList'] = capitalList d['capitalList'] = capitalList
d['drawdownList'] = drawdownList d['drawdownList'] = drawdownList
d['drawdownRateList'] = drawdownRateList
d['winRate'] = round(100*wins/len(pnlList),4)
return d return d
#---------------------------------------------------------------------- #----------------------------------------------------------------------
@ -1028,8 +1117,17 @@ class BacktestingEngine(object):
self.output(u'最后一笔交易:\t%s' % d['timeList'][-1]) self.output(u'最后一笔交易:\t%s' % d['timeList'][-1])
self.output(u'总交易次数:\t%s' % formatNumber(d['totalResult'])) self.output(u'总交易次数:\t%s' % formatNumber(d['totalResult']))
self.output(u'期初资金:\t%s' % formatNumber(d['initCapital']))
self.output(u'总盈亏:\t%s' % formatNumber(d['capital'])) self.output(u'总盈亏:\t%s' % formatNumber(d['capital']))
self.output(u'最大回撤: \t%s' % formatNumber(min(d['drawdownList']))) self.output(u'资金最高净值:\t%s' % formatNumber(d['maxCapital']))
self.output(u'每笔最大盈利:\t%s' % formatNumber(d['maxPnl']))
self.output(u'每笔最大亏损:\t%s' % formatNumber(d['minPnl']))
self.output(u'净值最大回撤: \t%s' % formatNumber(min(d['drawdownList'])))
self.output(u'净值最大回撤率: \t%s' % formatNumber(min(d['drawdownRateList'])))
self.output(u'胜率:\t%s' % formatNumber(d['winRate']))
self.output(u'最大持仓:\t%s' % formatNumber(d['maxVolume']))
self.output(u'平均每笔盈利:\t%s' %formatNumber(d['capital']/d['totalResult'])) self.output(u'平均每笔盈利:\t%s' %formatNumber(d['capital']/d['totalResult']))
self.output(u'平均每笔滑点成本:\t%s' %formatNumber(d['totalSlippage']/d['totalResult'])) self.output(u'平均每笔滑点成本:\t%s' %formatNumber(d['totalSlippage']/d['totalResult']))
@ -1128,11 +1226,12 @@ class TradingResult(object):
"""每笔交易的结果""" """每笔交易的结果"""
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def __init__(self, entry, exit, volume, rate, slippage, size): def __init__(self, entry, exit, volume, rate, slippage, size, groupId):
"""Constructor""" """Constructor"""
self.entry = entry # 开仓价格 self.entry = entry # 开仓价格
self.exit = exit # 平仓价格 self.exit = exit # 平仓价格
self.volume = volume # 交易数量(+/-代表方向) self.volume = volume # 交易数量(+/-代表方向)
self.groupId = groupId # 主交易ID针对多手平仓
self.turnover = (self.entry+self.exit)*size # 成交金额 self.turnover = (self.entry+self.exit)*size # 成交金额
self.commission =round(float(self.turnover*rate),4) # 手续费成本 self.commission =round(float(self.turnover*rate),4) # 手续费成本