Overall Statistics
Total Trades
10838
Average Win
0.39%
Average Loss
-0.33%
Compounding Annual Return
-28.627%
Drawdown
70.200%
Expectancy
-0.046
Net Profit
-62.654%
Sharpe Ratio
-0.546
Probabilistic Sharpe Ratio
0.049%
Loss Rate
56%
Win Rate
44%
Profit-Loss Ratio
1.17
Alpha
-0.265
Beta
1.04
Annual Standard Deviation
0.309
Annual Variance
0.096
Information Ratio
-1.185
Tracking Error
0.22
Treynor Ratio
-0.162
Total Fees
$17742.84
Estimated Strategy Capacity
$4900000.00
Lowest Capacity Asset
COST R735QTJ8XC9X
# region imports
from AlgorithmImports import *
# endregion

class SleepyYellowGreenGull(QCAlgorithm):

    word_scores = {'good': 1, 'great': 1, 'best': 1, 'growth': 1,
                   'bad': -1, 'terrible': -1, 'worst': -1, 'loss': -1}

    def Initialize(self):
        self.SetStartDate(2020, 1, 1)  # Set Start Date
        self.SetEndDate(2022, 12, 1)
        self.SetCash(100000)  # Set Strategy Cash

        self.news_by_symbol = {}
        self.SetSecurityInitializer(BrokerageModelSecurityInitializer(self.BrokerageModel, FuncSecuritySeeder(self.GetLastKnownPrices)))
        self.AddUniverse(self.Universe.ETF("QQQ", Market.USA, self.UniverseSettings, self.ETFConstituentsFilter))

    def OnData(self, data):
        
        symbols = []
        for kvp in data.Get(TiingoNews):
            symbol = kvp.Key
            title_words = kvp.Value.Description.lower()
            score = 0
            for word, word_score in self.word_scores.items():
                if word in title_words:
                    score += word_score
            
            # Contrarian: we are going to buy stocks with negative sentiment/score
            hours = self.Securities[symbol.Underlying].Exchange.Hours
            if score < 0 and hours.IsOpen(self.Time, False) and hours.IsOpen(self.Time - timedelta(minutes=1), False):
                symbols.append(symbol.Underlying)

        count = len(symbols)
        if count == 0:    # Do nothing if there is no negative scores
            return

        targets = [PortfolioTarget(symbol, .90/count)
            for symbol in symbols]

        # Liquidate if invested and no new negative news
        for symbol, holdings in self.Portfolio.items():
            if holdings.Invested and symbol not in symbols:
                targets.append(PortfolioTarget(symbol, 0))

        self.SetHoldings(targets)


    def ETFConstituentsFilter(self, constituents):
        # Get the 10 securities with the largest weight in the index
        selected = sorted([c for c in constituents if c.Weight],
            key=lambda c: c.Weight, reverse=True)[:10]
        symbols = [c.Symbol for c in selected]
        return symbols

    def OnSecuritiesChanged(self, changes):
        for security in changes.RemovedSecurities:
            symbol = security.Symbol
            if security.Invested:
                self.Liquidate(symbol, 'Removed From Universe')
            news_symbol = self.news_by_symbol.pop(symbol, None)
            if news_symbol:
                self.RemoveSecurity(news_symbol)

        for security in changes.AddedSecurities:
            symbol = security.Symbol
            if symbol not in self.news_by_symbol:
                self.news_by_symbol[symbol] = self.AddData(TiingoNews, symbol).Symbol