| Overall Statistics |
|
Total Orders 58 Average Win 1.19% Average Loss -0.87% Compounding Annual Return 22.657% Drawdown 11.100% Expectancy 0.581 Start Equity 100000 End Equity 120615.14 Net Profit 20.615% Sharpe Ratio 1.154 Sortino Ratio 1.484 Probabilistic Sharpe Ratio 76.101% Loss Rate 33% Win Rate 67% Profit-Loss Ratio 1.37 Alpha 0.031 Beta 0.657 Annual Standard Deviation 0.091 Annual Variance 0.008 Information Ratio -0.118 Tracking Error 0.067 Treynor Ratio 0.16 Total Fees $72.50 Estimated Strategy Capacity $480000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 12.20% |
from AlgorithmImports import *
class SentimentAnalysis(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 12, 1)
self.spy = self.add_equity("SPY", Resolution.DAILY).symbol
self.set_benchmark(self.spy)
#TODO:
self.set_universe_selection(ManualUniverseSelectionModel(self.spy))
self.set_alpha(NewsSentimentAlphaModel())
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
self.set_risk_management(NullRiskManagementModel())
class NewsData():
def __init__(self, symbol):
self.symbol = symbol
self.window = RollingWindow[float](100)
class NewsSentimentAlphaModel(AlphaModel):
def __init__(self):
self.news_data = {}
self.word_scores = {
"risk": -0.5, "opportunity": 0.5, "plunge": -0.5,
"recovery": 0.5, "volatile": -0.5, "steady": 0.5,
"decline": -0.5, "rise": 0.5, "loss": -0.5,
"gain": 0.5, "cut": -0.5, "increase": 0.5,
"reduce": -0.5, "expand": 0.5, "collapse": -0.5,
"revenue": 0.5, "decrease": -0.5, "improve": 0.5,
"strong": 0.5, "weak": -0.5, "boost": 0.5,
"fall": -0.5, "recover": 0.5, "underperform": -0.5,
"outperform": 0.5, "struggle": -0.5, "thrive": 0.5,
"trouble": -0.5, "prosper": 0.5, "drop": -0.5,
"stability": 0.5, "tumble": -0.5, "surge": 0.5,
"pressure": -0.5, "optimistic": 0.5, "bearish": -0.5,
"bullish": 0.5, "crash": -0.5, "slump": -0.5,
"bad": -0.5, "good": 0.5, "negative": -0.5,
"great": 0.5, "growth": 0.5, "fail": -0.5,
"failed": -0.5, "success": 0.5, "nailed": 0.5,
"beat": 0.5, "missed": -0.5, "profitable": 0.5,
"beneficial": 0.5, "right": 0.5, "positive": 0.5,
"large":0.5, "attractive": 0.5, "sound": 0.5,
"excellent": 0.5, "wrong": -0.5, "unproductive": -0.5,
"lose": -0.5, "missing": -0.5, "mishandled": -0.5,
"unlucrative": -0.5, "up": 0.5, "down": -0.5,
"unproductive": -0.5, "poor": -0.5, "wrong": -0.5,
"worthwhile": 0.5, "lucrative": 0.5, "solid": 0.5
}
def update(self, algorithm, data):
insights = []
news = data.get(TiingoNews)
for article in news.values():
words = article.description.lower().split(" ")
score = 0
for word in words:
if word in self.word_scores:
score += self.word_scores[word]
symbol = article.symbol.underlying
self.news_data[symbol].window.add(score)
sentiment = sum(self.news_data[symbol].window)
if sentiment > 5:
insights.append(Insight.price(symbol, timedelta(1), InsightDirection.Up, None, None))
return insights
def on_securities_changed(self, algorithm, changes):
for security in changes.added_securities:
symbol = security.symbol
news_asset = algorithm.add_data(TiingoNews, symbol)
self.news_data[symbol] = NewsData(news_asset.symbol)
for security in changes.removed_securities:
news_data = self.news_data.pop(security.symbol, None)
if news_data is not None:
algorithm.remove_security(news_data.symbol)