Overall Statistics
Total Orders
553
Average Win
0.62%
Average Loss
-0.32%
Compounding Annual Return
-0.119%
Drawdown
17.600%
Expectancy
0.002
Start Equity
100000
End Equity
99408.36
Net Profit
-0.592%
Sharpe Ratio
-0.722
Sortino Ratio
-0.388
Probabilistic Sharpe Ratio
0.383%
Loss Rate
66%
Win Rate
34%
Profit-Loss Ratio
1.96
Alpha
-0.042
Beta
0.074
Annual Standard Deviation
0.05
Annual Variance
0.003
Information Ratio
-0.778
Tracking Error
0.14
Treynor Ratio
-0.489
Total Fees
$906.17
Estimated Strategy Capacity
$17000000.00
Lowest Capacity Asset
IGW S6BDJ8ONH2ZP
Portfolio Turnover
13.33%
Drawdown Recovery
0
from AlgorithmImports import *


class CompetitionExampleAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(self.end_date - timedelta(5*365))
        self.set_cash(100_000)
        self.set_universe_selection(TechnologyETFUniverse())
        self.set_alpha(NewsSentimentAlphaModel())
        self.set_portfolio_construction(InsightWeightingPortfolioConstructionModel())
        self.set_risk_management(NullRiskManagementModel())
        
        
class NewsSentimentAlphaModel(AlphaModel):
    
    def __init__(self):
        # Sample pool of word sentiments.
        self._word_sentiment = {
            "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, 
            "un_lucrative": -0.5, "up": 0.5, "down": -0.5,
            "poor": -0.5, "worthwhile": 0.5, "lucrative": 0.5, "solid": 0.5
        }
        self._day = -1
        self._tiingo_symbols = {}
    
    def update(self, algorithm: QCAlgorithm, data: Slice) -> List[Insight]:
        # Run the model daily.
        if algorithm.time.day == self._day:
            return []
        self._day = algorithm.time.day
        weights = {}
        # Calculate sentiment scores for active securities.
        for tiingo_symbol, underlying_symbol in self._tiingo_symbols.items():
            if tiingo_symbol not in data:
                continue
            news = data[tiingo_symbol]
            description_words = news.description.lower().split(" ")
            intersection = set(self._word_sentiment.keys()).intersection(description_words)
            sentiment_sum = sum(self._word_sentiment[word] for word in intersection)
            if sentiment_sum > 0:
                weights[underlying_symbol] = sentiment_sum
        if not weights:
            return []
        # Select the top 10 securities by sentiment score.
        top_securities = sorted(weights.items(), key=lambda x: x[1])[-10:]
        return [
            Insight.price(symbol, timedelta(days=1), InsightDirection.UP, None, None, None, abs(weight))
            for symbol, weight in top_securities
        ]
    
    def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        # Subscribe to Tiingo news for US equities.
        for security in changes.added_securities:
            if security.type == SecurityType.EQUITY:
                tiingo_symbol = algorithm.add_data(TiingoNews, security.symbol).symbol
                self._tiingo_symbols[tiingo_symbol] = security.symbol