Overall Statistics
Total Trades
59
Average Win
3.83%
Average Loss
-8.10%
Compounding Annual Return
7.746%
Drawdown
34.200%
Expectancy
0.168
Net Profit
50.793%
Sharpe Ratio
0.475
Probabilistic Sharpe Ratio
8.375%
Loss Rate
21%
Win Rate
79%
Profit-Loss Ratio
0.47
Alpha
0.095
Beta
-0.158
Annual Standard Deviation
0.163
Annual Variance
0.027
Information Ratio
-0.134
Tracking Error
0.254
Treynor Ratio
-0.49
Total Fees
$150.14
from statsmodels.regression.linear_model import OLS
import numpy as np

class DynamicModulatedCoreWave(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 2, 27)  # Set Start Date
        self.SetCash(100000)  # Set Strategy Cash
        self.sym = self.AddEquity('SPY', Resolution.Daily).Symbol
        
        self.nth_trading_day = 0
        
        self.day = RollingWindow[int](50)
        self.data = RollingWindow[float](50)
        self.model = None
        
        self.SetWarmup(50)
        
    def OnData(self, data):
        if not data.ContainsKey(self.sym) or data[self.sym] is None:
            return
        
        close = data[self.sym].Close
        self.nth_trading_day += 1
        
        if self.model is not None and not self.Portfolio.Invested:
            # Making it explicit with kwargs so I don't screw up the function call
            if self.model.is_hold(y=close, x=self.nth_trading_day):
                self.SetHoldings(self.sym, 1)
            else:
                self.reset()
        elif self.model is not None and self.Portfolio.Invested:
            if not self.model.is_hold(y=close, x=self.nth_trading_day):
                self.reset()
        
        self.day.Add(self.nth_trading_day)
        self.data.Add(close)
    
        if not self.day.IsReady or not self.data.IsReady:
            return
        
        if self.model is None:
            model = Model(list(self.data)[::-1], list(self.day)[::-1])
            if model.useable():
                self.model = model
                
    def reset(self):
        self.Liquidate()
        self.nth_trading_day = 0
        self.model = None
        
class Model:
    def __init__(self, y, x):
        model = OLS(y, x)

        self.ols = model.fit()
        self.slope = self.ols.params[0]
        self.r2 = self.ols.rsquared
        self.mean_resid = np.abs(self.ols.resid).mean()
    
    def useable(self, r2=.7):
        return self.r2 > r2 and self.slope > 0
        
    def is_hold(self, y, x):
        return y + self.mean_resid > self.ols.predict(x)[0]