| Overall Statistics |
|
Total Trades 841 Average Win 0.08% Average Loss -0.03% Compounding Annual Return 1015.850% Drawdown 5.700% Expectancy 0.536 Net Profit 8.971% Sharpe Ratio 22.534 Probabilistic Sharpe Ratio 73.460% Loss Rate 59% Win Rate 41% Profit-Loss Ratio 2.75 Alpha 0 Beta 0 Annual Standard Deviation 0.507 Annual Variance 0.257 Information Ratio 22.534 Tracking Error 0.507 Treynor Ratio 0 Total Fees $904.24 Estimated Strategy Capacity $15000000.00 |
from clr import AddReference
AddReference("System")
AddReference("QuantConnect")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
from System import *
from System.Drawing import Color
from QuantConnect import *
from QuantConnect.Parameters import *
from QuantConnect.Benchmarks import *
from QuantConnect.Brokerages import *
from QuantConnect.Util import *
from QuantConnect.Interfaces import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Indicators import *
from QuantConnect.Data import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Data.Custom import *
from QuantConnect.Data.Fundamental import *
from QuantConnect.Data.Market import *
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Notifications import *
from QuantConnect.Orders import *
from QuantConnect.Orders.Fees import *
from QuantConnect.Orders.Fills import *
from QuantConnect.Orders.Slippage import *
from QuantConnect.Scheduling import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Equity import *
from QuantConnect.Securities.Forex import *
from QuantConnect.Securities.Interfaces import *
from datetime import date, datetime, timedelta
from QuantConnect.Python import *
from QuantConnect.Storage import *
QCAlgorithmFramework = QCAlgorithm
QCAlgorithmFrameworkBridge = QCAlgorithm
import decimal as d
import numpy as np
import pandas as pd
from io import StringIO
import random
class SymbolData:
"""
Class to hold Data for specific Tickers.
Currently holding:
- SPY - 50 dEMA
"""
def __init__(self, algorithm, symbol,
followerEMAPeriod = 50,
resolution = Resolution.Daily,
field = Field.Close):
self.Symbol = symbol
self.followerEMA = None
self.followerEmaWindow = None
self.ema = ExponentialMovingAverage(followerEMAPeriod)
self.consolidator = None
def regIndicator(self, algorithm, resolution):
self.consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution)
algorithm.RegisterIndicator(self.Symbol, self.ema, self.consolidator)
def removeConsolidators(self, algorithm):
if self.consolidator is not None:
algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.consolidator)
def warmup(self, history):
for tuple in history.itertuples():
self.ema.Update(tuple.Index, tuple.close)
class FollowerData:
"""
Class to hold Data for specific Tickers.
Currently holding:
- SPY - 50 dEMA
"""
def __init__(self, algorithm, security,
followerEMAPeriod = 50,
resolution = Resolution.Daily,
field = Field.Close):
self.Security = security
self.Symbol = security.Symbol
self.followerEMA = None
self.followerEmaWindow = None
def FollowerEmaUpdated(self, sender, updated):
self.followerEmaWindow.Add(updated)
class CAlphaModel(AlphaModel):
'''
Custom Alpha Model.
'''
def __init__(self, slowPeriod = 50,
lookup = 490,
resolution = Resolution.Daily,
field = Field.Close):
self.lookup = lookup
self.slowPeriod = slowPeriod
self.resolution = resolution
self.field = field
self.insightPeriod = timedelta(minutes=10)
# ETF watchers
self.followers = {'XLK' : True, 'XLC' : True, 'XLV' : True, 'XLY' : True,
'XLI' : True, 'XLF' : True, 'XLRE' : True, 'XLE' : True,
'XLP' : True, 'XLU' : True, 'XLB' : True, 'SPY' : True,
'IYZ' : True, 'ITB' : True, 'CARZ' : True, 'BJK' : True,
'ITA' : True, 'SMH' : True, 'KCE' : True, 'KIE' : True,
'KRE' : True, 'KBE' : True, 'XHS' : True, 'XBI' : True,
'XHE' : True, 'XPH' : True, 'XES' : True, 'XME' : True,
'WOOD' : True, 'ICLN' : True, 'XRT' : True, 'ONLN' : True,
'GLD' : True, 'SLX' : True, 'MJ' : True, 'CEX' : True,
'XWEB' : True, 'IGV' : True, 'JETS' : True, 'IYT' : True
}
self.followerData = {}
self.symbolData = {}
resolutionString = Extensions.GetEnumString(resolution, Resolution)
self.Name = '{}({},{})'.format(self.__class__.__name__, slowPeriod, resolutionString)
def Update(self, algorithm, data):
insights = []
key = 'SPY'
spyData = self.followerData[key]
if not (data.ContainsKey(spyData.Symbol) and data.Bars.ContainsKey(spyData.Symbol)):
return insights
spyTime = data[spyData.Symbol].Time
spyClose = data[spyData.Symbol].Close
if spyClose < spyData.followerEMA.Current.Value:
algorithm.Debug(f"- SPY < 50dEMA - {spyTime} close:{spyClose} 50dEMA method#1:{spyData.followerEMA.Current.Value} 50dEMA RW:{spyData.followerEmaWindow[0].Value} 50dEMA method#2:{self.symbolData[key].ema.Current.Value}")
self.followers['SPY'] = False
elif spyClose > spyData.followerEMA.Current.Value:
algorithm.Debug(f"- SPY > 50dEMA - {spyTime} close:{spyClose} 50dEMA method#1:{spyData.followerEMA.Current.Value} 50dEMA_window:{spyData.followerEmaWindow[0].Value} 50dEMA method#2:{self.symbolData[key].ema.Current.Value}")
self.followers['SPY'] = True
for key, follower in self.followerData.items():
if follower.Symbol.Value not in self.followers:
if not (data.ContainsKey(follower.Symbol) and data.Bars.ContainsKey(follower.Symbol)):
continue
if not self.followers['SPY']:
# self.Liquidate(key.Value)
insight = Insight.Price(follower.Symbol, self.insightPeriod, InsightDirection.Flat)
insights.append(insight)
elif self.followers['SPY']:
# self.SetHoldings(key.Value, 0.1)
insight = Insight.Price(follower.Symbol, self.insightPeriod, InsightDirection.Up)
insights.append(insight)
return insights
def OnSecuritiesChanged(self, algorithm, changes):
'''Event fired each time the 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'''
# method 1
symbols = [x.Symbol for x in changes.AddedSecurities]
his = algorithm.History(symbols, self.lookup)
if his.empty:
return
tickers = his.index.levels[0]
for ticker in tickers:
symbol = SymbolCache.GetSymbol(ticker)
if symbol.Value in self.followers and symbol.Value not in self.symbolData:
sd = SymbolData(algorithm, symbol, self.slowPeriod, self.resolution)
self.symbolData[symbol.Value] = sd
sd.regIndicator(algorithm, self.resolution)
sd.warmup(his.loc[ticker])
# method 2
for added in changes.AddedSecurities:
if not added.Symbol in self.followerData:
follower = FollowerData(algorithm, added, self.slowPeriod, self.resolution)
follower.followerEMA = algorithm.EMA(added.Symbol, self.slowPeriod, self.resolution, self.field)
follower.followerEMA.Updated += follower.FollowerEmaUpdated
follower.followerEmaWindow = RollingWindow[IndicatorDataPoint](self.slowPeriod)
# warmup our indicators by pushing history through the indicators
history = algorithm.History(added.Symbol, self.lookup, self.resolution)
if history.empty:
continue
if 'close' in history:
history = history.close.unstack(0).squeeze()
for time, value in history.iteritems():
follower.followerEMA.Update(time, value)
self.followerData[added.Symbol.Value] = follower
continue
for removed in changes.RemovedSecurities:
passfrom clr import AddReference
AddReference("System")
AddReference("QuantConnect")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
from System import *
from System.Drawing import Color
from QuantConnect import *
from QuantConnect.Parameters import *
from QuantConnect.Benchmarks import *
from QuantConnect.Brokerages import *
from QuantConnect.Util import *
from QuantConnect.Interfaces import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Indicators import *
from QuantConnect.Data import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Data.Custom import *
from QuantConnect.Data.Fundamental import *
from QuantConnect.Data.Market import *
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Notifications import *
from QuantConnect.Orders import *
from QuantConnect.Orders.Fees import *
from QuantConnect.Orders.Fills import *
from QuantConnect.Orders.Slippage import *
from QuantConnect.Scheduling import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Equity import *
from QuantConnect.Securities.Forex import *
from QuantConnect.Securities.Interfaces import *
from datetime import date, datetime, timedelta
import pandas as pd
from io import StringIO
import numpy as np
import random
from QuantConnect.Python import *
from QuantConnect.Storage import *
QCAlgorithmFramework = QCAlgorithm
QCAlgorithmFrameworkBridge = QCAlgorithm
from CUniverse import EmaUniverseSelectionModel
from Alpha import CAlphaModel
class GeekyOrangeSheep(QCAlgorithmFramework):
def Initialize(self):
self.SetStartDate(2021, 1, 20) # Set Start Date
self.SetEndDate(2021, 2, 1)
# self.SetEndDate(datetime.now() - timedelta(1))
self.SetCash(100000)
# Adjust the cash buffer from the default 2.5% to 5%
self.Settings.FreePortfolioValuePercentage = 0.05
# Rebalance when the insights state changes/added/expire.
self.Settings.RebalancePortfolioOnInsightChanges = False
# Rebalance when new assets are added to the universe.
self.Settings.RebalancePortfolioOnSecurityChanges = False
#Brokerage model and account type:
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
# Adjust the market fill-timeout to 30 seconds.
self.Transactions.MarketOrderFillTimeout = timedelta(minutes=30)
self.UniverseSettings.Resolution = Resolution.Daily
self.UniverseSettings.Leverage = 1
self.SetUniverseSelection(EmaUniverseSelectionModel())
self.SetAlpha(CAlphaModel())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.SetExecution(ImmediateExecutionModel())
self.SetRiskManagement(NullRiskManagementModel())
# set Security to SplitAdjusted price
self.SetSecurityInitializer(self.CustomSecurityInitializer)
def CustomSecurityInitializer(self, security):
'''
Initialize the security with SplitAdjusted prices
mimic using a cash account in backtesting by setting security leverage to 1.
'''
security.SetDataNormalizationMode(DataNormalizationMode.SplitAdjusted)
security.SetLeverage(1)# 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("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Algorithm.Framework")
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Indicators import ExponentialMovingAverage
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
import random
import numpy as np
class SymbolData:
"""
Holds every symbol's information and indicators.
"""
def __init__(self, symbol, algorithm,
volPeriod = 30,
rocPeriod = 9,
volMax = 1000000):
self.Symbol = symbol
self.Volume = 0
self.Price = 0
self.algo = algorithm
self.avgVolPeriod = volPeriod
self.avgVolMax = volMax
self.sma = SimpleMovingAverage(self.avgVolPeriod)
self.rocp = RateOfChangePercent(rocPeriod + 1)
self.rocW = RollingWindow[float](rocPeriod + 2)
self.rocV = 0
self.isGood = False
def update(self, time, volume, price):
self.Volume = volume
self.Price = price
self.sma.Update(time, volume)
self.rocp.Update(time, price)
self.rocW.Add(price)
if self.sma.IsReady and self.rocp.IsReady:
sma = self.sma.Current.Value
roc = self.rocp.Current.Value
self.rocV = 100 * (self.rocW[0] - self.rocW[self.rocW.Count-1]) / self.rocW[self.rocW.Count-1]
self.isGood = sma > self.avgVolMax and roc > 0
def warmup(self, history):
for index, row in history.loc[str(self.Symbol)].iterrows():
c = row['close']
v = row['volume']
self.sma.Update(index, v)
self.rocp.Update(index, c)
self.rocW.Add(c)
self.rocV = 100 * (self.rocW[0] - self.rocW[self.rocW.Count-1]) / self.rocW[self.rocW.Count-1]
class EmaUniverseSelectionModel(FundamentalUniverseSelectionModel):
'''Provides an implementation of FundamentalUniverseSelectionModel that subscribes to
symbols with the larger delta by percentage between the two exponential moving average'''
def __init__(self, slowPeriod = 50,
resolution = Resolution.Daily,
field = Field.Close,
universeSettings = None,
securityInitializer = None):
'''Initializes a new instance of the EmaCrossUniverseSelectionModel class
Args:
fastPeriod: Fast EMA period
slowPeriod: Slow EMA period
universeCount: Maximum number of members of this universe selection
universeSettings: The settings used when adding symbols to the algorithm, specify null to use algorthm.UniverseSettings
securityInitializer: Optional security initializer invoked when creating new securities, specify null to use algorithm.SecurityInitializer'''
super().__init__(True, universeSettings, securityInitializer)
self.slowPeriod = slowPeriod
self.resolution = resolution
self.field = field
self.coarseData = {}
self.week = 0
self.month = -1
self.__avgVolPeriod = 30
self.__avgVolMax = 1000000 #1M
self.__dailyVol = 500000
self.__filterPriceMin = 2
self.__filterPriceMax = 500
self.__numberOfSymbols = 100
self.spy = Symbol.Create('SPY', SecurityType.Equity, 'USA')
def SelectCoarse(self, algorithm, coarse):
'''Defines the coarse fundamental selection function.
Args:
algorithm: The algorithm instance
coarse: The coarse fundamental data used to perform filtering</param>
Returns:
An enumerable of symbols passing the filter'''
# current_week = algorithm.Time.month
# if current_week == self.week:
# return self.symbols
# self.week = current_week
symbols = [x for x in coarse if self.__filterPriceMax > x.Price > self.__filterPriceMin and \
x.Volume > self.__dailyVol]
symbols = sorted(symbols, key = lambda x: x.Volume, reverse=True)[:300]
for cf in symbols:
if not cf.Symbol in self.coarseData:
history = algorithm.History([cf.Symbol], 30, self.resolution)
if history.empty:
continue
if history.size < 30:
continue
self.coarseData[cf.Symbol] = SymbolData(cf.Symbol, algorithm)
self.coarseData[cf.Symbol].warmup(history)
self.coarseData[cf.Symbol].update(cf.EndTime, cf.Volume, cf.AdjustedPrice)
algorithm.Debug(f"-Universe Coarse- {algorithm.Time - timedelta(1)}")
values = list(filter(lambda x: x.isGood, self.coarseData.values()))
values = sorted(values, key = lambda x: x.rocV, reverse=True)[:self.__numberOfSymbols]
algorithm.Debug(f"\t symbols: {[x.Symbol.Value for x in values]}")
res = [x.Symbol for x in values]
res.append(self.spy)
return res
def SelectFine(self, algorithm, fine):
# current_week = algorithm.Time.month
# if current_week == self.week:
# return self.symbols
symbols = [x.Symbol for x in fine if x.SecurityReference.ExchangeId in ["NAS", "NYS", "ASE"]]
symbols.append(self.spy)
return symbols
def OnSecuritiesChanged(self, algorithm, changes):
'''Event fired each time the 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'''
for removed in changes.RemovedSecurities:
# clean up data from removed securities
symbol = removed.Symbol
if symbol in self.coarseData:
data = self.coarseData.pop(symbol)