| Overall Statistics |
|
Total Trades 9454 Average Win 0.02% Average Loss -0.07% Compounding Annual Return -31.288% Drawdown 23.200% Expectancy -0.075 Net Profit -17.040% Sharpe Ratio -0.953 Probabilistic Sharpe Ratio 3.937% Loss Rate 29% Win Rate 71% Profit-Loss Ratio 0.29 Alpha -0.148 Beta 0.691 Annual Standard Deviation 0.213 Annual Variance 0.045 Information Ratio -0.634 Tracking Error 0.194 Treynor Ratio -0.294 Total Fees $11099.67 Estimated Strategy Capacity $710000.00 Lowest Capacity Asset SOXL UKTSIYPJHFMT |
# 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 AlgorithmImports import *
class EmaCrossMagnitudeAlphaModel(AlphaModel):
'''Alpha model that uses an EMA cross to create insights'''
def __init__(self,
fastPeriod = 12,
slowPeriod = 26,
resolution = Resolution.Daily):
'''Initializes a new instance of the EmaCrossAlphaModel class
Args:
fastPeriod: The fast EMA period
slowPeriod: The slow EMA period'''
self.fastPeriod = fastPeriod
self.slowPeriod = slowPeriod
self.resolution = resolution
self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(resolution), fastPeriod)
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'''
insights = []
for symbol, symbolData in self.symbolDataBySymbol.items():
price = symbolData.algorithm.Securities[symbol].Close
magnitude = 100.0 * price * (symbolData.Fast.Current.Value - symbolData.Slow.Current.Value) / (symbolData.Fast.Current.Value + symbolData.Slow.Current.Value)
direction = InsightDirection.Flat
if symbolData.FastIsOverSlow:
if symbolData.Slow > symbolData.Fast:
direction = InsightDirection.Down
magnitude = -magnitude
elif symbolData.SlowIsOverFast:
if symbolData.Fast > symbolData.Slow:
direct = InsightDirection.Up
else:
direction = InsightDirection.Flat
insight = Insight.Price(symbol, self.predictionInterval, direction, magnitude=magnitude)
symbolData.FastIsOverSlow = symbolData.Fast > symbolData.Slow
insights.append(insight)
algorithm.Debug("EMA INSIGHT LENGTH " + str(len(insights)))
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'''
# 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)
# initialize data for added securities
symbols = [ x.Symbol for x in changes.AddedSecurities ]
history = algorithm.History(symbols, self.resolution)
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, algorithm, self.slowPeriod, self.fastPeriod, self.resolution)
self.symbolDataBySymbol[symbol] = symbolData
algorithm.Debug("EMA LENGTH")
algorithm.Debug(str(len(self.symbolDataBySymbol)))
class SymbolData:
'''Contains data specific to a symbol required by this model'''
def __init__(self, symbol, algorithm, slowPeriod, fastPeriod, resolution):
self.Symbol = symbol
self.algorithm = algorithm
self.slowPeriod = slowPeriod
self.fastPeriod = fastPeriod
self.resolution = resolution
self.Slow = algorithm.EMA(self.Symbol, self.slowPeriod, self.resolution)
self.Fast = algorithm.EMA(self.Symbol, self.fastPeriod, self.resolution)
# True if the fast is above the slow, otherwise false.
# This is used to prevent emitting the same signal repeatedly
self.FastIsOverSlow = False
@property
def SlowIsOverFast(self):
return not self.FastIsOverSlow
# 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 AlgorithmImports import *
class MacdMagnitudeAlphaModel(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.Daily):
''' 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.symbolDataBySymbol = {}
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 = []
for symbol, symbolData in self.symbolDataBySymbol.items():
close = symbolData.algorithm.Securities[symbol].Close
direction = InsightDirection.Flat
normalized_signal = symbolData.MACD.Signal.Current.Value / close
magnitude = (abs(symbolData.MACD.Histogram.Current.Value * symbolData.MACD.Signal.Current.Value))**0.5 / close**2
if normalized_signal > self.bounceThresholdPercent:
direction = InsightDirection.Up
elif normalized_signal < -self.bounceThresholdPercent:
direction = InsightDirection.Down
magnitude *= -1.0 #Placeholder magnitudes
# ignore signal for same direction as previous signal
if direction == symbolData.PreviousDirection:
magnitude/= 10.0
insight = Insight.Price(symbol, self.insightPeriod, direction, magnitude=magnitude) #Placeholder magnitudes
symbolData.PreviousDirection = insight.Direction
insights.append(insight)
algorithm.Debug("MACD INSIGHT LENGTH " + str(len(insights)))
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 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)
# clean up data for removed securities
for removed in changes.RemovedSecurities:
symbolData = self.symbolDataBySymbol.pop(removed.Symbol, None)
# initialize data for added securities
symbols = [ x.Symbol for x in changes.AddedSecurities ]
history = algorithm.History(symbols,self.resolution)
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(algorithm, symbol, self.fastPeriod, self.slowPeriod, self.signalPeriod, self.movingAverageType, self.resolution)
self.symbolDataBySymbol[symbol] = symbolData
algorithm.Debug("MACD LENGTH")
algorithm.Debug(str(len(self.symbolDataBySymbol)))
class SymbolData:
def __init__(self, algorithm, symbol, fastPeriod, slowPeriod, signalPeriod, movingAverageType, resolution):
self.Symbol = symbol
self.MACD = MovingAverageConvergenceDivergence(fastPeriod, slowPeriod, signalPeriod, movingAverageType)
self.algorithm = algorithm
self.Consolidator = algorithm.ResolveConsolidator(symbol, resolution)
algorithm.RegisterIndicator(symbol, self.MACD, self.Consolidator)
algorithm.WarmUpIndicator(symbol, self.MACD, resolution)
self.PreviousDirection = None
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.BlackLittermanOptimizationPortfolioConstructionModel import BlackLittermanOptimizationPortfolioConstructionModel
from Risk.MaximumDrawdownPercentPerSecurity import MaximumDrawdownPercentPerSecurity
from MacdMagnitudeAlphaModel import MacdMagnitudeAlphaModel
from EmaCrossMagnitudeAlphaModel import EmaCrossMagnitudeAlphaModel
class BlackLittermanDebugging(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 9, 7) # Set Start Date
self.SetCash(100000) # Set Strategy Cash
self.SetBrokerageModel(BrokerageName.AlphaStreams)
self.AddAlpha(EmaCrossMagnitudeAlphaModel(50, 200, Resolution.Minute))
self.AddAlpha(MacdMagnitudeAlphaModel(12, 26, 9, MovingAverageType.Simple, Resolution.Minute))
self.SetExecution(ImmediateExecutionModel())
self.SetPortfolioConstruction(BlackLittermanOptimizationPortfolioConstructionModel())
self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(0.01))
self.UniverseSettings.Resolution = Resolution.Minute
symbols = [
Symbol.Create("SPY", SecurityType.Equity, Market.USA),
Symbol.Create("AAPL", SecurityType.Equity, Market.USA),
Symbol.Create("UVXY", SecurityType.Equity, Market.USA),
Symbol.Create("SOXL", SecurityType.Equity, Market.USA),
Symbol.Create("FB", SecurityType.Equity, Market.USA)
]
self.AddUniverseSelection(ManualUniverseSelectionModel(symbols))
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
'''