Overall Statistics
Total Trades
8
Average Win
0.13%
Average Loss
-0.41%
Compounding Annual Return
-16.751%
Drawdown
0.900%
Expectancy
-0.335
Net Profit
-0.551%
Sharpe Ratio
-2.891
Probabilistic Sharpe Ratio
18.226%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
0.33
Alpha
-0.119
Beta
0.129
Annual Standard Deviation
0.041
Annual Variance
0.002
Information Ratio
-1.404
Tracking Error
0.09
Treynor Ratio
-0.919
Total Fees
$8.00
from datetime import datetime, timedelta
import numpy as np

from Risk.MaximumSectorExposureRiskManagementModel import MaximumSectorExposureRiskManagementModel
from QuantConnect.Data.Custom.Tiingo import *
    
class Compbasealgo(QCAlgorithm):

    def Initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''

        self.SetStartDate(2015, 8, 4)   # 5 years up to the submission date
        self.SetEndDate(2015, 8, 14)
        self.SetCash(1000000)           # Set $1m Strategy Cash to trade significant AUM
        self.SetBenchmark('SPY')        # SPY Benchmark
        self.SetBrokerageModel(AlphaStreamsBrokerageModel())
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddAlpha(MixedAlphaModel())
        self.SetRiskManagement(MaximumDrawdownPercentPerSecurityCustom(0.01))
        self.SetExecution(ImmediateExecutionModel())
        self.SetUniverseSelection(LiquidETFUniverse())
        self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel())
        
class MixedAlphaModel(AlphaModel):
    def __init__(self,
                 fastPeriod = 12,
                 slowPeriod = 26,
                 signalPeriod = 9,
                 movingAverageType = MovingAverageType.Simple,
                 resolution = Resolution.Daily):
        self.fastPeriod = fastPeriod
        self.slowPeriod = slowPeriod
        self.signalPeriod = signalPeriod
        self.movingAverageType = movingAverageType
        self.resolution = resolution
        self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(resolution), fastPeriod)
        self.bounceThresholdPercent = 0.1
        self.symbolData2 = {}
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        movingAverageTypeString = Extensions.GetEnumString(movingAverageType, MovingAverageType)
        self.Name = '{}({},{},{},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, signalPeriod, movingAverageTypeString, resolutionString)
        self.direction = InsightDirection.Flat
        
        
        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.currentWindow=0.1
        self.sentimentMean=0.1
        self.symbol2="EURUSD"
        
    def Update(self, algorithm, data):
            
        insights = []
        
        for key, sd in self.symbolData2.items():
            self.symbol= key

            tiingoNews=algorithm.Securities[self.symbol].Data.GetAll(TiingoNews)
            for article in tiingoNews:
                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
                self.currentWindow = self.newsData[symbol].Window[0]
                self.sentimentMean = sum(self.newsData[symbol].Window)/self.newsData[symbol].Window.Count

            if not (sd.MACD.IsReady and sd.Reg.IsReady):
                continue
            
            normalized_signal = sd.MACD.Signal.Current.Value / sd.Security.Price
            normalized_slope=sd.Reg.Slope.Current.Value/sd.Security.Price

            #if self.direction == sd.PreviousDirection:
             #   continue
            if normalized_signal > self.bounceThresholdPercent and normalized_slope>0 and self.currentWindow > (self.sentimentMean*1.1):
                insight = Insight.Price(sd.Security.Symbol, timedelta(hours = 1), InsightDirection.Up, None, None, None, 0.05)                                                  
                self.direction = InsightDirection.Up
                insights.append(insight)
                sd.PreviousDirection = insight.Direction
        
            elif normalized_signal < -self.bounceThresholdPercent and normalized_slope<-0 and self.currentWindow < -1:
                self.direction = InsightDirection.Down
                insight = Insight.Price(sd.Security.Symbol, timedelta(hours = 1), InsightDirection.Down, None, None, None, 0.05)
                insights.append(insight)
                sd.PreviousDirection = insight.Direction

            elif normalized_signal>-self.bounceThresholdPercent and normalized_signal<self.bounceThresholdPercent:
                self.direction = InsightDirection.Flat
                insight = Insight.Price(sd.Security.Symbol, timedelta(hours = 1), InsightDirection.Flat)
                insights.append(insight)
            
        return insights
        

    def OnSecuritiesChanged(self, algorithm, changes):
        for added in changes.AddedSecurities:
            self.symbol=added.Symbol
            self.symbolData2[added.Symbol] = SymbolData2(algorithm, added, self.fastPeriod, self.slowPeriod, self.signalPeriod, self.movingAverageType, self.resolution)
            newsAsset = algorithm.AddData(TiingoNews, self.symbol)
            self.newsData[self.symbol] = NewsData(newsAsset.Symbol)
            
        for removed in changes.RemovedSecurities:
            data = self.symbolData2.pop(removed.Symbol, None)
            if data is not None:
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator)
            newsData = self.newsData.pop(removed.Symbol, None)
            if newsData is not None:
                algorithm.RemoveSecurity(newsData.Symbol)    

                
class SymbolData2(object):
    def __init__(self, algorithm, security, fastPeriod, slowPeriod, signalPeriod, movingAverageType, resolution):
        self.Security = security
        self.MACD = MovingAverageConvergenceDivergence(fastPeriod, slowPeriod, signalPeriod, movingAverageType)
        self.Reg = RegressionChannel(50, 2)
        self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution)
        algorithm.RegisterIndicator(security.Symbol, self.MACD, self.Consolidator)
        algorithm.RegisterIndicator(security.Symbol, self.Reg, self.Consolidator)
        self.PreviousDirection = None
        
        history = algorithm.History(security.Symbol, 100, Resolution.Daily)
        self.WarmUpIndicators2(history)
         
    def WarmUpIndicators2(self, history):
        for index, row in history.loc[str(self.Security.Symbol)].iterrows():
            self.MACD.Update(index, row["close"])
            self.Reg.Update(index, row["close"])
            
            
class MaximumDrawdownPercentPerSecurityCustom(RiskManagementModel):

    def __init__(self, maximumDrawdownPercent = 0.001, reenter_after_days = 1):
        self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)
        self.maximumProfitPercent = abs(0.002)
        self.liquidated_dates_by_symbol = {}
        self.reenter_after_days = timedelta(reenter_after_days)

    def ManageRisk(self, algorithm, targets):
        symbols_to_pop = []
        for symbol, liquidation_date in self.liquidated_dates_by_symbol.items():
            if algorithm.Time - liquidation_date >= self.reenter_after_days:
                symbols_to_pop.append(symbol)
        for symbol in symbols_to_pop:
            self.liquidated_dates_by_symbol.pop(symbol, None)
        
        targets = []
        for kvp in algorithm.Securities:
            security = kvp.Value

            pnl = security.Holdings.UnrealizedProfitPercent
            if pnl > self.maximumProfitPercent or pnl < self.maximumDrawdownPercent or security.Symbol in self.liquidated_dates_by_symbol:
                # liquidate
                targets.append(PortfolioTarget(security.Symbol, 0))
                if algorithm.Securities[security.Symbol].Invested:
                    self.liquidated_dates_by_symbol[security.Symbol] = algorithm.Time

        return targets
        
class NewsData():
    def __init__(self, symbol):
        self.Symbol = symbol
        self.Window = RollingWindow[float](100)