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)