Overall Statistics
Total Orders
82
Average Win
2.75%
Average Loss
-2.19%
Compounding Annual Return
-4.102%
Drawdown
25.200%
Expectancy
-0.119
Start Equity
100000
End Equity
88191.82
Net Profit
-11.808%
Sharpe Ratio
-0.238
Sortino Ratio
-0.175
Probabilistic Sharpe Ratio
0.776%
Loss Rate
61%
Win Rate
39%
Profit-Loss Ratio
1.26
Alpha
-0.019
Beta
-0.173
Annual Standard Deviation
0.126
Annual Variance
0.016
Information Ratio
-0.353
Tracking Error
0.272
Treynor Ratio
0.173
Total Fees
$119.40
Estimated Strategy Capacity
$940000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
7.48%
#region imports
from AlgorithmImports import *
#endregion
from collections import deque

class AdaptableSkyBlueHornet(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 1, 1)
        self.SetEndDate(2023, 1, 1)
        self.SetCash(100000)
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        
    #    self.sma = self.SMA(self.spy, 30, Resolution.Daily)
        
        # History warm up for shortcut helper SMA indicator
    #    closing_prices = self.History(self.spy, 30, Resolution.Daily)["close"]
    #    for time, price in closing_prices.loc[self.spy].items():
    #        self.sma.Update(time, price)
        
        # Custom SMA indicator
        self.sma = CustomSimpleMovingAverage("CustomSMA", 30)
        self.RegisterIndicator(self.spy, self.sma, Resolution.Daily)

    
    def OnData(self, data):
        if not self.sma.IsReady:
            return
        
        # Save high, low, and current price
        hist = self.History(self.spy, timedelta(365), Resolution.Daily)
        low = min(hist["low"])
        high = max(hist["high"])
        
        price = self.Securities[self.spy].Price
        
        # Go long if near high and uptrending
        if price * 1.05 >= high and self.sma.Current.Value < price:
            if not self.Portfolio[self.spy].IsLong:
                self.SetHoldings(self.spy, 1)
        
        # Go short if near low and downtrending
        elif price * 0.95 <= low and self.sma.Current.Value > price:  
            if not self.Portfolio[self.spy].IsShort:
                self.SetHoldings(self.spy, -1)
        
        # Otherwise, go flat
        else:
            self.Liquidate()
        
        self.Plot("Benchmark", "52w-High", high)
        self.Plot("Benchmark", "52w-Low", low)
        self.Plot("Benchmark", "SMA", self.sma.Current.Value)


class CustomSimpleMovingAverage(PythonIndicator):
    
    def __init__(self, name, period):
        self.Name = name
        self.Time = datetime.min
        self.Value = 0
        self.queue = deque(maxlen=period)

    def Update(self, input):
        self.queue.appendleft(input.Close)
        self.Time = input.EndTime
        count = len(self.queue)
        self.Value = sum(self.queue) / count
        # returns true if ready
        return (count == self.queue.maxlen)