Overall Statistics
Total Trades
317
Average Win
0%
Average Loss
0.00%
Compounding Annual Return
-72.498%
Drawdown
2.100%
Expectancy
-1
Net Profit
-2.100%
Sharpe Ratio
-11.242
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.887
Beta
0.003
Annual Standard Deviation
0.079
Annual Variance
0.006
Information Ratio
-11.416
Tracking Error
0.079
Treynor Ratio
-275.189
Total Fees
$317.00
# 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.Common")
AddReference("QuantConnect.Algorithm.Framework")

from QuantConnect import Resolution, Extensions
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from itertools import groupby
from datetime import datetime, timedelta
from pytz import utc
UTCMIN = datetime.min.replace(tzinfo=utc)

class VolatilityBasedPortfolioConstructionModel(PortfolioConstructionModel):
    '''Provides an implementation of IPortfolioConstructionModel that gives equal weighting to all securities.
    The target percent holdings of each security is 1/N where N is the number of securities.
    For insights of direction InsightDirection.Up, long targets are returned and
    for insights of direction InsightDirection.Down, short targets are returned.'''

    def __init__(self, resolution = Resolution.Daily):
        '''Initialize a new instance of EqualWeightingPortfolioConstructionModel
        Args:
            resolution: Rebalancing frequency'''
        self.insightCollection = InsightCollection()
        self.removedSymbols = []
        self.nextExpiryTime = UTCMIN
        self.rebalancingTime = UTCMIN
        self.rebalancingPeriod = Extensions.ToTimeSpan(resolution)

    def CreateTargets(self, algorithm, insights):
        '''Create portfolio targets from the specified insights
        Args:
            algorithm: The algorithm instance
            insights: The insights to create portoflio targets from
        Returns:
            An enumerable of portoflio targets to be sent to the execution model'''

        targets = []

        if (algorithm.UtcTime <= self.nextExpiryTime and
            algorithm.UtcTime <= self.rebalancingTime and
            len(insights) == 0 and
            self.removedSymbols is None):
            return targets

        self.insightCollection.AddRange(insights)

        # Create flatten target for each security that was removed from the universe
        if self.removedSymbols is not None:
            universeDeselectionTargets = [ PortfolioTarget(symbol, 0) for symbol in self.removedSymbols ]
            targets.extend(universeDeselectionTargets)
            self.removedSymbols = None

        # Get insight that haven't expired of each symbol that is still in the universe
        activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime)

        # Get the last generated active insight for each symbol
        lastActiveInsights = []
        for symbol, g in groupby(activeInsights, lambda x: x.Symbol):
            lastActiveInsights.append(sorted(g, key = lambda x: x.GeneratedTimeUtc)[-1])

        # give equal weighting to each security
        count = sum(x.Direction != InsightDirection.Flat for x in lastActiveInsights)
        percent = 0 if count == 0 else 1.0 / count

        errorSymbols = {}
        for insight in lastActiveInsights:
            target = PortfolioTarget.Percent(algorithm, insight.Symbol, insight.Direction * percent)
            if not target is None:
                targets.append(target)
            else:
                errorSymbols[insight.Symbol] = insight.Symbol

        # Get expired insights and create flatten targets for each symbol
        expiredInsights = self.insightCollection.RemoveExpiredInsights(algorithm.UtcTime)

        expiredTargets = []
        for symbol, f in groupby(expiredInsights, lambda x: x.Symbol):
            if not self.insightCollection.HasActiveInsights(symbol, algorithm.UtcTime) and not symbol in errorSymbols:
                expiredTargets.append(PortfolioTarget(symbol, 0))
                continue

        targets.extend(expiredTargets)

        self.nextExpiryTime = self.insightCollection.GetNextExpiryTime()
        if self.nextExpiryTime is None:
            self.nextExpiryTime = UTCMIN

        self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod

        return targets

    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'''

        # Get removed symbol and invalidate them in the insight collection
        self.removedSymbols = [x.Symbol for x in changes.RemovedSecurities]
        self.insightCollection.Clear(self.removedSymbols)                        
# 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.

import numpy as np
from scipy import stats  # using this for the reg slope

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

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


class SlopeBasedEquityMomentumAlphaModel(AlphaModel):
    '''Defines a custom alpha model that uses the average of two momentum slopes'''

    def __init__(self,
                 shortTermMomentumWindow = 60,
                 longTermMomentumWindow = 90,
                 minimumMomentum = 60,
                 indexAverageWindow = 100,
                 resolution = Resolution.Daily):
        ''' Initializes a new instance of the MacdAlphaModel class
        Args:
            shortTermMomentumWindow: The short term momentum window
            longTermMomentumWindow:  The long term momentum window
            minimumMomentum: The minimum momentum required for signal generation
            indexAverageWindow: The window over which the momentum of the reference index is average. If the value is >0 the index momentum is used as trend filter. 
            resolution: The resolution of the momentum calculation'''
        self.shortTermMomentumWindow = shortTermMomentumWindow
        self.longTermMomentumWindow = longTermMomentumWindow
        self.minimumMomentum = minimumMomentum
        self.indexAverageWindow = indexAverageWindow
        self.resolution = resolution
        self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.longTermMomentumWindow)
        self.symbolData = {}

        
    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'''
        algorithm.Debug("changed")
        for added in changes.AddedSecurities:
            self.symbolData[added.Symbol] = added
        for removed in changes.RemovedSecurities:
            data = self.symbolData.pop(removed.Symbol, None)
 
        
    def Slope(self,ts):
        '''
        Args: 
            Price time series.
        Returns: 
            Annualized exponential regression slope
        '''
        x = np.arange(len(ts))
        log_ts = np.log(ts)
        slope, intercept, r_value, p_value, std_err = stats.linregress(x, log_ts)
        annualized_slope = (np.power(np.exp(slope), 250) - 1) * 100
        return annualized_slope * (r_value ** 2)
    
    def Update(self, algorithm, data):
        ''' Determines an insight for each security based on two annualized slopes
        Args:
            algorithm: The algorithm instance
            data: The new data available
        Returns:
            The new insights generated'''
        algorithm.Debug("Update")
        
        insights = []
        
        # Check trend filter if enabled.
        if self.indexAverageWindow>0:
            index_history = algorithm.History("SPY", TimeSpan.FromDays(self.indexAverageWindow), self.resolution) 
            index_sma = index_history.mean()  
            current_index = index_history['close'][-1] 
            bull_market = current_index > index_sma
            if not bull_market:
                return insights
        
        for symbol,equity in self.symbolData.items():
            #algorithm.Debug(symbol)
            if equity.Price == 0:
                continue
            shortTermBars = algorithm.History(symbol, TimeSpan.FromDays(self.shortTermMomentumWindow), self.resolution)
            longTermBars = algorithm.History(symbol, TimeSpan.FromDays(self.longTermMomentumWindow), self.resolution)
            shortTermSlope = self.Slope(shortTermBars['close'])
            longTermSlope = self.Slope(longTermBars['close'])
            meanMomentum = (shortTermSlope+longTermSlope)/2
            if meanMomentum < self.minimumMomentum:
                continue
            insight = insights.append(Insight.Price(symbol, self.predictionInterval, InsightDirection.Up,  meanMomentum, None))
            
            
        return insights                        
from Alphas.EmaCrossAlphaModel import EmaCrossAlphaModel

from Execution.ImmediateExecutionModel import ImmediateExecutionModel

from Risk.NullRiskManagementModel import NullRiskManagementModel

from Selection.QC500UniverseSelectionModel import QC500UniverseSelectionModel

from SlopeBasedEquityMomentumAlphaModel import SlopeBasedEquityMomentumAlphaModel

from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel

class BasicTemplateFrameworkAlgorithm(QCAlgorithmFramework):

    def Initialize(self):

        # Set requested data resolution
        self.UniverseSettings.Resolution = Resolution.Daily
    
        self.SetStartDate(2019, 3, 1)   #Set Start Date
        self.SetEndDate(2019, 3, 6)    #Set End Date
        self.SetCash(100000)           #Set Strategy Cash

        # selection will run on mon/tues/thurs at 00:00/06:00/12:00/18:00
        self.SetUniverseSelection(QC500UniverseSelectionModel())
        
        self.SetAlpha(SlopeBasedEquityMomentumAlphaModel(indexAverageWindow=0))
        
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) 
        
        self.SetExecution(ImmediateExecutionModel())
        
        self.SetRiskManagement(NullRiskManagementModel())
        

        
    

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