| Overall Statistics |
|
Total Trades 9716 Average Win 0.17% Average Loss -0.05% Compounding Annual Return 291.942% Drawdown 65.500% Expectancy 0.266 Net Profit 98.172% Sharpe Ratio 3.116 Probabilistic Sharpe Ratio 64.666% Loss Rate 71% Win Rate 29% Profit-Loss Ratio 3.36 Alpha 2.811 Beta 1.61 Annual Standard Deviation 1.023 Annual Variance 1.047 Information Ratio 2.925 Tracking Error 1.01 Treynor Ratio 1.981 Total Fees $33421.37 Estimated Strategy Capacity $40000.00 Lowest Capacity Asset GWPH 2T |
"""
420 Friendly
Take positions based on legalization terms in news articles.
Backtest Report: https://www.quantconnect.com/reports/8589b1fe98375a450d74889521470e38
Backtest: https://www.quantconnect.com/terminal/processCache/?request=embedded_backtest_8589b1fe98375a450d74889521470e38.html
"""
from QuantConnect.Data.Custom.Tiingo import *
from datetime import timedelta
class FourTwentyFriendlyAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 12, 9)
self.SetCash(100000)
weed_symbols = [
Symbol.Create(sym, SecurityType.Equity, Market.USA) for sym in [
'ABBV', 'BUD', 'MO', 'TAP', 'WEED.TO', 'SMG', 'CGC', 'TLRY',
'GWPH', 'CRON', 'ACB', 'NBEV', 'CRBP', 'TGOD.TO', 'TGODF',
'CANN', 'MJ', 'SNDL'
]
]
self.SetUniverseSelection(ManualUniverseSelectionModel(weed_symbols))
self.SetAlpha(FourTwentyFriendlyAlphaModel())
self.SetExecution(StandardDeviationExecutionModel())
self.SetPortfolioConstruction(AccumulativeInsightPortfolioConstructionModel())
self.SetRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(maximumUnrealizedProfitPercent=0.1168))
def OnData(self, data):
""" OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
"""
pass
class FourTwentyFriendlyAlphaModel(AlphaModel):
def __init__(self):
self.Name = '420FriendlyAlphaModel'
self.newsData = {}
self.wordScores = {
"legalization": 5, "legal": 5
}
def Update(self, algorithm, data=None):
"""
Updates this alpha model with the latest data from the algorithm.
This is called each time the algorithm receives data for subscribed securities
Generate insights on the securities in the universe.
"""
insights = []
if data is None:
return insights
news = data.Get(TiingoNews)
for article in news.Values:
words = article.Description.lower().split(" ")
score = sum(
[
self.wordScores[word] for word in words
if word in self.wordScores
]
)
symbol = article.Symbol.Underlying
# Add scores to the rolling window associated with its newsData symbol
self.newsData[symbol].Window.Add(score)
# Sum the rolling window scores, save to sentiment
# If sentiment aggregate score for the time period is greater than 5, emit an up insight
sentiment = sum(self.newsData[symbol].Window)
if sentiment > 5:
insights.append(Insight.Price(symbol, timedelta(1), InsightDirection.Up, None, None))
else:
insights.append(Insight.Price(symbol, timedelta(1), InsightDirection.Flat, None, None))
return insights
def OnSecuritiesChanged(self, algorithm, changes=None):
if changes is None:
return
for security in changes.AddedSecurities:
symbol = security.Symbol
news_asset = algorithm.AddData(TiingoNews, symbol)
self.newsData[symbol] = NewsData(news_asset.Symbol)
for security in changes.RemovedSecurities:
newsData = self.newsData.pop(security.Symbol, None)
if newsData is not None:
algorithm.RemoveSecurity(newsData.Symbol)
class NewsData:
def __init__(self, symbol):
self.Symbol = symbol
self.Window = RollingWindow[float](100)
class SymbolData:
"""Contains data specific to a symbol required by this model"""
def __init__(self, security):
self.Security = security
self.Symbol = security.Symbol