| Overall Statistics |
|
Total Trades 72 Average Win 0.27% Average Loss -0.28% Compounding Annual Return 0.509% Drawdown 5.400% Expectancy 0.041 Net Profit 0.349% Sharpe Ratio 0.123 Probabilistic Sharpe Ratio 21.071% Loss Rate 47% Win Rate 53% Profit-Loss Ratio 0.97 Alpha 0.006 Beta -0.005 Annual Standard Deviation 0.04 Annual Variance 0.002 Information Ratio -0.454 Tracking Error 0.358 Treynor Ratio -0.923 Total Fees $0.00 |
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 *
from QuantConnect.Data.Consolidators import *
from datetime import timedelta
class MACDForexAlphaModel(AlphaModel):
def __init__(self,
fastPeriod = 12,
slowPeriod = 26,
signalPeriod = 9,
trendPeriod = 200,
movingAverageType = MovingAverageType.Exponential,
resolution = Resolution.Minute,
difference = 0):
self.fastPeriod = fastPeriod
self.slowPeriod = slowPeriod
self.signalPeriod = signalPeriod
self.trendPeriod = trendPeriod
self.movingAverageType = movingAverageType
self.resolution = resolution
self.insightPeriod = timedelta(days=10)
self.bounceThresholdPercent = 0.000
self.differenceThreshold = difference
self.symbolData = {}
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'''
x = 1
insights = []
for key, sd in self.symbolData.items():
if sd.Security.Price == 0:
continue
if sd.fridayInsight:
insights.append(sd.fridayInsight)
sd.fridayInsight = None
if data.Bars[key].Time.hour == 16 and data.Bars[key].Time.minute == 59:
algorithm.Debug(algorithm.Time)
sd.MACD.Update(data.Time, data.Bars[key].Close)
sd.SMA.Update(data.Time, data.Bars[key].Close)
sd.Values.append((data.Time, data.Bars[key].Close))
else:
continue
if sd.MACD.Samples < 26:# or not sd.Trend.IsReady:
continue
direction = InsightDirection.Flat
if algorithm.Time.month == 4 and algorithm.Time.day == 13:
x = 1
difference = sd.MACD.Current.Value - sd.MACD.Signal.Current.Value
if difference * (1 + self.bounceThresholdPercent) > 0:# and sd.Security.Price > sd.Trend.Current.Value:# and sd.MACD.Current.Value < (0 + self.differenceThreshold):
direction = InsightDirection.Up
elif difference * (1 - self.bounceThresholdPercent) < 0:# and sd.Security.Price < sd.Trend.Current.Value:# and sd.MACD.Current.Value > (0 + self.differenceThreshold):
direction = InsightDirection.Down
if not algorithm.Portfolio[key].Invested and sd.PreviousDirection[1] and direction == sd.PreviousDirection[1]:
continue
if sd.PreviousDirection[0] and direction == sd.PreviousDirection[0]:
if abs(difference) - abs(sd.Difference) < 0:
sd.PreviousDirection[1] = direction
direction = InsightDirection.Flat
sd.Difference = 0
else:
sd.Difference = difference
continue
else:
sd.Difference = difference
sd.PreviousDirection[0] = direction
insight = Insight.Price(sd.Security.Symbol, self.insightPeriod, direction)
if algorithm.Time.weekday() == 4:
sd.fridayInsight = insight
continue
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.trendPeriod, self.movingAverageType, self.resolution)
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)
class SymbolData:
def __init__(self, algorithm, security, fastPeriod, slowPeriod, signalPeriod, trendPeriod, movingAverageType, resolution):
self.Security = security
self.MACD = MovingAverageConvergenceDivergence(fastPeriod, slowPeriod, signalPeriod, movingAverageType)
self.SMA = SimpleMovingAverage(slowPeriod)
# self.Trend = ExponentialMovingAverage(trendPeriod)
# self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, Resolution.Daily)
#resolveconsolidator vs addconsolidator
#self.Consolidator = QuoteBarConsolidator(timedelta(days=1))
# Register consolidator to get automatically updated with minute data
#algorithm.SubscriptionManager.AddConsolidator(self.Security.Symbol, self.Consolidator)
# algorithm.RegisterIndicator(security.Symbol, self.MACD, timedelta(1))
# algorithm.RegisterIndicator(security.Symbol, self.Trend, self.Consolidator)
self.fridayInsight = None
self.PreviousDirection = [None, None]
self.Difference = 0
self.Values = []
returnfrom Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from G10CurrencySelectionModel import G10CurrencySelectionModel
from MACDForexModel import MACDForexAlphaModel
from RiskManagementModule import ModifiedTrailingStopRiskManagementModel
import datetime
class MACDTrendFollower(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 12, 1)# - timedelta(200)) # Set Start Date
# self.SetEndDate(2020, 5, 20)
self.SetCash(100000) # Set Strategy Cash
#self.AddEquity("SPY", Resolution.Minute)
self.SetExecution(ImmediateExecutionModel())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.AddAlpha(MACDForexAlphaModel())
# self.SetRiskManagement(ModifiedTrailingStopRiskManagementModel(0.02, .04))
self.AddUniverseSelection( G10CurrencySelectionModel())
self.SetBrokerageModel(BrokerageName.OandaBrokerage, AccountType.Margin)
self.UniverseSettings.Leverage = 1
self.UniverseSettings.Resolution = Resolution.Minute
#self.SetWarmUp(timedelta(200))
return
self.pricePlot = Chart('Price')
self.macdPlot = Chart('MACD')
self.pricePlot.AddSeries(Series('Price', SeriesType.Line, 0))
self.macdPlot.AddSeries(Series('Difference', SeriesType.Line, 0))from QuantConnect import *
from Selection.ManualUniverseSelectionModel import ManualUniverseSelectionModel
class G10CurrencySelectionModel(ManualUniverseSelectionModel):
def __init__(self):
super().__init__([Symbol.Create(x, SecurityType.Forex, Market.Oanda) for x in [ "EURJPY"]])
#super().__init__([Symbol.Create(x, SecurityType.Equity, Market.USA) for x in [ "AMD"]])
# super().__init__([Symbol.Create(x, SecurityType.Forex, Market.Oanda) for x in [ "EURUSD", "GBPUSD", "USDJPY", "AUDUSD", "NZDUSD","USDCAD", "USDCHF", "USDNOK", "USDSEK"]])from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTarget
from QuantConnect.Algorithm.Framework.Risk import RiskManagementModel
class ModifiedTrailingStopRiskManagementModel(RiskManagementModel):
'''Provides an implementation of IRiskManagementModel that limits the maximum possible loss
measured from the highest unrealized profit'''
def __init__(self, maximumDrawdownPercent = 0.01, takeProfitPercent = 0.01):
'''Initializes a new instance of the TrailingStopRiskManagementModel class
Args:
maximumDrawdownPercent: The maximum percentage drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown'''
self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)
self.takeProfitPercent = takeProfitPercent
self.trailingHighs = dict()
def ManageRisk(self, algorithm, targets):
'''Manages the algorithm's risk at each time step
Args:
algorithm: The algorithm instance
targets: The current portfolio targets to be assessed for risk'''
riskAdjustedTargets = list()
for kvp in algorithm.Portfolio:
symbol = kvp.Key
security = kvp.Value
# Remove if not invested
if not security.Invested:
self.trailingHighs.pop(symbol, None)
continue
# Add newly invested securities
if symbol not in self.trailingHighs:
self.trailingHighs[symbol] = security.HoldingsCost # Set to average holding cost
continue
if security.UnrealizedProfitPercent >= self.takeProfitPercent:
algorithm.Debug(f"{algorithm.Time}: 'take profit'")
riskAdjustedTargets.append(PortfolioTarget(symbol, 0))
continue
bidPrice = algorithm.Securities[symbol].BidPrice
askPrice = algorithm.Securities[symbol].AskPrice
if security.IsLong:
# Check for new highs and update - set to tradebar high
algorithm.Debug(f"{algorithm.Time}: {symbol}: 'long'")
if self.trailingHighs[symbol] < askPrice:
self.trailingHighs[symbol] = askPrice
continue
# Check for securities past the drawdown limit
securityHigh = self.trailingHighs[symbol]
drawdown = (bidPrice / securityHigh) - 1
if security.IsShort:
# Check for new lows and update - set to tradebar Low
algorithm.Debug(f"{algorithm.Time}: {symbol}: 'short'")
if self.trailingHighs[symbol] > bidPrice:
self.trailingHighs[symbol] = bidPrice
continue
# Check for securities past the drawdown limit
securityHigh = self.trailingHighs[symbol]
drawdown = (securityHigh / askPrice) - 1
if drawdown < self.maximumDrawdownPercent:
# liquidate
algorithm.Debug(f"{algorithm.Time}: loss liquidate")
riskAdjustedTargets.append(PortfolioTarget(symbol, 0))
return riskAdjustedTargets