| Overall Statistics |
|
Total Trades 117 Average Win 4.59% Average Loss -1.14% Compounding Annual Return 4.606% Drawdown 25.200% Expectancy 1.082 Net Profit 84.475% Sharpe Ratio 0.457 Probabilistic Sharpe Ratio 2.262% Loss Rate 59% Win Rate 41% Profit-Loss Ratio 4.03 Alpha 0.018 Beta 0.325 Annual Standard Deviation 0.117 Annual Variance 0.014 Information Ratio -0.328 Tracking Error 0.168 Treynor Ratio 0.164 Total Fees $6779.78 |
def UpdateBenchmarkValue(self):
''' Simulate buy and hold the Benchmark '''
if self.initBenchmarkPrice == 0:
self.initBenchmarkCash = self.Portfolio.Cash
self.initBenchmarkPrice = self.Benchmark.Evaluate(self.Time)
self.benchmarkValue = self.initBenchmarkCash
else:
currentBenchmarkPrice = self.Benchmark.Evaluate(self.Time)
self.benchmarkValue = (currentBenchmarkPrice / self.initBenchmarkPrice) * self.initBenchmarkCash
def UpdatePlots(self, currentEquitySMA, equityClosePrice):
# plot close price, SMA and portfolio exposures
self.Plot('Signals', 'Equity SMA', float(currentEquitySMA))
self.Plot('Signals', 'Equity Close Price', float(equityClosePrice))
portfolioValue = self.Portfolio.TotalPortfolioValue
equityExposure = (self.Portfolio[self.equitySymbol].HoldingsValue / portfolioValue) * 100
safeExposure = (self.Portfolio[self.safeSymbol].HoldingsValue / portfolioValue) * 100
self.Plot('Portfolio Exposures', 'Equity Exposure', equityExposure)
self.Plot('Portfolio Exposures', 'Safe Exposure', safeExposure)
if not self.newSignal:
return
# plot signals
if self.signal == 1:
self.Plot('Signals', 'Equity Position', float(equityClosePrice))
elif self.signal == 2:
self.Plot('Signals', 'Mix Position', float(equityClosePrice))
elif self.signal == 3:
self.Plot('Signals', 'Safe Position', float(equityClosePrice))
def CustomSecurityInitializer(self, security):
'''
Description:
Initialize the security with different models
Args:
security: Security which characteristics we want to change'''
security.SetLeverage(self.leverage)### 2020_07_17 v3
### ----------------------------------------------------------------------------
#
### ----------------------------------------------------------------------------
from HelperFunctions import *
from System.Drawing import Color
class TrendFollowingSystemTemplateAlgorithm(QCAlgorithm):
''' Implementation of the Pacer Trendpilot Strategy '''
def Initialize(self):
''' Initialization at beginning of backtest '''
### USER-DEFINED INPUTS ---------------------------------------------------------------------------------------------------
self.SetStartDate(2007, 1, 1)
self.SetEndDate(2020, 7, 31)
self.SetCash(1000000)
# set account leverage
self.leverage = 1
# TICKERS ----------------------------------------------------------------------------------
self.equityTicker = 'SPY' # equity like asset
self.safeTicker = 'BIL' # cash/bond like asset
self.benchmarkTicker = 'SPY' # select a benchmark
# TECHNICAL INDICATORS ---------------------------------------------------------------------
self.periodSMA = 200 # period for equity SMA
# SIGNALS AND ALLOCATIONS ------------------------------------------------------------------
# parameters for step 1 ------------------------------------------------
# step 1 (equity position) equity close price is above its SMA for n consecutive days
# number of days to check if price has been above its SMA for n consecutive days
self.daysConsecutivePriceAboveSMA = 5
# allocations = [equityTicker allocation, safeTicker allocation]
self.allocationStepOne = [1, 0] # allocation in step one
# parameters for step 2 ------------------------------------------------
# step 2 (mix position) equity close price is below its SMA for n consecutive days
# number of days to check if price has been below its SMA for n consecutive days
self.daysConsecutivePriceBelowSMA = 5
# allocations = [equityTicker allocation, safeTicker allocation]
self.allocationStepTwo = [0, 1] # allocation in step two
# parameters for step 3 ------------------------------------------------
# step 3 (safe position) equity current SMA is lower than SMA n days ago
# number of days to check if current SMA is lower than n days ago
self.daysAgoLowerSMA = 5
# allocations = [equityTicker allocation, safeTicker allocation]
self.allocationStepThree = [0, 1] # allocation in step three
### -----------------------------------------------------------------------------------------------------------------------
# apply CustomSecurityInitializer
self.SetSecurityInitializer(lambda x: CustomSecurityInitializer(self, x))
# add benchmark
self.SetBenchmark(self.benchmarkTicker)
# add data
self.equitySymbol = self.AddEquity(self.equityTicker, Resolution.Hour).Symbol
self.safeSymbol = self.AddEquity(self.safeTicker, Resolution.Hour).Symbol
# add indicators
self.equitySMA = self.SMA(self.equitySymbol, self.periodSMA, Resolution.Daily)
self.winEquitySMA = RollingWindow[float](self.daysAgoLowerSMA)
# set a warm-up period to initialize the indicator
self.SetWarmUp(self.periodSMA)
# initialize variables
self.countDaysEquityAboveSMA = 0
self.countDaysEquityBelowSMA = 0
self.signal = 0
self.initBenchmarkPrice = 0
# initialize plots
signalsPlot = Chart('Signals')
signalsPlot.AddSeries(Series('Equity SMA', SeriesType.Line, '$', Color.Blue))
signalsPlot.AddSeries(Series('Equity Close Price', SeriesType.Line, '$', Color.Black))
signalsPlot.AddSeries(Series('Equity Position', SeriesType.Scatter, '', Color.Green, ScatterMarkerSymbol.Triangle))
signalsPlot.AddSeries(Series('Mix Position', SeriesType.Scatter, '', Color.Orange, ScatterMarkerSymbol.Triangle))
signalsPlot.AddSeries(Series('Safe Position', SeriesType.Scatter, '', Color.Red, ScatterMarkerSymbol.Triangle))
self.AddChart(signalsPlot)
portfolioExposuresPlot = Chart('Portfolio Exposures')
portfolioExposuresPlot.AddSeries(Series('Equity Exposure', SeriesType.Line, '%', Color.Green))
portfolioExposuresPlot.AddSeries(Series('Safe Exposure', SeriesType.Line, '%', Color.Red))
self.AddChart(portfolioExposuresPlot)
def OnData(self, data):
''' Event triggering every time there is new data '''
if self.Time.hour != 10:
return
if not self.equitySMA.IsReady:
return
# wait until winEquitySMA is ready
if not self.winEquitySMA.IsReady:
currentEquitySMA = self.equitySMA.Current.Value
self.winEquitySMA.Add(currentEquitySMA)
return
if (data.ContainsKey(self.equitySymbol) and data.ContainsKey(self.safeSymbol)
and self.ActiveSecurities[self.equitySymbol].Price > 0
and self.ActiveSecurities[self.safeSymbol].Price > 0):
# get curent SMA value
currentEquitySMA = self.equitySMA.Current.Value
# get last daily close price
history = self.History(self.equitySymbol, 1, Resolution.Daily)
if 'close' in history:
equityClosePrice = history['close'][0]
else:
return
# check if close price is above/below indicator
# add to the count or reset accordingly
if equityClosePrice > currentEquitySMA:
self.countDaysEquityAboveSMA += 1
self.countDaysEquityBelowSMA = 0
else:
self.countDaysEquityBelowSMA += 1
self.countDaysEquityAboveSMA = 0
self.newSignal = False
# equity position: we can get here from any other position
if self.countDaysEquityAboveSMA == self.daysConsecutivePriceAboveSMA and self.signal != 1:
# get allocations
allocationEquity = self.allocationStepOne[0]
allocationSafe = self.allocationStepOne[1]
self.signal = 1
self.newSignal = True
# reset counts
self.countDaysEquityAboveSMA = 0
self.countDaysEquityBelowSMA = 0
# mix position: we can get here only from the equity position
elif self.countDaysEquityBelowSMA == self.daysConsecutivePriceBelowSMA and self.signal != 2 and self.signal != 3:
# get allocations
allocationEquity = self.allocationStepTwo[0]
allocationSafe = self.allocationStepTwo[1]
self.signal = 2
self.newSignal = True
# reset counts
self.countDaysEquityAboveSMA = 0
self.countDaysEquityBelowSMA = 0
# safe position: we can get here from any other position
elif currentEquitySMA < self.winEquitySMA[self.daysAgoLowerSMA-1] and self.signal != 3:
# get allocations
allocationEquity = self.allocationStepThree[0]
allocationSafe = self.allocationStepThree[1]
self.signal = 3
self.newSignal = True
# simulate buy and hold the benchmark and plot its daily value
UpdateBenchmarkValue(self)
self.Plot('Strategy Equity', self.benchmarkTicker, self.benchmarkValue)
# set holdings
if self.newSignal:
self.SetHoldings([PortfolioTarget(self.equitySymbol, allocationEquity),
PortfolioTarget(self.safeSymbol, allocationSafe)])
# update plots
UpdatePlots(self, currentEquitySMA, equityClosePrice)
# update the rolling window
self.winEquitySMA.Add(currentEquitySMA)
def OnOrderEvent(self, orderEvent):
''' Event triggered every time there is a new order event '''
ticket = self.Transactions.GetOrderTicket(orderEvent.OrderId)