增加CTA模块回测引擎中的灵活手数计算功能,允许每次委托的数量不一样,实现自动比较清算
This commit is contained in:
parent
e36bd4a3f2
commit
ab9d99d725
@ -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) # 净盈亏
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
|
Loading…
Reference in New Issue
Block a user