Overall Statistics
Total Trades
94
Average Win
0.61%
Average Loss
-2.47%
Compounding Annual Return
8.655%
Drawdown
29.700%
Expectancy
0.024
Net Profit
36.897%
Sharpe Ratio
0.751
Loss Rate
18%
Win Rate
82%
Profit-Loss Ratio
0.25
Alpha
0.101
Beta
-1.695
Annual Standard Deviation
0.097
Annual Variance
0.009
Information Ratio
0.582
Tracking Error
0.097
Treynor Ratio
-0.043
Total Fees
$216.71
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from clr import AddReference
AddReference("QuantConnect.Algorithm.Framework")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")

from QuantConnect import *
from QuantConnect.Indicators import *
from QuantConnect.Algorithm.Framework.Alphas import *
from datetime import timedelta
from enum import Enum

class PPOAlphaModel(AlphaModel):
    '''Uses PPO to create insights. 
    Using default settings, slow/fast is 12/26. Above/below -.5 will trigger a new insight.'''
    
    rebalance_date = None
    rebalance_complete = False

    def __init__(self,
                 fastPeriod = 12,
                 slowPeriod = 26,
                 trigger = 0.0,
                 consolidationPeriod = 7,
                 resolution = Resolution.Daily):
        self.lookback = (slowPeriod * consolidationPeriod)
        self.fastPeriod = fastPeriod
        self.slowPeriod = slowPeriod
        self.resolution = resolution
        self.consolidationPeriod = consolidationPeriod
        self.trigger = trigger
        self.predictionInterval = Extensions.ToTimeSpan(self.resolution)
        self.symbolDataBySymbol = {}

        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        self.Name = '{}({},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, resolutionString)

    def Update(self, algorithm, data):
        '''Updates this alpha model with the latest data from the algorithm.
        This is called each time the algorithm receives data for subscribed securities
        Args:
            algorithm: The algorithm instance
            data: The new data available
        Returns:
            The new insights generated'''
            
        
        #algorithm.Debug("Update Called on PPOAlphaModel")
        # generate insights when scheduled
        insights = []
        #and algorithm.Time.hour == 8 and algorithm.Time.minute == 59
        if self.rebalance_complete or not (algorithm.Time.date() == self.rebalance_date):
            return insights
        
        # only get here to build new insights once on the scheduled trading date
        self.rebalance_complete = True
        #algorithm.Debug("Now Generating Insights in PPOAlpha")
        for symbol, symbolData in self.symbolDataBySymbol.items():
            
            if symbolData.CanEmit:
                direction = InsightDirection.Flat
                magnitude = symbolData.Return
                #algorithm.Debug("got a positive CanEmit with symbol and magnitude: " + str(symbol)  + str(magnitude))
                if magnitude > self.trigger: direction = InsightDirection.Up
                if magnitude < self.trigger: direction = InsightDirection.Flat

                insights.append(Insight.Price(symbol, self.predictionInterval, direction))
                algorithm.Log(str(symbol) + ": " + str(magnitude) + ": " + str(direction))
                
        return insights


    def OnSecuritiesChanged(self, algorithm, changes):
        '''Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''
            
        #algorithm.Debug("starting OnSecuritiesChanged")
        # clean up data for removed securities
        for removed in changes.RemovedSecurities:
            symbolData = self.symbolDataBySymbol.pop(removed.Symbol, None)
            if symbolData is not None:
                symbolData.RemoveConsolidators(algorithm)
         
        #algorithm.Debug("in OnSecuritiesChanged past removed list")       
        # initialize data for added securities
        symbols = [ x.Symbol for x in changes.AddedSecurities ]
        #algorithm.Debug("before history retrieval")
        history = algorithm.History(symbols, self.lookback, self.resolution)
        #algorithm.Debug("after history retrieval")
        if history.empty: return

        tickers = history.index.levels[0]
        for ticker in tickers:
            symbol = SymbolCache.GetSymbol(ticker)

            if symbol not in self.symbolDataBySymbol:
                symbolData = SymbolData(symbol, self.fastPeriod, self.slowPeriod, self.consolidationPeriod)
                self.symbolDataBySymbol[symbol] = symbolData
                symbolData.RegisterIndicators(algorithm)
                symbolData.WarmUpIndicators(history.loc[ticker])
        

class SymbolData:
    '''Contains data specific to a symbol required by this model'''

    
    def __init__(self, symbol, fastPeriod, slowPeriod, consolidationPeriod):
        self.Symbol = symbol
        self.Fast = fastPeriod
        self.Slow = slowPeriod
        self.PPO = PercentagePriceOscillator(fastPeriod, slowPeriod, MovingAverageType.Exponential)
        self.Consolidator = TradeBarConsolidator(TimeSpan.FromDays(consolidationPeriod))
        #self.Consolidator = None
        self.Previous = None

    @property
    def belowPPOTrigger(self):
        return (not self.abovePPOTrigger)
        
    @property
    def CanEmit(self):
        if self.Previous == self.PPO.Samples:
            return False

        self.Previous = self.PPO.Samples
        return self.PPO.IsReady

    @property
    def Return(self):
        return float(self.PPO.Current.Value)
        
    #def OnWeeklyData(self, sender, bar):
        #self.PPO.Update(bar.EndTime, bar.Close)
        
    def RegisterIndicators(self, algorithm):
        #self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution)
        algorithm.RegisterIndicator(self.Symbol, self.PPO, self.Consolidator)

    def RemoveConsolidators(self, algorithm):
        if self.Consolidator is not None:
            algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.Consolidator)

    def WarmUpIndicators(self, history):
        #index = 0
        #for tuple in history.itertuples():
        for index, tuple in history.iterrows():
            #index = index + 1
            #if index % consolidationPeriod == 0: 
                #self.PPO.Update(tuple.Index, tuple.close)
            tradeBar = TradeBar()
            tradeBar.Close = tuple['close']
            tradeBar.Open = tuple['open']
            tradeBar.High = tuple['high']
            tradeBar.Low = tuple['low']
            tradeBar.Volume = tuple['volume']
            tradeBar.Time = index
            self.Consolidator.Update(tradeBar)
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Orders import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Selection import *
from PPOAlphaModel import PPOAlphaModel
#from QuantConnect.Algorithm.Framework.Alphas.ConstantAlphaModel import ConstantAlphaModel
from Alphas.RsiAlphaModel import RsiAlphaModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from QuantConnect.Algorithm.Framework.Execution import ImmediateExecutionModel
from QuantConnect.Algorithm.Framework.Execution import VolumeWeightedAveragePriceExecutionModel
from QuantConnect.Algorithm.Framework.Risk import NullRiskManagementModel
from datetime import datetime, timedelta

import numpy as np


### <summary>
### Basic template framework algorithm uses framework components to define the algorithm.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="trading and orders" />
class AssetClassRotationFrameworkAlgorithm(QCAlgorithmFramework):
    '''Basic template framework algorithm uses framework components to define the algorithm.'''

    alpha = None
    
    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.'''

        # Set requested data resolution
        self.UniverseSettings.Resolution = Resolution.Daily
        self.UniverseSettings.Leverage = 1.0
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)

        self.SetStartDate(2014,10,7)   #Set Start Date
        self.SetEndDate(2018,7,18)    #Set End Date
        self.SetCash(100000)           #Set Strategy Cash

        # Find more symbols here: http://quantconnect.com/data
        # Forex, CFD, Equities Resolutions: Tick, Second, Minute, Hour, Daily.
        # Futures Resolution: Tick, Second, Minute
        # Options Resolution: Minute Only.
        #tickers = ['IWM', 'QQQ','SCHH',  'USO', 'VDE', 'XBI', 'AAXJ', 'DBA', 'DBB', 'DBC', 'DOG', 'EEM', 'EFA', 'EWH', 'EWJ', 'EWZ', 'EZU', 'FXI', 'GDX', 'GLD', 'HYG', 'IEF', 'IEFA', 'LQD', 'SH', 'SLV', 'TAN', 'TIP', 'TLT', 'UUP', 'VAW', 'VEU', 'VNQ', 'XLE', 'XLK', 'XLY', 'XLB', 'XLF', 'XLI', 'XLP', 'XLU', 'XLV']
        tickers = ['IWM', 'XLK', 'DOG', 'USO']
        symbols = [ Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers ]
        
        
        # set algorithm framework models
        self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
        #self.SetAlpha(IchimokuAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(days = 10), 0.025, None))
        self.alpha = PPOAlphaModel()
        self.SetAlpha(self.alpha)
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        self.SetExecution(ImmediateExecutionModel())
        self.SetRiskManagement(NullRiskManagementModel())
        
        # will initialize a position immediately then on the given schedule
        self.alpha.rebalance_date = self.Time.date()
        
        self.rebalance_trading_day = 7
        self.AddEquity('IWM')
        self.Schedule.On(self.DateRules.MonthStart('IWM'), self.TimeRules.AfterMarketOpen('IWM', 1), Action(self.ScheduleRebalance))
       
        
    def ScheduleRebalance(self):
        month_last_day = DateTime(self.Time.year, self.Time.month, DateTime.DaysInMonth(self.Time.year, self.Time.month))
        trading_days = self.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, self.Time, month_last_day)

        #get the correct trading_day
        day_count = 0
        for x in trading_days:
            day_count = day_count + 1
            if (day_count == self.rebalance_trading_day):
                self.alpha.rebalance_date = x.Date.date()
                self.alpha.rebalance_complete = False

    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status == OrderStatus.Filled:
            #self.Debug("Purchased Stock: {0}, {1}".format(orderEvent.Symbol, orderEvent.FillQuantity))
            return