增加CTA模块回测引擎中的灵活手数计算功能,允许每次委托的数量不一样,实现自动比较清算

This commit is contained in:
chenxy123 2016-07-28 21:58:28 +08:00
parent e36bd4a3f2
commit ab9d99d725

View File

@ -455,7 +455,8 @@ class BacktestingEngine(object):
self.output(u'计算回测结果') self.output(u'计算回测结果')
# 首先基于回测后的成交记录,计算每笔交易的盈亏 # 首先基于回测后的成交记录,计算每笔交易的盈亏
resultDict = OrderedDict() # 交易结果记录 resultList = [] # 交易结果列表
longTrade = [] # 未平仓的多头交易 longTrade = [] # 未平仓的多头交易
shortTrade = [] # 未平仓的空头交易 shortTrade = [] # 未平仓的空头交易
@ -467,26 +468,83 @@ class BacktestingEngine(object):
longTrade.append(trade) longTrade.append(trade)
# 当前多头交易为平空 # 当前多头交易为平空
else: else:
entryTrade = shortTrade.pop(0) while True:
entryTrade = shortTrade[0]
result = TradingResult(entryTrade.price, trade.price, -trade.volume, exitTrade = trade
self.rate, self.slippage, self.size)
resultDict[trade.dt] = result # 清算开平仓交易
closedVolume = min(exitTrade.volume, entryTrade.volume)
result = TradingResult(entryTrade.price, entryTrade.dt,
exitTrade.price, exitTrade.dt,
-closedVolume, self.rate, self.slippage, self.size)
resultList.append(result)
# 计算未清算部分
entryTrade.volume -= closedVolume
exitTrade.volume -= closedVolume
# 如果开仓交易已经全部清算,则从列表中移除
if not entryTrade.volume:
shortTrade.pop(0)
# 如果平仓交易已经全部清算,则退出循环
if not exitTrade.volume:
break
# 如果平仓交易未全部清算,
if exitTrade.volume:
# 且开仓交易已经全部清算完,则平仓交易剩余的部分
# 等于新的反向开仓交易,添加到队列中
if not shortTrade:
longTrade.append(exitTrade)
break
# 如果开仓交易还有剩余,则进入下一轮循环
else:
pass
# 空头交易 # 空头交易
else: else:
# 如果尚无多头交易 # 如果尚无多头交易
if not longTrade: if not longTrade:
shortTrade.append(trade) shortTrade.append(trade)
# 当前空头交易为平多 # 当前空头交易为平多
else: else:
entryTrade = longTrade.pop(0) while True:
entryTrade = longTrade[0]
result = TradingResult(entryTrade.price, trade.price, trade.volume, exitTrade = trade
self.rate, self.slippage, self.size)
resultDict[trade.dt] = result # 清算开平仓交易
closedVolume = min(exitTrade.volume, entryTrade.volume)
result = TradingResult(entryTrade.price, entryTrade.dt,
exitTrade.price, exitTrade.dt,
-closedVolume, self.rate, self.slippage, self.size)
resultList.append(result)
# 计算未清算部分
entryTrade.volume -= closedVolume
exitTrade.volume -= closedVolume
# 如果开仓交易已经全部清算,则从列表中移除
if not entryTrade.volume:
longTrade.pop(0)
# 如果平仓交易已经全部清算,则退出循环
if not exitTrade.volume:
break
# 如果平仓交易未全部清算,
if exitTrade.volume:
# 且开仓交易已经全部清算完,则平仓交易剩余的部分
# 等于新的反向开仓交易,添加到队列中
if not longTrade:
shortTrade.append(exitTrade)
break
# 如果开仓交易还有剩余,则进入下一轮循环
else:
pass
# 检查是否有交易 # 检查是否有交易
if not resultDict: if not resultList:
self.output(u'无交易结果') self.output(u'无交易结果')
return {} return {}
@ -505,18 +563,13 @@ class BacktestingEngine(object):
capitalList = [] # 盈亏汇总的时间序列 capitalList = [] # 盈亏汇总的时间序列
drawdownList = [] # 回撤的时间序列 drawdownList = [] # 回撤的时间序列
winningResult = 0 # 盈利次数 for result in resultList:
losingResult = 0 # 亏损次数
totalWinning = 0 # 总盈利金额
totalLosing = 0 # 总亏损金额
for time, result in resultDict.items():
capital += result.pnl capital += result.pnl
maxCapital = max(capital, maxCapital) maxCapital = max(capital, maxCapital)
drawdown = capital - maxCapital drawdown = capital - maxCapital
pnlList.append(result.pnl) pnlList.append(result.pnl)
timeList.append(time) timeList.append(result.exitDt) # 交易的时间戳使用平仓时间
capitalList.append(capital) capitalList.append(capital)
drawdownList.append(drawdown) drawdownList.append(drawdown)
@ -537,7 +590,7 @@ class BacktestingEngine(object):
averageWinning = totalWinning/winningResult # 平均每笔盈利 averageWinning = totalWinning/winningResult # 平均每笔盈利
averageLosing = totalLosing/losingResult # 平均每笔亏损 averageLosing = totalLosing/losingResult # 平均每笔亏损
profitLossRatio = -averageWinning/averageLosing # 盈亏比 profitLossRatio = -averageWinning/averageLosing # 盈亏比
# 返回回测结果 # 返回回测结果
d = {} d = {}
d['capital'] = capital d['capital'] = capital
@ -550,7 +603,7 @@ class BacktestingEngine(object):
d['timeList'] = timeList d['timeList'] = timeList
d['pnlList'] = pnlList d['pnlList'] = pnlList
d['capitalList'] = capitalList d['capitalList'] = capitalList
d['drawdownList'] = drawdownList d['drawdownList'] = drawdownList
d['winningRate'] = winningRate d['winningRate'] = winningRate
d['averageWinning'] = averageWinning d['averageWinning'] = averageWinning
d['averageLosing'] = averageLosing d['averageLosing'] = averageLosing
@ -580,7 +633,7 @@ class BacktestingEngine(object):
self.output(u'平均每笔盈利\t%s' %formatNumber(d['averageWinning'])) self.output(u'平均每笔盈利\t%s' %formatNumber(d['averageWinning']))
self.output(u'平均每笔亏损\t%s' %formatNumber(d['averageLosing'])) self.output(u'平均每笔亏损\t%s' %formatNumber(d['averageLosing']))
self.output(u'盈亏比:\t%s' %formatNumber(d['profitLossRatio'])) self.output(u'盈亏比:\t%s' %formatNumber(d['profitLossRatio']))
# 绘图 # 绘图
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
@ -674,17 +727,22 @@ class TradingResult(object):
"""每笔交易的结果""" """每笔交易的结果"""
#---------------------------------------------------------------------- #----------------------------------------------------------------------
def __init__(self, entry, exit, volume, rate, slippage, size): def __init__(self, entryPrice, entryDt, exitPrice,
exitDt, volume, rate, slippage, size):
"""Constructor""" """Constructor"""
self.entry = entry # 开仓价格 self.entryPrice = entryPrice # 开仓价格
self.exit = exit # 平仓价格 self.exitPrice = exitPrice # 平仓价格
self.entryDt = entryDt # 开仓时间datetime
self.exitDt = exitDt # 平仓时间
self.volume = volume # 交易数量(+/-代表方向) self.volume = volume # 交易数量(+/-代表方向)
self.turnover = (self.entry+self.exit)*size*abs(volume) # 成交金额 self.turnover = (self.entryPrice+self.exitPrice)*size*abs(volume) # 成交金额
self.commission = self.turnover*rate # 手续费成本 self.commission = self.turnover*rate # 手续费成本
self.slippage = slippage*2*size*abs(volume) # 滑点成本 self.slippage = slippage*2*size*abs(volume) # 滑点成本
self.pnl = ((self.exit - self.entry) * volume * size self.pnl = ((self.exitPrice - self.entryPrice) * volume * size
- self.commission - self.slippage) # 净盈亏 - self.commission - self.slippage) # 净盈亏
######################################################################## ########################################################################