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

from System import *
from QuantConnect import *
from QuantConnect.Indicators import *
from QuantConnect.Algorithm.Framework.Alphas import *



class MacdMomentumAlphaModel(AlphaModel):
    '''Defines a custom alpha model that uses MACD crossovers. The MACD signal line
    is used to generate up/down insights if it's stronger than the bounce threshold.
    If the MACD signal is within the bounce threshold then a flat price insight is returned.'''

    def __init__(self,
                 fastPeriod = 12,
                 slowPeriod = 26,
                 signalPeriod = 9,
                 movingAverageType = MovingAverageType.Exponential,
                 resolution = Resolution.Minute):
        ''' Initializes a new instance of the MacdAlphaModel class
        Args:
            fastPeriod: The MACD fast period
            slowPeriod: The MACD slow period</param>
            signalPeriod: The smoothing period for the MACD signal
            movingAverageType: The type of moving average to use in the MACD'''
        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.01
        self.symbolData = {};
        
        resolutionString = Extensions.GetEnumString(resolution, Resolution)
        movingAverageTypeString = Extensions.GetEnumString(movingAverageType, MovingAverageType)
        self.Name = '{}({},{},{},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, signalPeriod, movingAverageTypeString, resolutionString)
        

    def Update(self, algorithm, data):
        ''' Determines an insight for each security based on it's current MACD signal
        Args:
            algorithm: The algorithm instance
            data: The new data available
        Returns:
            The new insights generated'''
        insights = []
        if algorithm.IsWarmingUp: return 
        
        for key, sd in self.symbolData.items():
            if sd.Security.Price == 0:
                continue
            algorithm.Debug("Inside Update For loop...")
            
            
            direction = InsightDirection.Flat
            
            holdings = algorithm.Portfolio[sd.Security.Symbol.Value].Quantity
            algorithm.Debug(sd.Security.Symbol.Value+" holdings ="+str(holdings))
            
            if(sd.MACDTrend_Previous == None): return
            normalized_signal = sd.macd.Signal.Current.Value / sd.Security.Price
            
            if normalized_signal > self.bounceThresholdPercent:
                direction = InsightDirection.Up
            elif normalized_signal < -self.bounceThresholdPercent:
                direction = InsightDirection.Down
                
            # ignore signal for same direction as previous signal
            if direction == sd.PreviousDirection:
                continue;
            
            insight = Insight.Price(sd.Security.Symbol, self.insightPeriod, direction)
            sd.PreviousDirection = insight.Direction
            insights.append(insight)
            
        return insights


    def OnSecuritiesChanged(self, algorithm, changes):
        '''Event fired each time the we add/remove securities from the data feed.
        This initializes the MACD 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'''
        for added in changes.AddedSecurities:
            self.symbolData[added.Symbol] = SymbolData(algorithm, added, self.fastPeriod, self.slowPeriod, self.signalPeriod, self.movingAverageType, self.resolution)
            
            data = self.symbolData.pop(added.Symbol, None)
            # Do we need the following two lines ?
            algorithm.SubscriptionManager.AddConsolidator(added.Symbol, data.Consolidator_15)   
            algorithm.SubscriptionManager.AddConsolidator(added.Symbol, data.Consolidator_240)
            
            #Creating the RollingWindow here and updating the 240 mins MACDTrend
            data.MACDWindow = RollingWindow[IndicatorDataPoint](3)
            data.MACDTrend.Updated += data.MacdUpdated
            
        for removed in changes.RemovedSecurities:
            data = self.symbolData.pop(removed.Symbol, None)
            if data is not None:
                # clean up our consolidator
                
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator_15);
                algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator_240);
                
                # How to Reset() ???
    
        
class SymbolData:
    '''
    Not sure which indicator and/or rolling window should be created here, or OnSecuritiesChanged
    Here, I am trying to translate what I saw in your C# sample into python.
    
    '''
    def __init__(self, algorithm, security, fastPeriod, slowPeriod, signalPeriod, movingAverageType, resolution):
        self.Security = security
        
    
        self.macd = MovingAverageConvergenceDivergence(fastPeriod, slowPeriod, signalPeriod, movingAverageType)
        self.MACDTrend = MovingAverageConvergenceDivergence(fastPeriod, slowPeriod, signalPeriod, movingAverageType)
        #self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution)
        self.Consolidator_15 = QuoteBarConsolidator(15)
        
        algorithm.RegisterIndicator(security.Symbol, self.macd, self.Consolidator_15)
        
        self.Consolidator_240 = QuoteBarConsolidator(240)
        algorithm.RegisterIndicator(security.Symbol, self.MACDTrend, self.Consolidator_240)
        self.MACDWindow =  None
        
        self.MACDTrend_Previous = None
        
        self.kamaFast = KaufmanAdaptiveMovingAverage(security.Symbol.Value,5)
        algorithm.RegisterIndicator(security.Symbol, self.kamaFast, self.Consolidator_15)
        self.kamaSlow =  KaufmanAdaptiveMovingAverage(security.Symbol.Value,30)
        algorithm.RegisterIndicator(security.Symbol, self.kamaSlow, self.Consolidator_15)

        self.adx = AverageDirectionalIndex(security.Symbol.Value,14)
        self.adxr = AverageDirectionalMovementIndexRating(security.Symbol.Value,14)
        algorithm.RegisterIndicator(security.Symbol, self.adx, self.Consolidator_15)
        algorithm.RegisterIndicator(security.Symbol, self.adxr, self.Consolidator_15)
        
        self.PreviousDirection = None
        
        
        ''' 
        Wait for our macd to fully initialize
        May not look right to have this check in this method here, but if I put it elsewhere, there will be object instance error. 
        i.e. seems macd is called before it is instantiated.
        '''
        if not (self.macd.IsReady and
                self.MACDTrend.IsReady and
                self.kamaFast.IsReady and
                self.kamaSlow.IsReady and
                self.MACDWindow.IsReady): return
        
        algorithm.Debug(self.macd.Current.Value)
        '''
        I need to wait at least 4 QuoteBars (15m) or 240 mins to get a 4 hour QuoteBars,so self.MACDWindow.IsReady check is essential
        '''
        self.MACDTrend_Previous = self.MACDWindow[1]
            
        algorithm.Debug(self.MACDTrend_Previous.Value)
        algorithm.Debug(self.MACDTrend)
        
            
        
    
    def MacdUpdated(self, sender, updated):
       # if not self._macdWindow.IsReady : return
        self.MACDWindow.Add(updated)                        
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 Alphas.ConstantAlphaModel import ConstantAlphaModel
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Risk.MaximumDrawdownPercentPerSecurity import MaximumDrawdownPercentPerSecurity
from datetime import timedelta
import numpy as np
from MACDMomentumAlphaModel import MacdMomentumAlphaModel

### <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 MACDMomentumFramework(QCAlgorithmFramework):
    '''Basic template framework algorithm uses framework components to define the algorithm.'''

    def Initialize(self):
        '''
        A function to define things to do at the start of the strategy
        Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
        '''
        # universe selection
        #self.failedSecurities = ["USDCAD"]
        #self.symbols = ["EURUSD","EURAUD","GBPUSD","AUDUSD"]
        #self.forex = self.AddForex(self.symbols[0], Resolution.Minute, Market.FXCM)
        
        self.params = {'start_yr':2016,
                       'start_mth':1,
                       'start_day':1,
                       'end_yr':2018,
                       'end_mth':6,
                       'end_day':1,
                       'principal':25000,
                       'warmup':100,
                       'max_weight_per_pos':0.02,
                       'leverage':200,
                       'tp_atr_factor':2.5,
                       'sl_atr_factor':3.0,
                       'main_timeframe':15,
                       'trend_timeframe':240,
                       'kama_fast_period':5,
                       'kama_slow_period':30,
                       'adx_period':14,
                       'adxr_period':14,
                       'atr_period':14,
                       'macd_fast_period':12,
                       'macd_slow_period':26,
                       'macd_signal_period':9,
                       'lookback_win':5
                        }
        
        
        self.SetStartDate(self.params['start_yr'], self.params['start_mth'], self.params['start_day'])     #Set Start Date
        self.SetEndDate(self.params['end_yr'], self.params['end_mth'], self.params['end_day'])             #Set End Date
        self.SetCash(self.params['principal'])                                                   #Set Strategy Cash
       
        # Set requested data resolution
        self.UniverseSettings.Resolution = Resolution.Minute
        self.SetBrokerageModel(BrokerageName.FxcmBrokerage)
        
    
        # 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.
        self.symbols = []
        #curr = Currencies.CurrencyPairs
        curr = ["EURUSD","GBPUSD","EURAUD","GBPNZD"]
        for i in range(len(curr)):
            self.Debug(curr[i])
            self.symbols.append(Symbol.Create(curr[i], SecurityType.Forex, Market.FXCM))
       
       

        # set algorithm framework models
        
        self.SetUniverseSelection(ManualUniverseSelectionModel(self.symbols))
        self.Debug("Before!!!")
        self.SetAlpha(MacdMomentumAlphaModel(fastPeriod = 12,
                 slowPeriod = 26,
                 signalPeriod = 9,
                 movingAverageType = MovingAverageType.Exponential,
                 resolution = Resolution.Minute))
        self.Debug("After...")
        #self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(minutes = 20), 0.025, None))
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        #self.SetExecution(ImmediateExecutionModel())
        #self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(0.01))

        self.Debug("numpy test >>> print numpy.pi: " + str(np.pi))

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