| Overall Statistics |
|
Total Trades 4884 Average Win 0.06% Average Loss -0.01% Compounding Annual Return 13.684% Drawdown 20.900% Expectancy 4.189 Net Profit 340.589% Sharpe Ratio 1.188 Probabilistic Sharpe Ratio 67.375% Loss Rate 16% Win Rate 84% Profit-Loss Ratio 5.16 Alpha 0.043 Beta 0.555 Annual Standard Deviation 0.098 Annual Variance 0.01 Information Ratio -0.191 Tracking Error 0.084 Treynor Ratio 0.211 Total Fees $4884.00 Estimated Strategy Capacity $42000000.00 Lowest Capacity Asset TLT SGNKIKYGE9NP |
# 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.Algorithm.Framework.Alphas import *
from Portfolio.EqualWeightingPortfolioConstructionModel import *
class AccumulativeInsightPortfolioConstructionModel(EqualWeightingPortfolioConstructionModel):
'''Provides an implementation of IPortfolioConstructionModel that allocates percent of account
to each insight, defaulting to 3%.
For insights of direction InsightDirection.Up, long targets are returned and
for insights of direction InsightDirection.Down, short targets are returned.
By default, no rebalancing shall be done.
Rules:
1. On active Up insight, increase position size by percent
2. On active Down insight, decrease position size by percent
3. On active Flat insight, move by percent towards 0
4. On expired insight, and no other active insight, emits a 0 target'''
def __init__(self, rebalance = None, portfolioBias = PortfolioBias.Long, percent = 0.04):
'''Initialize a new instance of AccumulativeInsightPortfolioConstructionModel
Args:
rebalance: Rebalancing parameter. If it is a timedelta, date rules or Resolution, it will be converted into a function.
If None will be ignored.
The function returns the next expected rebalance time for a given algorithm UTC DateTime.
The function returns null if unknown, in which case the function will be called again in the
next loop. Returning current time will trigger rebalance.
portfolioBias: Specifies the bias of the portfolio (Short, Long/Short, Long)
percent: percent of portfolio to allocate to each position'''
super().__init__(rebalance)
self.portfolioBias = portfolioBias
self.percent = abs(percent)
self.sign = lambda x: -1 if x < 0 else (1 if x > 0 else 0)
def DetermineTargetPercent(self, activeInsights):
'''Will determine the target percent for each insight
Args:
activeInsights: The active insights to generate a target for'''
percentPerSymbol = {}
insights = sorted(self.InsightCollection.GetActiveInsights(self.currentUtcTime), key=lambda insight: insight.GeneratedTimeUtc)
for insight in insights:
targetPercent = 0
if insight.Symbol in percentPerSymbol:
targetPercent = percentPerSymbol[insight.Symbol]
if insight.Direction == InsightDirection.Flat:
# We received a Flat
# if adding or subtracting will push past 0, then make it 0
if abs(targetPercent) < self.percent:
targetPercent = 0
else:
# otherwise, we flatten by percent
targetPercent += (-self.percent if targetPercent > 0 else self.percent)
targetPercent += self.percent * insight.Direction
# adjust to respect portfolio bias
if self.portfolioBias != PortfolioBias.Long and self.sign(targetPercent) != self.portfolioBias:
targetPercent = 0
percentPerSymbol[insight.Symbol] = targetPercent
return dict((insight, percentPerSymbol[insight.Symbol]) for insight in activeInsights)
def CreateTargets(self, algorithm, insights):
'''Create portfolio targets from the specified insights
Args:
algorithm: The algorithm instance
insights: The insights to create portfolio targets from
Returns:
An enumerable of portfolio targets to be sent to the execution model'''
self.currentUtcTime = algorithm.UtcTime
return super().CreateTargets(algorithm, insights)from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
import numpy as np
from itertools import groupby
class SymbolData:
def __init__(self,algo,symbol, history_length=50):
self.symbol = symbol
self.algo = algo
self.history_length = history_length
self.cons = TradeBarConsolidator(timedelta(hours=1))
self.cons.DataConsolidated += self.handler
# Register consolidator to get automatically updated with hour data
self.algo.SubscriptionManager.AddConsolidator(self.symbol, self.cons)
self.history = pd.Series()
# Warm up history
history = algo.History(symbol, history_length, Resolution.Hour)
if history.empty or 'close' not in history.columns:
return
self.history = history.loc[symbol].close
@property
def IsReady(self):
return len(self.history) == self.history_length
def handler(self,sender,bar):
self.history = self.history.append(pd.Series([bar.Close], index=[bar.EndTime]))[-self.history_length:]
def dispose(self):
self.algo.SubscriptionManager.RemoveConsolidator(self.symbol, self.cons)
class AlphaFiveUSTreasuries(QCAlgorithm):
def Initialize(self):
#1. Required: Five years of backtest history
self.SetStartDate(2008, 1, 1)
self.SetStartDate(2010, 1, 1)
#2. Required: Alpha Streams Models:
# self.SetBrokerageModel(BrokerageName.AlphaStreams)
#3. Required: Significant AUM Capacity
self.SetCash(25000)
#4. Required: Benchmark to SPY
self.AddEquity("SPY", Resolution.Hour)
self.SetBenchmark("SPY")
Benchmark = "QQQ"
self.dataBySymbol = {}
#self.SetPortfolioConstruction(AccumulativeInsightPortfolioConstructionModel())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
# self.SetExecution(TradeExecution()))
#Market Indicators
self.spy = "SPY"
self.iwm = "IWM"
self.mdy = "MDY"
self.qqq = "QQQ"
self.vix = "VIX"
self.assets = ["QQQ","TLT"]
#self.assets = ['QQQ', 'FDN', 'TLT', 'TLH']
self.symbols = {}
self.portfolioValue = RollingWindow[Decimal](500)
self.timeDelta = int(self.GetParameter("timeDelta"))
self.UniverseSettings.Resolution = Resolution.Hour
self.SetWarmup(500)
# Add Equity ------------------------------------------------
self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), self.TimeRules.AfterMarketOpen("SPY", 30), self.EveryDayAfterMarketOpen)
# Stock Selector
self.AddUniverse(self.Coarse)
self.activelyTrading = []
self.weight = 0
self.numberOfSymbolsCoarse = 5
self.exposureToSector = 2
self.lastMonth = -1
def OnSecuritiesChanged(self,changes):
for x in changes.AddedSecurities:
if x.Symbol not in self.dataBySymbol:
self.dataBySymbol[x.Symbol] = SymbolData(self,x.Symbol)
for x in changes.RemovedSecurities:
data = self.dataBySymbol.pop(x.Symbol,None)
if data:
data.dispose()
def Coarse(self, coarse):
selected = []
for asset in self.assets:
symbol = Symbol.Create(asset, SecurityType.Equity, Market.USA)
selected.append(symbol)
return selected
def EveryDayAfterMarketOpen(self):
if not self.Portfolio.Invested:
insights = []
for symbol in self.dataBySymbol:
insights.append( Insight.Price(symbol, timedelta(days=(int(self.GetParameter("timeDelta")))), InsightDirection.Up, 0.01, None, None, 1/len(self.dataBySymbol)) )
self.EmitInsights(insights)
else:
#==============================
# Initialize instance of Random Forest Regressor
regressor = RandomForestRegressor(n_estimators=100, min_samples_split=5, random_state = 1990)
# Fetch history on our universe
df = pd.DataFrame()
for symbol, symbol_data in self.dataBySymbol.items():
if symbol_data.IsReady:
df[symbol] = symbol_data.history
# Get train/test data
returns = df.pct_change().dropna()
X = returns
y = [x for x in self.portfolioValue][-X.shape[0]:]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 1990)
# Fit regressor
regressor.fit(X_train, y_train)
# Get long-only predictions
weights = regressor.feature_importances_
symbols = returns.columns[np.where(weights)]
selected = zip(symbols, weights)
# ==============================
insights = []
for symbol, weight in selected:
insights.append( Insight.Price(symbol, timedelta(days=(int(self.GetParameter("timeDelta")))), InsightDirection.Up, 0.01, None, None, weight) )
self.EmitInsights(insights)
def OnData(self, data):
self.portfolioValue.Add(self.Portfolio.TotalPortfolioValue)
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 TrailingStopRiskManagementModel(RiskManagementModel):
'''Provides an implementation of IRiskManagementModel that limits the maximum possible loss
measured from the highest unrealized profit'''
def __init__(self, maximumDrawdownPercent = 0.04):
'''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.trailingHighs = dict()
self.lastDay = -1
self.percentGain = 0.005
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'''
if algorithm.Time.day == self.lastDay:
return []
self.lastDay = algorithm.Time.day
riskAdjustedTargets = list()
for kvp in algorithm.Securities:
symbol = kvp.Key
security = kvp.Value
percentChange = algorithm.Securities[symbol].Holdings.UnrealizedProfitPercent / 0.01
# Add newly invested securities
if symbol not in self.trailingHighs:
self.trailingHighs[symbol] = security.Close # Set to average holding cost
continue
# Remove if not invested
if not security.Invested and symbol in self.trailingHighs:
try:
self.trailingHighs.pop(symbol, None)
except:
continue
continue
if percentChange.is_integer() and percentChange > 0:
self.trailingHighs[symbol] = security.Close
# Check for new highs and update - set to tradebar high
# if self.trailingHighs[symbol] < security.High:
# self.trailingHighs[symbol] = security.High
# continue
# Check for securities past the drawdown limit
securityHigh = self.trailingHighs[symbol]
if securityHigh == 0:
riskAdjustedTargets.append(PortfolioTarget(symbol, 0))
continue
drawdown = (security.Low / securityHigh) - 1
if drawdown < self.maximumDrawdownPercent:
# liquidate
riskAdjustedTargets.append(PortfolioTarget(symbol, 0))
return riskAdjustedTargets
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")
from System import *
from QuantConnect import *
from QuantConnect.Indicators import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Portfolio import *
import numpy as np
from datetime import datetime
class VolumeWeightedAveragePriceExecutionModel(ExecutionModel):
'''Execution model that submits orders while the current market price is more favorable that the current volume weighted average price.'''
def __init__(self):
'''Initializes a new instance of the VolumeWeightedAveragePriceExecutionModel class'''
self.targetsCollection = PortfolioTargetCollection()
self.symbolData = {}
# Gets or sets the maximum order quantity as a percentage of the current bar's volume.
# This defaults to 0.01m = 1%. For example, if the current bar's volume is 100,
# then the maximum order size would equal 1 share.
self.MaximumOrderQuantityPercentVolume = 0.02
def Execute(self, algorithm, targets):
'''Executes market orders if the standard deviation of price is more
than the configured number of deviations in the favorable direction.
Args:
algorithm: The algorithm instance
targets: The portfolio targets'''
# update the complete set of portfolio targets with the new targets
self.targetsCollection.AddRange(targets)
# for performance we check count value, OrderByMarginImpact and ClearFulfilled are expensive to call
if self.targetsCollection.Count > 0:
for target in self.targetsCollection.OrderByMarginImpact(algorithm):
symbol = target.Symbol
# calculate remaining quantity to be ordered
unorderedQuantity = OrderSizing.GetUnorderedQuantity(algorithm, target)
# fetch our symbol data containing our VWAP indicator
data = self.symbolData.get(symbol, None)
if data is None: return
# check order entry conditions
if self.PriceIsFavorable(data, unorderedQuantity):
# adjust order size to respect maximum order size based on a percentage of current volume
orderSize = OrderSizing.GetOrderSizeForPercentVolume(data.Security, self.MaximumOrderQuantityPercentVolume, unorderedQuantity)
if orderSize != 0:
algorithm.MarketOrder(symbol, orderSize)
self.targetsCollection.ClearFulfilled(algorithm)
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'''
for removed in changes.RemovedSecurities:
# clean up removed security data
if removed.Symbol in self.symbolData:
if self.IsSafeToRemove(algorithm, removed.Symbol):
data = self.symbolData.pop(removed.Symbol)
algorithm.SubscriptionManager.RemoveConsolidator(removed.Symbol, data.Consolidator)
for added in changes.AddedSecurities:
if added.Symbol not in self.symbolData:
self.symbolData[added.Symbol] = SymbolData(algorithm, added)
def PriceIsFavorable(self, data, unorderedQuantity):
'''Determines if the current price is more than the configured
number of standard deviations away from the mean in the favorable direction.'''
if unorderedQuantity > 0:
if data.Security.BidPrice < data.VWAP:
return True
else:
if data.Security.AskPrice > data.VWAP:
return True
return False
def IsSafeToRemove(self, algorithm, symbol):
'''Determines if it's safe to remove the associated symbol data'''
# confirm the security isn't currently a member of any universe
return not any([kvp.Value.ContainsMember(symbol) for kvp in algorithm.UniverseManager])
class SymbolData:
def __init__(self, algorithm, security):
self.Security = security
self.Consolidator = algorithm.ResolveConsolidator(security.Symbol, security.Resolution)
name = algorithm.CreateIndicatorName(security.Symbol, "VWAP", security.Resolution)
self.vwap = IntradayVwap(name)
algorithm.RegisterIndicator(security.Symbol, self.vwap, self.Consolidator)
@property
def VWAP(self):
return self.vwap.Value
class IntradayVwap:
'''Defines the canonical intraday VWAP indicator'''
def __init__(self, name):
self.Name = name
self.Value = 0.0
self.lastDate = datetime.min
self.sumOfVolume = 0.0
self.sumOfPriceTimesVolume = 0.0
@property
def IsReady(self):
return self.sumOfVolume > 0.0
def Update(self, input):
'''Computes the new VWAP'''
success, volume, averagePrice = self.GetVolumeAndAveragePrice(input)
if not success:
return self.IsReady
# reset vwap on daily boundaries
if self.lastDate != input.EndTime.date():
self.sumOfVolume = 0.0
self.sumOfPriceTimesVolume = 0.0
self.lastDate = input.EndTime.date()
# running totals for Σ PiVi / Σ Vi
self.sumOfVolume += volume
self.sumOfPriceTimesVolume += averagePrice * volume
if self.sumOfVolume == 0.0:
# if we have no trade volume then use the current price as VWAP
self.Value = input.Value
return self.IsReady
self.Value = self.sumOfPriceTimesVolume / self.sumOfVolume
return self.IsReady
def GetVolumeAndAveragePrice(self, input):
'''Determines the volume and price to be used for the current input in the VWAP computation'''
if type(input) is Tick:
if input.TickType == TickType.Trade:
return True, float(input.Quantity), float(input.LastPrice)
if type(input) is TradeBar:
if not input.IsFillForward:
averagePrice = float(input.High + input.Low + input.Close) / 3
return True, float(input.Volume), averagePrice
return False, 0.0, 0.0