Overall Statistics
Total Trades
5
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
0
Tracking Error
0
Treynor Ratio
0
Total Fees
$5.00
Estimated Strategy Capacity
$31000000.00
Lowest Capacity Asset
RNR R735QTJ8XC9X
Portfolio Turnover
41.66%
#region imports
from AlgorithmImports import *
#endregion

class FundamentalFactorAlphaModel(AlphaModel):
   
    def __init__(self, str_period=7, str_multiplier=3):
        self.rebalanceTime = datetime.min
        # Dictionary containing set of securities in each sector
        # e.g. {technology: set(AAPL, TSLA, ...), healthcare: set(XYZ, ABC, ...), ... }
        self.sectors = {}
        self.symbolData = {}
        self.exclusions = ['SPY']
        self.str_period = str_period
        self.str_multiplier = str_multiplier

    def Update(self, algorithm, data):
        '''Updates this alpha model with the latest data from the algorithm.
        This is called each time the algorithm receives data for subscribed securities
        Args:
            algorithm: The algorithm instance
            data: The new data available
        Returns:
            New insights'''
        
        algorithm.Plot("myPlot", "SPY", self.spy.Close)

        if algorithm.Time <= self.rebalanceTime:
            return []
        
        # Set the rebalance time to match the insight expiry
        #self.rebalanceTime = Expiry.EndOfQuarter(algorithm.Time)
        self.rebalanceTime = Expiry.EndOfMonth(algorithm.Time)

        insights = []
        
        for sector in self.sectors:
            securities = self.sectors[sector]
            sortedByROE_pos = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.ROE.Value, reverse=True)
            sortedByPM_pos = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.NetMargin.Value, reverse=True)
            sortedByPE_pos = sorted(securities, key=lambda x: x.Fundamentals.ValuationRatios.PERatio, reverse=True)
            sortedByROE_neg = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.ROE.Value, reverse=False)
            sortedByPM_neg = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.NetMargin.Value, reverse=False)
            sortedByPE_neg = sorted(securities, key=lambda x: x.Fundamentals.ValuationRatios.PERatio, reverse=False)

            # Dictionary holding a dictionary of scores for each security in the sector
            scores_pos = {}
            for security in securities:
                score = sum([sortedByROE_pos.index(security), sortedByPM_pos.index(security), sortedByPE_pos.index(security)])
                scores_pos[security] = score

            scores_neg = {}
            for security in securities:
                score = sum([sortedByROE_neg.index(security), sortedByPM_neg.index(security), sortedByPE_neg.index(security)])
                scores_neg[security] = score
            
            for key, sd in self.symbolData.items():
                # Add best 20% of each sector to long set (minimum 1)
                if self.spy.Close > sd.str.Current.Value:
                    #length = max(int(len(scores_pos)/5), 1)
                    length = 1
                    for security in sorted(scores_pos.items(), key=lambda x: x[1], reverse=False)[:length]:
                        symbol = security[0].Symbol
                        # Use Expiry.EndOfQuarter in this case to match Universe, Alpha and PCM
                        #insights.append(Insight.Price(symbol, Expiry.EndOfQuarter, InsightDirection.Up))
                        insights.append(Insight.Price(symbol, Expiry.EndOfMonth, InsightDirection.Up))


                # Add worst 20% of each sector to short set (minimum 1)
                elif self.spy.Close < sd.str.Current.Value:
                    #length = max(int(len(scores_neg)/5), 1)
                    length = 1
                    for security in sorted(scores_neg.items(), key=lambda x: x[1], reverse=False)[:length]:
                        symbol = security[0].Symbol
                        # Use Expiry.EndOfQuarter in this case to match Universe, Alpha and PCM
                        #insights.append(Insight.Price(symbol, Expiry.EndOfQuarter, InsightDirection.Down))
                        insights.append(Insight.Price(symbol, Expiry.EndOfMonth, InsightDirection.Down))

        return insights

    def OnSecuritiesChanged(self, algorithm, changes):
        '''Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''
        
        # Remove security from sector set
        for security in changes.RemovedSecurities:
            if security.Symbol.Value not in self.exclusions:                
                for sector in self.sectors:
                    if security in self.sectors[sector]:
                        self.sectors[sector].remove(security)
        
        # Add security to corresponding sector set
        for security in changes.AddedSecurities:
            if security.Symbol.Value in self.exclusions: # Add SPY to symbolData
                self.symbolData[security.Symbol] = SymbolData(algorithm, security, self.str_period, self.str_multiplier)
                #self.indicators[security.Symbol] = SymbolData(algorithm, security, self.str_period, self.str_multiplier)
                self.spy = security
            if security.Symbol.Value not in self.exclusions:                
                sector = security.Fundamentals.AssetClassification.MorningstarSectorCode
                if sector not in self.sectors:
                    self.sectors[sector] = set()
                self.sectors[sector].add(security)

class SymbolData:
    def __init__(self, algorithm, security, str_period, str_multiplier):
        self.algorithm = algorithm
        self.Security = security
        self.str = SuperTrend(str_period, str_multiplier, MovingAverageType.Wilders)
        #self.ema = ExponentialMovingAverage(20,0.5)

        #self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, Resolution.Daily)
        #algorithm.RegisterIndicator(security.Symbol, self.str, self.Consolidator)

        algorithm.RegisterIndicator(self.Security.Symbol, self.str, Resolution.Daily)
        algorithm.WarmUpIndicator(security.Symbol, self.str, Resolution.Daily)

        #algorithm.RegisterIndicator(self.Security.Symbol, self.ema, Resolution.Daily)
        #algorithm.WarmUpIndicator(security.Symbol, self.ema, Resolution.Daily)


        self.str.Updated += self.OnSTRUpdated
        #self.ema.Updated += self.OnEMAUpdated


    def OnSTRUpdated(self, sender, updated):
        self.algorithm.Plot("myPlot", "STR", updated)

    def OnEMAUpdated(self, sender, updated):
        self.algorithm.Plot("myPlot", "EMA", updated)
# region imports
from AlgorithmImports import *
import numpy as np
# endregion

from AlphaModel import *
class VerticalTachyonRegulators(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2023, 5, 30)
        self.SetEndDate(2023, 6, 1)
        self.SetCash(3000)
        self.EnableAutomaticIndicatorWarmUp = True
        # Universe selection
        self.month = 0
        self.num_coarse = 500
        self.UniverseSettings.Resolution = Resolution.Daily
        #
        # Add SPY to the universe 
        #
        self.spy = self.AddEquity('SPY', Resolution.Daily).Symbol
        self.AddUniverseSelection(ManualUniverseSelectionModel(self.spy))
        #
        # Add rest of stocks to universe
        #
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
       
        # Alpha Model
        self.AddAlpha(FundamentalFactorAlphaModel())
        # Portfolio construction model
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(self.IsRebalanceDue))
       
        # Risk model
        #self.SetRiskManagement(NullRiskManagementModel())
        self.SetRiskManagement(TrailingStopRiskManagementModel(0.1))
        #self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(0.10))
        
        # Execution model
        self.SetExecution(ImmediateExecutionModel())

        chart = Chart("myPlot")
        self.AddChart(chart)        

    # Share the same rebalance function for Universe and PCM for clarity
    def IsRebalanceDue(self, time):
        # Rebalance on the first day of the Quarter or Month
        #if time.month == self.month or time.month not in [1, 4, 7, 10]:
        #if time.month == self.month or time.month not in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]:
        if time.month == self.month or time.month not in np.arange(1,13):
            return None
           
        self.month = time.month
        return time

    def CoarseSelectionFunction(self, coarse):
        # If not time to rebalance, keep the same universe
        if not self.IsRebalanceDue(self.Time): 
            return Universe.Unchanged
        # Select only those with fundamental data and a sufficiently large price
        # Sort by top dollar volume: most liquid to least liquid
        selected = sorted([x for x in coarse if x.HasFundamentalData and x.Price > 5],
                            key = lambda x: x.DollarVolume, reverse=True)
        return [x.Symbol for x in selected[:self.num_coarse]]

    def FineSelectionFunction(self, fine):
        # Filter the fine data for equities that IPO'd more than 5 years ago in selected sectors
       
        sectors = [
            MorningstarSectorCode.FinancialServices,
            MorningstarSectorCode.RealEstate,
            MorningstarSectorCode.Healthcare,
            MorningstarSectorCode.Utilities,
            MorningstarSectorCode.Technology]

        #sectors = [
        #    MorningstarSectorCode.Technology]       

        filtered_fine = [x.Symbol for x in fine if x.SecurityReference.IPODate + timedelta(365*5) < self.Time
                                    and x.AssetClassification.MorningstarSectorCode in sectors
                                    and x.OperationRatios.ROE.Value != 0
                                    and x.OperationRatios.NetMargin.Value != 0
                                    and x.ValuationRatios.PERatio != 0]

        return filtered_fine