海龟策略7大要素分别是品种选择、头寸规模、单位头寸限制、入场信号、逐步建仓、止损、止盈。由于品种选择无法通过不属于交易策略内容,故只对后面5大要素的进行代码解析。
1.头寸规模
头寸规模=(1%账户资金)/(ATR 合约规模),其代码如下
class TurtlePortfolio(object):
......
#----------------------------------------------------------------------
def newSignal(self, signal, direction, offset, price, volume):
......
riskValue = self.portfolioValue * 0.01
multiplier = riskValue / (signal.atrVolatility * size)
multiplier = int(round(multiplier, 0)) # multiplier即为头寸规模
self.multiplierDict[signal.vtSymbol] = multiplier
......
#----------------------------------------------------------------------
def sendOrder(self, vtSymbol, direction, offset, price, volume, multiplier):
""""""
# 计算合约持仓
if direction == DIRECTION_LONG:
self.unitDict[vtSymbol] += volume # volume即为单位头寸
self.posDict[vtSymbol] += volume * multiplier
else:
self.unitDict[vtSymbol] -= volume
self.posDict[vtSymbol] -= volume * multiplier # 实际持仓=单位头寸 * 头寸规模
2.单位头寸限制
原版海龟策略规定了4个维度的单位头寸限制,分别是
- 单个市场:头寸上限是4个
- 高度关联的多个市场:单个方向头寸单位不超过6个
- 松散关联的多个市场:某一个方向上的头寸单位不超过10个
- 单个方向:最多12个
基于高度关联市场和松散关联市场判断起来都非常主观,并无统一标准。在客观上的层面只能实现单个市场和单个方向的头寸限制。
在开仓交易前,需要检查上一笔交易是否盈利,若是盈利则直接返回,不进行买卖操作(仅仅适用于短周期版本的入场策略,即profitCheck=True;长周期版本对应的是profitCheck=False)
MAX_PRODUCT_POS = 4 # 单品种最大持仓
MAX_DIRECTION_POS = 12 # 单方向最大持仓
......
class TurtlePortfolio(object):
......
#----------------------------------------------------------------------
def newSignal(self, signal, direction, offset, price, volume):
......
# 开仓
if offset == OFFSET_OPEN:
# 检查上一次是否为盈利
if signal.profitCheck:
pnl = signal.getLastPnl()
if pnl > 0:
return
# 买入
if direction == DIRECTION_LONG:
# 组合持仓不能超过上限
if self.totalLong >= MAX_DIRECTION_POS:
return
# 单品种持仓不能超过上限
if self.unitDict[signal.vtSymbol] >= MAX_PRODUCT_POS:
return
# 卖出
else:
if self.totalShort <= -MAX_DIRECTION_POS:
return
if self.unitDict[signal.vtSymbol] <= -MAX_PRODUCT_POS:
return
3.入场信号、逐步建仓、止损、止盈
原版海龟策略提供2个版本的入场和止盈信号,分别是长周期版本和短周期版本的唐奇安通道突破。
- 短周期信号:入场用是20日唐奇安通道,止盈用是10日唐奇安通道,用20日周期计算ATR值,有上一笔盈利当前信号无效的过滤条件。
- 短周期信号:入场用是55日唐奇安通道,止盈用是20日唐奇安通道,用20日周期计算ATR值,无上一笔盈利当前信号无效的过滤条件。
逐步建仓的规则是每隔0.5*ATR幅度慢慢加满至4个单位头寸,止损也相应根据0.5*ATR的步进移动。
class TurtleSignal(object):
#----------------------------------------------------------------------
def __init__(self, portfolio, vtSymbol,
entryWindow, exitWindow, atrWindow,
profitCheck=False):
......
#----------------------------------------------------------------------
def onBar(self, bar):
self.bar = bar
self.am.bar
if not self.am.inited:
return
self.generateSignal(bar)
self.calculateIndicator()
#----------------------------------------------------------------------
def generateSignal(self, bar):
"""
判断交易信号
"""
# 如果指标尚未初始化,则忽略
if not self.longEntry1:
return
# 优先检查平仓
if self.unit > 0:
longExit = max(self.longStop, self.exitDown)
if bar.low <= longExit:
self.sell(longExit)
return
elif self.unit < 0:
shortExit = min(self.shortStop, self.exitUp)
if bar.high >= shortExit:
self.cover(shortExit)
return
# 没有仓位或者持有多头仓位的时候,可以做多(加仓)
if self.unit >= 0:
trade = False
if bar.high >= self.longEntry1 and self.unit < 1:
self.buy(self.longEntry1, 1)
trade = True
if bar.high >= self.longEntry2 and self.unit < 2:
self.buy(self.longEntry2, 1)
trade = True
if bar.high >= self.longEntry3 and self.unit < 3:
self.buy(self.longEntry3, 1)
trade = True
if bar.high >= self.longEntry4 and self.unit < 4:
self.buy(self.longEntry4, 1)
trade = True
if trade:
return
# 没有仓位或者持有空头仓位的时候,可以做空(加仓)
if self.unit <= 0:
if bar.low <= self.shortEntry1 and self.unit > -1:
self.short(self.shortEntry1, 1)
if bar.low <= self.shortEntry2 and self.unit > -2:
self.short(self.shortEntry2, 1)
if bar.low <= self.shortEntry3 and self.unit > -3:
self.short(self.shortEntry3, 1)
if bar.low <= self.shortEntry4 and self.unit > -4:
self.short(self.shortEntry4, 1)
#----------------------------------------------------------------------
def calculateIndicator(self):
"""计算技术指标"""
self.entryUp, self.entryDown = self.am.donchian(self.entryWindow)
self.exitUp, self.exitDown = self.am.donchian(self.exitWindow)
# 有持仓后,ATR波动率和入场位等都不再变化
if not self.unit:
self.atrVolatility = self.am.atr(self.atrWindow)
self.longEntry1 = self.entryUp
self.longEntry2 = self.entryUp + self.atrVolatility * 0.5
self.longEntry3 = self.entryUp + self.atrVolatility * 1
self.longEntry4 = self.entryUp + self.atrVolatility * 1.5
self.longStop = 0
self.shortEntry1 = self.entryDown
self.shortEntry2 = self.entryDown - self.atrVolatility * 0.5
self.shortEntry3 = self.entryDown - self.atrVolatility * 1
self.shortEntry4 = self.entryDown - self.atrVolatility * 1.5
self.shortStop = 0
......
class TurtlePortfolio(object):
......
#----------------------------------------------------------------------
def init(self, portfolioValue, vtSymbolList, sizeDict):
""""""
self.portfolioValue = portfolioValue
self.sizeDict = sizeDict
for vtSymbol in vtSymbolList:
signal1 = TurtleSignal(self, vtSymbol, 20, 10, 20, True)
signal2 = TurtleSignal(self, vtSymbol, 55, 20, 20, False)
l = self.signalDict[vtSymbol]
l.append(signal1)
l.append(signal2)
self.unitDict[vtSymbol] = 0
self.posDict[vtSymbol] = 0
#----------------------------------------------------------------------
def onBar(self, bar):
""""""
for signal in self.signalDict[bar.vtSymbol]:
signal.onBar(bar)