Overall Statistics
Total Trades
148
Average Win
1.58%
Average Loss
-0.82%
Compounding Annual Return
9.030%
Drawdown
9.100%
Expectancy
0.527
Net Profit
35.765%
Sharpe Ratio
0.846
Probabilistic Sharpe Ratio
33.442%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.94
Alpha
0.057
Beta
0.073
Annual Standard Deviation
0.077
Annual Variance
0.006
Information Ratio
-0.214
Tracking Error
0.177
Treynor Ratio
0.889
Total Fees
$7915.85
from QuantConnect.Data.Custom.Tiingo import *
from datetime import datetime, timedelta
import takeprofit
import numpy as np

class SentimentAlpha(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017, 1, 1)
        self.SetEndDate(2020, 7, 14)
        self.SetCash(1000000)
        
        self.tickers = ["TSLA", "MSFT", "GS", "PEP", "NIO", "F", "MTCH","DDOG", "LUV", "TMUS", "AMD", "INO"]
         
        symbols = []
        
        for i in self.tickers:
            symbols.append(Symbol.Create(i, SecurityType.Equity, Market.USA))
        
        self.UniverseSettings.Resolution = Resolution.Hour
        self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
        self.SetAlpha(SentimentWindow())
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) 
        self.SetExecution(ImmediateExecutionModel()) 
        self.SetRiskManagement(takeprofit.TrailingStopRiskManagementModel())
        

class NewsData():
    def __init__(self, symbol):
        self.Symbol = symbol
        #two month rolling window if insights are emitted daily
        self.Window = RollingWindow[float](60)  
        
class SentimentWindow(AlphaModel):
    
    def __init__(self): 
        self.newsData = {}

        self.wordScores = {
            "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,
            "unproductive": -0.5, "poor": -0.5, "wrong": -0.5,
            "worthwhile": 0.5, "lucrative": 0.5, "solid": 0.5,
            "beat": 0.5, "missed": -0.5
            
        }
        
        self.lastday = -1
        
    def Update(self, algorithm, data):

        insights = []
        
        day = algorithm.Time.day
        if day == self.lastday:
            return insights
        self.lastday = day
        
        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])
                
            if score == 0.0:
                continue
            
            symbol = article.Symbol.Underlying 
    
            self.newsData[symbol].Window.Add(score)
            
            
            if self.newsData[symbol].Window.Count < 3:
                return insights
                
            currentWindow = self.newsData[symbol].Window[0]
                
            sentimentMean = sum(self.newsData[symbol].Window)/self.newsData[symbol].Window.Count
            algorithm.Debug("Mean "+ str(sentimentMean))
            algorithm.Debug("Window "+ str(currentWindow))
            
            if currentWindow > (sentimentMean*1.1):
                insights.append(Insight.Price(symbol, timedelta(days=2), InsightDirection.Up, None, None))
            elif currentWindow < -1:
                insights.append(Insight.Price(symbol, timedelta(days=1), InsightDirection.Down, None, None))
            else:
                insights.append(Insight.Price(symbol, timedelta(days=1), InsightDirection.Flat, None, None))
            
        return insights
    
    def OnSecuritiesChanged(self, algorithm, changes):

        for security in changes.AddedSecurities:
            symbol = security.Symbol
            newsAsset = algorithm.AddData(TiingoNews, symbol)
            self.newsData[symbol] = NewsData(newsAsset.Symbol)

        for security in changes.RemovedSecurities:
            newsData = self.newsData.pop(security.Symbol, None)
            if newsData is not None:
                algorithm.RemoveSecurity(newsData.Symbol)
class TrailingStopRiskManagementModel(RiskManagementModel):
    '''Provides an implementation of IRiskManagementModel that limits the maximum possible loss
    measured from the highest unrealized profit'''
    
    def __init__(self):
        '''Initializes a new instance of the TrailingStopRiskManagementModel class'''

        self.liquidated = set()
        self.lastmonth=-1
        
    def ManageRisk(self, algorithm, targets):
        '''Manages the algorithm's risk at each time step
        Args:
            algorithm: The algorithm instance
            targets: The current portfolio targets to be assessed for risk'''
        month= algorithm.Time.month
        if month!= self.lastmonth:
            self.liquidated.clear()
            self.lastmonth= month
        #if different month clear all liquidations
        riskAdjustedTargets = list()

        for kvp in algorithm.Securities:
            symbol = kvp.Key
            security = kvp.Value

            if security.Holdings.UnrealizedProfitPercent>0.05 or security.Holdings.UnrealizedProfitPercent<-0.03 or security.Symbol in self.liquidated:
                riskAdjustedTargets.append(PortfolioTarget(symbol, 0))
                if algorithm.Securities[security.Symbol].Invested:
                    self.liquidated.add(security.Symbol)
        return riskAdjustedTargets