Overall Statistics
Total Trades
3
Average Win
0%
Average Loss
-0.39%
Compounding Annual Return
7.732%
Drawdown
1.200%
Expectancy
-1
Net Profit
0.177%
Sharpe Ratio
0.862
Probabilistic Sharpe Ratio
49.038%
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.39
Beta
0.729
Annual Standard Deviation
0.083
Annual Variance
0.007
Information Ratio
-8.743
Tracking Error
0.064
Treynor Ratio
0.098
Total Fees
$7.76
import statsmodels.api as sm
import pandas as pd
from dateutil.relativedelta import relativedelta
from QuantConnect.Indicators import SimpleMovingAverage
from functools import partial 

class OptimizedNadionRadiator(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 1, 6)  # Set Start Date
        self.SetEndDate(2020, 1, 14)
        self.SetCash(100000)  # Set Strategy Cash
        
        symbols = [ Symbol.Create("SPY", SecurityType.Equity, Market.USA) ]
        self.SetUniverseSelection( ManualUniverseSelectionModel(symbols) )
        self.UniverseSettings.Resolution = Resolution.Minute
        
        self.AddAlpha(MyAlphaModel())
        
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        
        self.SetExecution(ImmediateExecutionModel())


class MyAlphaModel(AlphaModel):
    symbol = None
    sma = None
    model = None
    
    def Update(self, algorithm, slice):
        # Only emit insights if the exchange is on its last minute of the day (proxy for daily close)
        exchange = algorithm.Securities[self.symbol].Exchange
        if not (exchange.DateTimeIsOpen(slice.Time) and not exchange.DateTimeIsOpen(slice.Time + relativedelta(minutes=1))):
            return []
        
        close = slice[self.symbol].Close
        
        self.sma.Update(slice.Time, slice[self.symbol].Close)
        normalized_sma = (close - self.sma.Current.Value) / close
        normalized_sma = pd.DataFrame({"const": [1], "normalized_SMA": [normalized_sma]})
        prediction = self.model.predict(normalized_sma).values[0]

        if prediction == 0:
            return []
        
        direction = InsightDirection.Up if prediction > 0 else InsightDirection.Down
        return [Insight.Price(self.symbol, timedelta(days=1), direction)]
        
    def OnSecuritiesChanged(self, algorithm, changes):
        for security in changes.AddedSecurities:
            self.symbol = security.Symbol
            self.sma = SimpleMovingAverage(5)

            history = algorithm.History(self.symbol, 50, Resolution.Daily).loc[self.symbol][['close']]
            for idx, row in history.iterrows():
                self.sma.Update(idx, row.close)
                if self.sma.IsReady:
                    history.loc[idx, "SMA"] = self.sma.Current.Value
            
            history['normalized_SMA'] = (history.close - history.SMA) / history.close
            history['return'] = history.close.pct_change()
            history.dropna(inplace=True)
            
            X = sm.add_constant(history.normalized_SMA)
            y = history['return'].values
            self.model = sm.OLS(y, X).fit()