Overall Statistics
Total Trades
0
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
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
#region imports
from AlgorithmImports import *
#endregion
from QuantConnect.Algorithm import *
from QuantConnect.Securities.Option import OptionPriceModels

class RegAlytics(QCAlgorithm):
    symbols_dict = {}       

    def Initialize(self):
        
        self.SetStartDate(2022, 1, 1)  
        self.SetEndDate(2022, 6, 1)  
        self.SetCash(100000) 

        self.UniverseSettings.Resolution = Resolution.Daily
        symbol_names = ["WTI", "XLE", "XLF", "XLV", "BND", "QQQ", "SPY"]
    
        for symbol_name in symbol_names:
            self.symbols_dict[symbol_name] = Symbol.Create(symbol_name, SecurityType.Equity, Market.USA)

        for ticker in symbol_names:

            equity = self.AddEquity(ticker)
            option = self.AddOption(ticker)
            '''self.option_symbol = option.Symbol'''

        # set our strike/expiry filter for this option chain
            option.SetFilter(lambda u: (u.Strikes(-5, +5)
                                     # Expiration method accepts TimeSpan objects or integer for days.
                                     # The following statements yield the same filtering criteria
                                     .Expiration(0, 180)))
                                     #.Expiration(TimeSpan.Zero, TimeSpan.FromDays(180))))
    
        self.AddUniverseSelection(ManualUniverseSelectionModel(list(self.symbols_dict.values())))
     
        # Add an instance of the REGSentimentAlphaModel
        
        self.AddAlpha(REGSentimentAlphaModel(self))
        self.SetPortfolioConstruction(NullPortfolioConstructionModel())
        self.SetExecution(ImmediateExecutionModel())
        self.AddRiskManagement(NullRiskManagementModel())
        
    
        

class REGSentimentAlphaModel(AlphaModel):

    target_direction = InsightDirection.Up
    last_news_date = datetime.min
    symbol_names = ["WTI", "XLE", "XLF", "XLV", "BND", "QQQ", "SPY"]
    
    symbols_dict = {}

    open_interest_puts = 0
    open_interest_calls = 0

    negative_sentiment_phrases = ["against","privacy","discrimination","investigation", "charges", "penalty", "violation", "cease and desist", "sanctions", "antitrust", "limiting", "emergency rule", "proposed rule change", "development of rulemaking", "increase rate", "lawsuit", "sue", "shortage", "crisis", "unemployment increase", "damage", "danger", "poor", "recession", "inflation", "reject", "bad"]
    positive_sentiment_phrases = ["growth","better", "approve", "allow", "produce", "surplus", "develop", "gains", "good", "great", "best", "increase", "production", "aid", "stimulus", "positive", "create", "add", "benefit", "promote", "construction", "provide", "loan"]
    negative_news_affect_duration = timedelta(days = 2)
    positive_news_affect_duration = timedelta(days = 2)

  
   #class members
    spy_sector_phrases = ["interest rate", "inflation", "tesla", "apple", "infrastructure bill", "stimulus", "supply chain"]
    qqq_sector_phrases = ["apple","semiconductor", "microsoft", "tesla", "nvidia", "google", "amazon", "facebook", "meta", "netflix", "qualcomm", "amd", "intel corp.", "adobe" , "tech", "nasdaq"]
    xlv_sector_phrases = [ "pfizer", "moderna", "biontech", "johnson and johnson", "coronavirus", "covid", "vaccine"]
    xle_sector_phrases = ["energy","battery", "electric", "wind", "natural gas", "lithium", "cobalt","nextera", "solar"]
    wti_sector_phrases = ["oil", "gas", "fuel", "drilling", "pipeline", "exxon", "petrol", "chevron", "conocophillips", "occidental petrol" ]
    #xlf
    
    def __init__(self, algorithm: QCAlgorithm) -> None:   
        # Requesting data
        self.regalytics_symbol = algorithm.AddData(RegalyticsRegulatoryArticles, "REG").Symbol

        # Historical data
        history = algorithm.History(self.regalytics_symbol, 7, Resolution.Daily)
        algorithm.Debug(f"We got {len(history)} from our history request")
    
    def Update(self, algorithm: QCAlgorithm, slice: Slice) -> List[Insight]:
        
        insights = []
        
        # data = slice.Get(RegalyticsRegulatoryArticles)

        # if not data: 
        #     return

        if slice.ContainsKey(self.regalytics_symbol) and slice[self.regalytics_symbol] is not None:
            alerts = slice[self.regalytics_symbol]
            title = [alert.Title.lower() for alert in alerts]
            summary = [alert.Summary.lower() for alert in alerts]
            for alert in alerts:
                algorithm.Debug(f"Regulatory Alert received at {alert.Time}. Id: {alert.Id}; Title: {alert.Title}; Summary: {alert.Summary}; Status: {alert.Status}; Classification: {alert.Classification}; FilingType: {alert.FilingType}; InFederalRegister: {alert.InFederalRegister}; FederalRegisterNumber: {alert.FederalRegisterNumber}; ProposedCommentsDueDate: {alert.ProposedCommentsDueDate}; OriginalPublicationDate: {alert.OriginalPublicationDate}; FederalRegisterPublicationDate: {alert.FederalRegisterPublicationDate}; RuleEffectiveDate: {alert.RuleEffectiveDate}; LatestUpdate: {alert.LatestUpdate}; AlertType: {alert.AlertType}; States: {alert.States}; Agencies: {alert.Agencies}; AnnouncementUrl: {alert.AnnouncementUrl}")
            
            word_bank = title.lower().split() + summary.lower().split()
            
            chosen_symbol = self.selectETF(algorithm, word_bank)
            if (chosen_symbol == None):
              	return 
            	
            chain = slice.OptionChains.get(self.chosen_symbol)
            if chain:
                for contract in chain.contracts:
            #contract = chain.Contracts.get(self.contract_symbol)
                    if option.Symbol.ID.OptionRight == OptionRight.Call:
                        self.open_interest_calls += contract.OpenInterest
                    else:
                        self.open_interest_puts += contract.OptionInterest
  
            #list.count(element) => number of occurences of element in the list
            putcall = self.open_interest_puts / (self.open_interest_calls + self.open_interest_puts)
            callput = self.open_interest_calls / (self.open_interest_calls + self.open_interest_puts)
           
            negative = sum([word_bank.count(word) for word in self.negative_sentiment_phrases])
            positive = sum([word_bank.count(word) for word in self.positive_sentiment_phrases])
            	
        
            # Add put call ratio to P/N OI ratio (as decimal) in article to get severity
            if positive > negative:
                pos_ratio = positive / (positive + negative)
                severity = (pos_ratio + callput )/2

                # Signal an entry in the market when regulatory articles with more positive sentiment are released
                
                if severity <= 0.3:
                    self.target_holdings = .1
                elif severity > .3 and severity <= .6:
                    self.target_holdings = .2
                else: #severity > .6
                    self.target_holdings = .3
            
                self.target_direction = InsightDirection.Up
                self.last_news_date = slice.Time

                if self.last_news_date + self.news_affect_duration < slice.Time:
                    self.target_direction = InsightDirection.Flat 
            
            else:
                neg_ratio = negative / (positive + negative)
                severity = (neg_ratio + putcall )/2
                # Signal an exit from the market when regulatory articles with more negative sentiment are released
                self.target_holdings = 0
                if self.Portfolio[chosen_symbol].Invested: 
                    self.Liquidate(security_hold.Symbol)

                self.target_direction = InsightDirection.Flat 
                self.last_news_date = slice.Time 
                
                if self.last_news_date + self.news_affect_duration < slice.Time:
                    self.target_direction = InsightDirection.Up 
            	    
            self.SetHoldings(self.chosen_symbol, self.target_holdings)
            
        
            # Ensure we have security data in the current Slice
            #if not (slice.ContainsKey(chosen_symbol) and slice[chosen_symbol] is not None):
                #continue
            
            if self.target_direction == InsightDirection.Up and not self.Portfolio[chosen_symbol].Invested:
                insights += [Insight.Price(chosen_symbol, timedelta(days=90), InsightDirection.Up)]
            elif self.target_direction == InsightDirection.Flat and self.Portfolio[chosen_symbol].Invested:
                insights += [Insight.Price(chosen_symbol, self.news_affect_duration, InsightDirection.Flat)]
        
        return insights

#method       
def selectETF(self, algorithm, word_bank):
        
        '''
        return index (number) of etf each symbol list based on phrases in word bank
        @word_bank lowercase list of words to match
        '''

        
        #makes sure title and summary are list of words
        for phrase in self.spy_sector_phrases:
            if phrase in word_bank: 
                    return algorithm.symbols_dict["SPY"]
                    
        for phrase in self.qqq_sector_phrases:
                if phrase in word_bank:
                     return algorithm.symbols_dict["QQQ"]
                    
        for phrase in self.xlv_sector_phrases:
                if phrase in word_bank:
                     return algorithm.symbols_dict["XLV"]
                    
        for phrase in self.xle_sector_phrases:
                if phrase in word_bank:
                     return algorithm.symbols_dict["XLE"]
                    
        for phrase in self.wti_sector_phrases:
                if phrase in word_bank:
                     return algorithm.symbols_dict["WTI"]
              
        #no phrase matched
        return algorithm.symbols_dict["NONE"]  #throw exception or use dummy
        
        
        
           
'''class RiskMOM_Model(AlphaModel):
            def __init__(self):
                self.MOM = []
            
            def OnSecuritiesChanged(self, algorithm, changes):
                # Initialize a 14 day, 30 day and 90 day momentum indicator for each symbol
                for security in changes.AddedSecurities:
                    symbol = security.Symbol
                    self.mom.append({"symbol":symbol, "indicator":algorithm.MOM(symbol, 14, Resolution.Daily)})
                
                for security in changes.AddedSecurities:
                    symbol = security.Symbol
                    self.mom.append({"symbol":symbol, "indicator":algorithm.MOM(symbol, 30, Resolution.Daily)})
                    
                for security in changes.AddedSecurities:
                    symbol = security.Symbol
                    self.mom.append({"symbol":symbol, "indicator":algorithm.MOM(symbol, 90, Resolution.Daily)})
                    
                # Check if MOM and Sentiment are the same
                # If not same cancel insight unless high severity 
                # If same 
        
   # def OnSecuritiesChanged(self, changes):
        #for security in changes.AddedSecurities:
           # self.symbols.append(security.Symbol)
        
       # for security in changes.RemovedSecurities:
           # symbol = security.Symbol
           # if symbol in self.symbols:
           #     self.symbols.remove(symbol)
#calculate VIX change over the last 5 days to determine MOM period to associate 

    if sentiment and MOM do not match:
        cancel insight
        
    else:
        use 10 MOM day for +/= 2.5% change in VIX
        use 30 MOM day for positive change
        use 60 MOM day for negative '''