| 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 '''