| Overall Statistics |
|
Total Trades 642 Average Win 0.19% Average Loss -0.12% Compounding Annual Return -10.752% Drawdown 4.100% Expectancy -0.060 Net Profit -2.381% Sharpe Ratio -1.164 Probabilistic Sharpe Ratio 14.996% Loss Rate 63% Win Rate 37% Profit-Loss Ratio 1.56 Alpha -0.257 Beta 0.483 Annual Standard Deviation 0.088 Annual Variance 0.008 Information Ratio -4.609 Tracking Error 0.091 Treynor Ratio -0.213 Total Fees $1247.33 Estimated Strategy Capacity $6500000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
import pandas as pd
def get_supertrend(high:pd.Series, low:pd.Series, close:pd.Series, lookback:int, multiplier:int):
# ATR
tr1 = pd.DataFrame(high - low)
tr2 = pd.DataFrame(abs(high - close.shift(1)))
tr3 = pd.DataFrame(abs(low - close.shift(1)))
frames = [tr1, tr2, tr3]
tr = pd.concat(frames, axis = 1, join = 'inner').max(axis = 1)
atr = tr.ewm(lookback).mean()
# H/L AVG AND BASIC UPPER & LOWER BAND
hl_avg = (high + low) / 2
upper_band = (hl_avg + multiplier * atr).dropna()
lower_band = (hl_avg - multiplier * atr).dropna()
# FINAL UPPER BAND
final_bands = pd.DataFrame(columns = ['upper', 'lower'])
final_bands.iloc[:,0] = [x for x in upper_band - upper_band]
final_bands.iloc[:,1] = final_bands.iloc[:,0]
for i in range(len(final_bands)):
if i == 0:
final_bands.iloc[i,0] = 0
else:
if (upper_band[i] < final_bands.iloc[i-1,0]) | (close[i-1] > final_bands.iloc[i-1,0]):
final_bands.iloc[i,0] = upper_band[i]
else:
final_bands.iloc[i,0] = final_bands.iloc[i-1,0]
# FINAL LOWER BAND
for i in range(len(final_bands)):
if i == 0:
final_bands.iloc[i, 1] = 0
else:
if (lower_band[i] > final_bands.iloc[i-1,1]) | (close[i-1] < final_bands.iloc[i-1,1]):
final_bands.iloc[i,1] = lower_band[i]
else:
final_bands.iloc[i,1] = final_bands.iloc[i-1,1]
# SUPERTREND
supertrend = pd.DataFrame(columns = [f'supertrend_{lookback}'])
supertrend.iloc[:,0] = [x for x in final_bands['upper'] - final_bands['upper']]
for i in range(len(supertrend)):
if i == 0:
supertrend.iloc[i, 0] = 0
elif supertrend.iloc[i-1, 0] == final_bands.iloc[i-1, 0] and close[i] < final_bands.iloc[i, 0]:
supertrend.iloc[i, 0] = final_bands.iloc[i, 0]
elif supertrend.iloc[i-1, 0] == final_bands.iloc[i-1, 0] and close[i] > final_bands.iloc[i, 0]:
supertrend.iloc[i, 0] = final_bands.iloc[i, 1]
elif supertrend.iloc[i-1, 0] == final_bands.iloc[i-1, 1] and close[i] > final_bands.iloc[i, 1]:
supertrend.iloc[i, 0] = final_bands.iloc[i, 1]
elif supertrend.iloc[i-1, 0] == final_bands.iloc[i-1, 1] and close[i] < final_bands.iloc[i, 1]:
supertrend.iloc[i, 0] = final_bands.iloc[i, 0]
supertrend = supertrend.set_index(upper_band.index)
supertrend = supertrend.dropna()[1:]
# ST UPTREND/DOWNTREND
upt = []
dt = []
close = close.iloc[len(close) - len(supertrend):]
for i in range(len(supertrend)):
if close[i] > supertrend.iloc[i, 0]:
upt.append(supertrend.iloc[i, 0])
dt.append(np.nan)
elif close[i] < supertrend.iloc[i, 0]:
upt.append(np.nan)
dt.append(supertrend.iloc[i, 0])
else:
upt.append(np.nan)
dt.append(np.nan)
st, upt, dt = pd.Series(supertrend.iloc[:, 0]), pd.Series(upt), pd.Series(dt)
upt.index, dt.index = supertrend.index, supertrend.index
return st, upt, dt
# from AlgorithmImports import *
from collections import deque
from datetime import datetime
import math
class BasicNerdEquityAlgorithm(QCAlgorithm):
def Initialize(self):
superTrendPeriod = 10
superTrendMultiple = 3
self.SetCash(100000)
self.SetStartDate(2018, 4, 1)
self.sym = self.AddEquity("SPY", Resolution.Minute).Symbol
# define custom indicator
self.supertrend = MySuperTrend(self, superTrendPeriod, superTrendMultiple)
self.RegisterIndicator(self.sym, self.supertrend, Resolution.Minute)
self.SetWarmUp(superTrendPeriod)
def OnData(self, data):
if not self.supertrend.IsReady: return
self.Plot('Custom', 'spy', data[self.sym].Close)
self.Plot('Custom', 'Supertrend', self.supertrend.Value)
if self.supertrend.Value < data[self.sym].Close:
self.SetHoldings(self.sym, 1)
else:
self.SetHoldings(self.sym, 0)
class MySuperTrend:
def __init__(self, algorithm, period, multiple, movingAverageType=MovingAverageType.Simple):
self.Name = "Custom Indicator"
self.Time = datetime.min
self.Value = 0
self.multiplier = multiple
self.atr = AverageTrueRange(period, movingAverageType)
self.values = deque(maxlen=period)
self.previousTrailingLowerBand = 0
self.previousTrailingUpperBand = 0
self.previousClose = 0
self.previousTrend = 0
def __repr__(self):
return "{0} -> IsReady: {1}. Time: {2}. Value: {3}".format(self.Name, self.IsReady, self.Time, self.Value)
def Update(self, input:TradeBar):
self.Time = input.EndTime
self.atr.Update(input)
superTrend = 0
currentClose = input.Close
currentBasicLowerBand = (input.Low + input.High) / 2 - self.multiplier * self.atr.Current.Value
currentBasicUpperBand = (input.Low + input.High) / 2 + self.multiplier * self.atr.Current.Value
if self.previousClose > self.previousTrailingLowerBand:
currentTrailingLowerBand = max(currentBasicLowerBand, self.previousTrailingLowerBand)
else:
currentTrailingLowerBand = currentBasicLowerBand
if self.previousClose < self.previousTrailingUpperBand:
currentTrailingUpperBand = min(currentBasicUpperBand, self.previousTrailingUpperBand)
else:
currentTrailingUpperBand = currentBasicUpperBand
if currentClose > currentTrailingUpperBand:
currentTrend = 1
elif currentClose < currentTrailingLowerBand:
currentTrend = -1
else:
currentTrend = self.previousTrend
if currentTrend == 1:
superTrend = currentTrailingLowerBand
elif currentTrend == -1:
superTrend = currentTrailingUpperBand
self.previousTrailingLowerBand = currentTrailingLowerBand
self.previousTrailingUpperBand = currentTrailingUpperBand
self.previousClose = currentClose
self.previousTrend = currentTrend
if not self.atr.IsReady:
return 0
self.Value = superTrend
return self.IsReady
@property
def IsReady(self):
return self.atr.IsReady and self.Value != 0