Overall Statistics
Total Trades
5835
Average Win
0.34%
Average Loss
-0.20%
Compounding Annual Return
-10.596%
Drawdown
75.500%
Expectancy
-0.140
Net Profit
-63.869%
Sharpe Ratio
-0.269
Probabilistic Sharpe Ratio
0.000%
Loss Rate
68%
Win Rate
32%
Profit-Loss Ratio
1.67
Alpha
-0.079
Beta
0.275
Annual Standard Deviation
0.204
Annual Variance
0.042
Information Ratio
-0.626
Tracking Error
0.227
Treynor Ratio
-0.199
Total Fees
$78061.92
Estimated Strategy Capacity
$9300000.00
Lowest Capacity Asset
TMV UBTUG7D0B7TX
#region imports
from AlgorithmImports import *
from scipy.stats import norm, zscore
#endregion

class MeanReversionDemo(QCAlgorithm):

    def Initialize(self):
        #1. Required: Five years of backtest history
        self.SetStartDate(2014, 1, 1)
    
        #2. Required: Alpha Streams Models:
        self.SetBrokerageModel(BrokerageName.AlphaStreams)
    
        #3. Required: Significant AUM Capacity
        self.SetCash(1000000)
    
        #4. Required: Benchmark to SPY
        self.SetBenchmark("SPY")
        
        self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel())
        self.SetExecution(ImmediateExecutionModel())
    
        self.assets = ["SHY", "TLT", "IEI", "SHV", "TLH", "EDV", "BIL",
                        "SPTL", "TBT", "TMF", "TMV", "TBF", "VGSH", "VGIT",
                        "VGLT", "SCHO", "SCHR", "SPTS", "GOVT"]
        
        # Add Equity ------------------------------------------------ 
        for i in range(len(self.assets)):
            self.AddEquity(self.assets[i], Resolution.Minute)
        
        # Set Scheduled Event Method For Our Model
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SHY", 5), self.EveryDayBeforeMarketClose)
        
        
    def EveryDayBeforeMarketClose(self):
        qb = self
        # Fetch history on our universe
        df = qb.History(qb.Securities.Keys, 30, Resolution.Daily)
        if df.empty: return
    
        # Make all of them into a single time index.
        df = df.close.unstack(level=0)
    
        # Calculate the truth value of the most recent price being less than 1 std away from the mean
        classifier = df.le(df.mean().subtract(df.std())).iloc[-1]
        if not classifier.any(): return
    
        # Get the z-score for the True values, then compute the expected return and probability
        z_score = df.apply(zscore)[[classifier.index[i] for i in range(classifier.size) if classifier.iloc[i]]]
    
        magnitude = -z_score * df.std() / df
        confidence = (-z_score).apply(norm.cdf)
    
        # Get the latest values
        magnitude = magnitude.iloc[-1].fillna(0)
        confidence = confidence.iloc[-1].fillna(0)
    
        # Get the weights, then zip together to iterate over later
        weight = confidence - 1 / (magnitude + 1)
        weight = weight[weight > 0].fillna(0)
        sum_ = np.sum(weight)
        if sum_ > 0:
            weight = (weight) / sum_
            selected = zip(weight.index, magnitude, confidence, weight)
        else:
            return
    
        # ==============================
        
        insights = []
        
        for symbol, magnitude, confidence, weight in selected:
            insights.append( Insight.Price(symbol, timedelta(days=1), InsightDirection.Up, magnitude, confidence, None, weight) )
    
        self.EmitInsights(insights)