Overall Statistics
# 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,
# See the License for the specific language governing permissions and
# limitations under the License.

from clr import AddReference

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*5,
                 slowPeriod = 26*5,
                 trigger = 0.0,
                 consolidationPeriod = 7,
                 resolution = Resolution.Daily):
        self.lookback = (slowPeriod * 3)
        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
            algorithm: The algorithm instance
            data: The new data available
            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
            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:
        #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, self.resolution)

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

        # True if the fast is above the slow, otherwise false.
        # This is used to prevent emitting the same signal repeatedly
        #self.FastIsOverSlow = False
        #self.abovePPOTrigger = False
    #def setPPOTrigger (self, value):
        #self.abovePPOTrigger = value

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

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

    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, resolution):
        #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):
        for tuple in history.itertuples():
            self.PPO.Update(tuple.Index, tuple.close)
from clr import AddReference

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 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', 'EEM']
        symbols = [ Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers ]
        # set algorithm framework models
        #self.SetAlpha(IchimokuAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(days = 10), 0.025, None))
        self.alpha = PPOAlphaModel()
        # will initialize a position immediately then on the given schedule
        self.alpha.rebalance_date = self.Time.date()
        self.rebalance_trading_day = 7
        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))