| Overall Statistics |
|
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return -6.690% Drawdown 5.900% Expectancy 0 Net Profit -1.101% Sharpe Ratio -0.267 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.041 Beta -0.235 Annual Standard Deviation 0.158 Annual Variance 0.025 Information Ratio -0.192 Tracking Error 0.248 Treynor Ratio 0.18 Total Fees $1.67 |
from collections import deque
from datetime import datetime, timedelta
class ZigZagDev(QCAlgorithm):
def Initialize(self):
self.myResolution1 = Resolution.Hour
self.myResolution2 = Resolution.Daily
self.zzPriod1 = 200
self.zzPriod2 = 200
self.ticker ="SPY"
self.SetStartDate(2019,8,1)
#self.SetEndDate(2019,8,31)
self.SetEndDate(datetime.now())
self.SetWarmUp(TimeSpan.FromDays(self.zzPriod2))
self.symbol = self.AddEquity(self.ticker, self.myResolution1).Symbol
self.atr1 = AverageTrueRange(15)
self.RegisterIndicator(self.symbol, self.atr1, self.myResolution1)
self.ZZ1 = ZigZag(self, 'ZZ1', self.zzPriod1, 5, 1, 0.05, self.atr1)
self.RegisterIndicator(self.symbol, self.ZZ1, self.myResolution1)
self.atr2 = AverageTrueRange(15)
self.RegisterIndicator(self.symbol, self.atr2, self.myResolution2)
self.ZZ2 = ZigZag(self, 'ZZ2', self.zzPriod2, 5, 2, 5, self.atr2)
self.RegisterIndicator(self.symbol, self.ZZ2, self.myResolution2)
self.rsi1 = RelativeStrengthIndex(35)
self.RegisterIndicator(self.ticker, self.rsi1, self.myResolution1)
self.rsi1_zz = ZigZag(self, 'rsi1_zz', self.zzPriod1, 5, 1, 0.1, self.atr1)
self.rsi1.Updated += self.rsi1_zz.IndicatorUpdate
def OnData(self, data):
if self.IsWarmingUp: return
if not self.Portfolio.Invested:
self.SetHoldings(self.ticker, 1)
def OnEndOfAlgorithm(self):
if False: self.ZZ1.ListZZ()
if True: self.ZZ1.listZZPoints()
if False: self.ZZ2.ListZZ()
if False: self.ZZ2.listZZPoints()
if False: self.rsi1_zz.ListZZ()
if False: self.rsi1_zz.listZZPoints()
def MyDebug(self, debugString):
message = str(self.Time) + " " + str(self.symbol) + ": "+ debugString
self.Debug(message)
class ZigZag():
def __init__(self, algo, name, period, lookback=10, thresholdType=1, threshold=0.05, atr=None):
self.algo = algo
self.name = name
self.period = period
self.lookback = lookback
self.thresholdType = thresholdType
self.threshold = threshold
self.currentThreshold = 0
self.atr = None
if atr != None: self.atr = atr
self.Time = datetime.min
self.Value = 0
self.IsReady = False
self.lastLow = None
self.lastHigh = None
self.shortTrendCount = 0
self.longTrendCount = 0
self.trendDir = None
self.trendChange = False
self.bars = deque(maxlen=period)
self.zzLow = deque(maxlen=period)
self.shortTrendCount = deque(maxlen=period)
self.zzHigh = deque(maxlen=period)
self.longTrendCount = deque(maxlen=period)
self.zigzag = deque(maxlen=period)
self.barCount = 0
self.count = 0
self.zzPoints = []
def __str__(self):
return self.name
def __repr__(self):
return "Zig Zag Name:{}, IsReady:{}, Time:{}, Value:{}".format(self.Name, self.IsReady, self.Time, self.Value)
# Update method is mandatory
def IndicatorUpdate(self, caller, updated):
bar = TradeBar()
value = updated.Value
bar.Open = value
bar.High = value
bar.Low = value
bar.Close = value
bar.EndTime = updated.EndTime
self.Update(bar)
# Update method is mandatory
def Update(self, input):
bar = input
self.count +=1
self.bars.appendleft(bar)
self.barCount = len(self.bars)
self.Time = bar.EndTime
self.Value = self.count
self.IsReady = self.barCount == self.bars.maxlen
#update currentThreshold value
if self.thresholdType == 1:
self.currentThreshold = self.bars[0].Close * self.threshold
elif self.thresholdType == 2:
self.currentThreshold = self.atr.Current.Value * self.threshold
if self.barCount < self.lookback:
#Not enough bars yet to calculte
self.zzLow.appendleft(0)
self.shortTrendCount.appendleft(0)
self.zzHigh.appendleft(0)
self.longTrendCount.appendleft(0)
self.zigzag.appendleft(0)
return self.IsReady
low, thisisLow , retraceLow = self.localLow()
high, thisisHigh, retraceHigh = self.localHigh()
self.lastLow = low
self.lastHigh = high
#previousTrendDir = self.trendDir
self.trendChange = False
if thisisLow or retraceLow:
if self.trendDir == 1:
self.trendChange = True
self.trendDir = -1
if thisisHigh or retraceHigh:
if self.trendDir == -1:
self.trendChange = True
self.trendDir = 1
#Update ZZ Low
if thisisLow or retraceLow:
self.zzLow.appendleft(low)
else: self.zzLow.appendleft(0)
#Update Short TrendCount
shortTrendCount = self.shortTrendCount[0]
if self.trendChange and self.trendDir == -1:
shortTrendCount-=1
self.shortTrendCount.appendleft(shortTrendCount)
#Update ZZ High
if thisisHigh or retraceHigh:
self.zzHigh.appendleft(high)
else: self.zzHigh.appendleft(0)
#Update Long TrendCount
longTrendCount = self.longTrendCount[0]
if self.trendChange and self.trendDir == 1:
longTrendCount+=1
self.longTrendCount.appendleft(longTrendCount)
#Update ZZ
if self.zzLow[0]!=0:
self.zigzag.appendleft(self.zzLow[0])
else:
self.zigzag.appendleft(self.zzHigh[0])
self.Value = self.zigzag[0]
#Erase precious values if any in this Short Trend
i=1
while i < len(self.bars)-1 and self.zzLow[0] !=0 and self.shortTrendCount[i] == shortTrendCount:
if self.zzLow[i] == self.zigzag[i]:
self.zigzag[i] = 0
self.zzLow[i] = 0
i+=1
#Erase precious values if any in this Long Trend
i=1
while i < len(self.bars)-1 and self.zzHigh[0] != 0 and self.longTrendCount[i] == longTrendCount:
if self.zzHigh[i] == self.zigzag[i]:
self.zigzag[i] = 0
self.zzHigh[i] = 0
i+=1
if False and self.IsReady :self.algo.MyDebug("Low:{}/{}/{}/{}/{}, High:{}/{}/{}/{}/{}, ZZ:{}/{}".format( \
str(low),str(thisisLow),str(retraceLow),str(self.zzLow[0]),str(self.shortTrendCount[0]), \
str(high),str(thisisHigh),str(retraceHigh),str(self.zzHigh[0]),str(self.longTrendCount[0]), \
str(self.zigzag[0]),str(self.trendDir)))
#Update zzPoints List
del self.zzPoints[:]
for i in range(0, len(self.zigzag)-1):
if self.zigzag[i]!=0:
self.zzPoints.append(ZZPoint(self,i))
return self.IsReady
#Local Low or Retracement
def localLow(self):
thisisLow = False
retrace = False
low = self.bars[0].Low
for i in range(1, self.lookback):
if self.bars[i].Low < low: low = self.bars[i].Low
thisisLow = self.bars[0].Low == low
#trendDir is not updated yet so it is the previous value
if self.lastHigh != None and self.trendDir == 1 and (self.lastHigh - self.bars[0].High)>self.currentThreshold:
retrace = True
low = self.bars[0].Low
return low, thisisLow, retrace
#Local High or Retracement
def localHigh(self):
thisisHigh = False
retrace = False
high = self.bars[0].High
for i in range(1, self.lookback):
if self.bars[i].High > high: high = self.bars[i].High
thisisHigh = self.bars[0].High == high
#trendDir is not updated yet so it is the previous value
if self.lastLow != None and self.trendDir == -1 and (self.bars[0].Low - self.lastLow)>self.currentThreshold:
retrace = True
high = self.bars[0].High
return high, thisisHigh, retrace
def ListZZ(self):
printItems = min(70, len(self.bars)-1)
i=printItems
while i >= 0:
if self.zzLow[i]==self.zigzag[i] and self.zigzag[i]!=0:
trendCount = self.shortTrendCount[i]
elif self.zzHigh[i]==self.zigzag[i] and self.zigzag[i]!=0:
trendCount = self.longTrendCount[i]
else:
trendCount = ""
self.algo.MyDebug("Date:{}, Close:{}, zzLow:{}/{}, zzHigh:{}/{}, ZZ:{}/{}".format( \
str(self.bars[i].EndTime), str(self.bars[i].Close), \
str(self.zzLow[i]),str(self.shortTrendCount[i]), \
str(self.zzHigh[i]),str(self.longTrendCount[i]), \
str(self.zigzag[i]),str(trendCount)))
i-=1
return
def listZZPoints(self):
i=1
for level in self.zzPoints:
self.algo.MyDebug("{}/{}. Value:{}, EndTime:{}, Index:{}, TrendCount:{}".format( \
str(level.zz), str(i), \
str(level.value), str(level.endTime), str(level.index), str(level.trendCount),))
i+=1
return
class ZZPoint():
def __init__(self, zz, index):
self.zz = zz
self.index = index
self.endTime = zz.bars[index].EndTime
self.value = zz.zigzag[index]
if self.value == zz.zzLow[index]:
self.trendCount = zz.shortTrendCount[index]
else:
self.trendCount = zz.longTrendCount[index]
return