Overall Statistics
Total Trades
8
Average Win
1.87%
Average Loss
-0.40%
Compounding Annual Return
0.212%
Drawdown
1.200%
Expectancy
0.408
Net Profit
0.637%
Sharpe Ratio
0.156
Probabilistic Sharpe Ratio
2.284%
Loss Rate
75%
Win Rate
25%
Profit-Loss Ratio
4.63
Alpha
0.001
Beta
0.001
Annual Standard Deviation
0.01
Annual Variance
0
Information Ratio
-1.004
Tracking Error
0.106
Treynor Ratio
1.048
Total Fees
$16067.28
from clr import AddReference
AddReference("QuantConnect.Indicators")
from QuantConnect.Indicators  import IchimokuKinkoHyo

class IchimokuAlphaModel(AlphaModel):
    ''' 
    Enhanced IchimokuKinkoHyo Indicator is used to generate trend signal.
    We use tenkansen, kijunsen, spanA,spanB and Chikou span to calculate the 
    deltas. We then combine then to get a bullish or bearish signal
    
    '''
    
    def __init__(self, tenkan_period=9, kijun_period=26, 
                senkouA_period=26,senkouB_period=52,
                chikou_period=26,chikou_threshold=0,kijun_threshold=0,resolution=Resolution.Daily):

                    
        self.TenkanPeriod = tenkan_period
        self.KijunPeriod = kijun_period
        self.SenkouAPeriod = senkouA_period
        self.SenkouBPeriod = senkouB_period
        self.ChikouPeriod = chikou_period
        self.ChikouThreshold = chikou_threshold
        self.KijunThreshold = kijun_threshold
        self.resolution = resolution
        self.symbolData = {};
        self.insightPeriod = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.SenkouBPeriod)
        
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{},{},{},{},{},{},{})'.format(self.__class__.__name__, 
                                                tenkan_period, kijun_period, senkouA_period, 
                                                senkouB_period, chikou_period, 
                                                chikou_threshold, kijun_threshold, 
                                                resolutionString)
                                                

    def Update(self, algorithm, data):
        ''' Determines an insight for each security based on it's current Ichimoku signal
        Args:
            algorithm: The algorithm instance
            data: The new data available
        Returns:
            The new insights generated'''
        insights = []
        
        for symbol, sd in self.symbolData.items():
            
            direction = InsightDirection.Flat
            if sd.strong_bullish() and sd.price_above_kijunsen() and sd.price_above_kumo():
                direction = InsightDirection.Up
            if sd.strong_bearish() and sd.price_below_kijunsen() and sd.price_below_kumo():
                direction = InsightDirection.Down
            
            if direction != InsightDirection.Flat and direction != sd.PreviousDirection:
                algorithm.Debug("Added  Insight for Symbol : " + str(symbol) + " Direction : " + str(direction))
                insights.append(Insight.Price(symbol, self.insightPeriod, direction))
                sd.PreviousDirection = direction
                    
        return insights
            
    
    def OnSecuritiesChanged(self, algorithm, changes):
        '''Event fired each time the we add/remove securities from the data feed.
        This initializes the Ichimoku for each added security and cleans up the indicator for each removed security.
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''
        
        # Handle Added Securities
        for security in changes.AddedSecurities:
            self.symbolData[security.Symbol] = SymbolData(security, algorithm, 
                                                            self.TenkanPeriod, 
                                                            self.KijunPeriod, 
                                                            self.SenkouAPeriod,
                                                            self.SenkouBPeriod,
                                                            self.ChikouPeriod,
                                                            self.ChikouThreshold,
                                                            self.KijunThreshold,
                                                            self.resolution)
            algorithm.Debug("Added Symbol : " + str(security.Symbol))
        for security in changes.RemovedSecurities:
            algorithm.Debug("Removed Symbol : " + str(security.Symbol))
            data = self.symbolData.pop(security.Symbol, None)
            if data is not None:
                pass # Do any clean up here if required

class SymbolData():
    def __init__(self,
                security,
                algorithm,
                tenkan_period, 
                kijun_period, 
                senkouA_period,
                senkouB_period,
                chikou_period,
                chikou_threshold,
                kijun_threshold,
                resolution):
        
    
        self.Security = security
        self.ichimoku = IchimokuKinkoHyo(security.Symbol, tenkan_period, 
                                        kijun_period, senkouA_period, senkouB_period,
                                        chikou_period, chikou_period)
                                        
        self.thirtyMinuteConsolidator = TradeBarConsolidator(timedelta(minutes=30))
        #self.thirtyMinuteConsolidator.DataConsolidated += self.OnThirtyMinuteBarHandler
        algorithm.SubscriptionManager.AddConsolidator(security.Symbol, self.thirtyMinuteConsolidator)
        algorithm.RegisterIndicator(security.Symbol, self.ichimoku, self.thirtyMinuteConsolidator)
        
        self.algorithm = algorithm
        self.tenkansen = self.ichimoku.Tenkan
        self.kijunsen = self.ichimoku.Kijun
        self.senkouA = self.ichimoku.SenkouA
        self.senkouB = self.ichimoku.SenkouB
        self.chikou = self.ichimoku.Chikou
        self.low = self.Security.Low
        self.high = self.Security.High
        self.PreviousDirection = InsightDirection.Flat
        
    def OnThirtyMinuteBarHandler(self, sender, bar):
        pass
        #self.algorithm.Debug( "Updated 30 minute bar : " + str(bar) )
    
    def weak_bullish(self):
        tenkan_above_span_a_b = (self.tenkansen.Current.Value > self.senkouA.Current.Value) and (self.tenkansen.Current.Value > self.senkouB.Current.Value)
        kijun_above_span_a_b = (self.kijunsen.Current.Value > self.senkouA.Current.Value) and (self.kijunsen.Current.Value > self.senkouB.Current.Value)
        return (tenkan_above_span_a_b) and (kijun_above_span_a_b)
        
    def weak_bearish(self):
        tenkan_below_span_a_b = (self.tenkansen.Current.Value < self.senkouA.Current.Value) and (self.tenkansen.Current.Value < self.senkouB.Current.Value)
        kijun_below_span_a_b = (self.kijunsen.Current.Value < self.senkouA.Current.Value) and (self.kijunsen.Current.Value < self.senkouB.Current.Value)
        return (tenkan_below_span_a_b) and (kijun_below_span_a_b)
        
    def tenkansen_cross(self):
        retVal = (self.tenkansen.Current.Value > self.kijunsen.Current.Value)
        return retVal
        
    def kijunsen_cross(self):
        retVal = (self.tenkansen.Current.Value < self.kijunsen.Current.Value)
        return retVal
        
    def strong_bullish(self):
        return self.tenkansen_cross() and self.weak_bullish()
        
    def strong_bearish(self):
        return self.kijunsen_cross() and self.weak_bearish()
        
    def tenkansen_delta(self):
        return ((self.tenkansen-self.kijunsen)/self.kijunsen)*100
        
    def kijunsen_delta(self):
        return ((self.kijunsen-self.tenkansen)/self.tenkansen)*100
        
    def price_above_kijunsen(self):
        return self.low > self.kijunsen.Current.Value
    
    def price_below_kijunsen(self):
        return self.high > self.kijunsen.Current.Value
        
    def price_above_kumo(self):
        return self.low > self.senkouA.Current.Value and self.low > self.senkouB.Current.Value
        
    def price_below_kumo(self):
        return self.high < self.senkouA.Current.Value and self.high < self.senkouB.Current.Value
        
    def price_above_kijun_delta(self):
        return ((self.low - self.kijunsen.Current.Value)/self.kijunsen.Current.Value)*100
        
    def price_below_kijun_delta(self):
        return ((self.kijunsen.Current.Value-self.high)/self.high)*100
from Alphas.RsiAlphaModel import RsiAlphaModel

from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Risk.TrailingStopRiskManagementModel import TrailingStopRiskManagementModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from IchimokuAlphaModel import IchimokuAlphaModel

class CryptoGenie(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017, 1, 1)  # Set Start Date
        self.SetEndDate(2019, 12, 31)  # Set End Date
        self.SetCash(1000000)  # Set Strategy Cash
        self.SetWarmUp(78)
        # Symbol.Create("ETHUSD", SecurityType.Crypto, Market.Bitfinex)
        self.symbols = [ Symbol.Create("BTCUSD", SecurityType.Crypto, Market.Bitfinex) ]
        self.SetUniverseSelection( ManualUniverseSelectionModel(self.symbols) )
        self.UniverseSettings.Resolution = Resolution.Minute
        self.SetBrokerageModel(BrokerageName.Bitfinex)
        
        self.tenkan_period=9
        self.kijun_period=26 
        self.senkouA_period=26
        self.senkouB_period=52
        self.chikou_period=26
        self.chikou_threshold=0
        self.kijun_threshold=0
        self.resolution = Resolution.Minute
        
        self.Debug("Started Alpha Model")
        
        self.AddAlpha(IchimokuAlphaModel(self.tenkan_period, self.kijun_period, 
                                        self.senkouA_period, self.senkouB_period,
                                        self.chikou_period, self.chikou_threshold,
                                        self.kijun_threshold, self.resolution))
                                        
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        
        #self.SetRiskManagement(TrailingStopRiskManagementModel(0.1)) # 
        self.SetExecution(ImmediateExecutionModel())



    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''

        # if not self.Portfolio.Invested:
        #     self.SetHoldings("BTCUSD", 1)