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)