Overall Statistics
Total Trades
470
Average Win
1.50%
Average Loss
-4.49%
Compounding Annual Return
5.938%
Drawdown
39.400%
Expectancy
0.131
Net Profit
252.789%
Sharpe Ratio
0.387
Probabilistic Sharpe Ratio
0.025%
Loss Rate
15%
Win Rate
85%
Profit-Loss Ratio
0.34
Alpha
0.012
Beta
0.622
Annual Standard Deviation
0.127
Annual Variance
0.016
Information Ratio
-0.113
Tracking Error
0.099
Treynor Ratio
0.079
Total Fees
$470.00
Estimated Strategy Capacity
$26000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
#region imports
from AlgorithmImports import *
#endregion
#################################################################### 
# Simple System that Trades the S&P 500 (SPY)
# Inspired by https://relaxedtrader.com/spy-swing-trading-system/
# Logic / Decision tree: https://imgur.com/a/kYmsiLi
#################################################################### 
class SimpleSpyClimber(QCAlgorithm):

    def Initialize(self):
        ## Initialize algo params
        self.ticker = "SPY"             # Ticker symbol to trade
        self.SetStartDate(2001, 1, 1)   # Backtest start date
        self.SetCash(10000)             # Starting portfolio balance

        ## Subscrbe to the data feed for this ticker. 
        ## We're subscribing to the Daily resolution, 
        ## so each 'bar' (candlesrtick) represents one day. 
        self.Equity = self.AddEquity(self.ticker, Resolution.Minute)

        ## Register our technical indicators w/the ticker so they are updated automatically
        self.MA_66   = self.SMA(self.ticker, 66, Resolution.Daily)  # track moving average of past 66 days
        self.LOW_3   = self.MIN(self.ticker,  3, Resolution.Daily)  # track min price of past 3 days
        self.HIGH_19 = self.MAX(self.ticker, 19, Resolution.Daily)  # track max price of past 19 days        
        
    # A handler that's called every time there is a new bar 
    # (ie candlestick) of data. The 'dataSlice' holds all the data.
    # ==============================================================
    def OnData(self, dataSlice):

        ## Make sure we have data for this ticker before we check for our entry conditions
        if( dataSlice.ContainsKey(self.ticker)) and (dataSlice[self.ticker] is not None ):

            ## The price that the last bar closed at    
            closePrice = dataSlice[self.ticker].Close

            ## If we're not hodling any positions, check for a signal                 
            if not self.Portfolio.Invested:
                
                ## If closing price > 66 day Moving average && is the lowest in past 3 days, take a position.
                if(closePrice > self.MA_66.Current.Value) and (closePrice < self.LOW_3.Current.Value):
                    self.SetHoldings(self.ticker, 1) # allocate 100% of portfolio to ticker

            else:
                # If we're holding, and the day's close price is the highest of the last 19 days, then exit.
                if(closePrice > self.HIGH_19.Current.Value):
                    self.Liquidate()