| Overall Statistics |
|
Total Trades 1739 Average Win 0.22% Average Loss -0.06% Compounding Annual Return 15.344% Drawdown 7.700% Expectancy 3.232 Net Profit 585.234% Sharpe Ratio 1.633 Probabilistic Sharpe Ratio 98.562% Loss Rate 12% Win Rate 88% Profit-Loss Ratio 3.81 Alpha 0.129 Beta -0.007 Annual Standard Deviation 0.079 Annual Variance 0.006 Information Ratio 0.129 Tracking Error 0.202 Treynor Ratio -19.288 Total Fees $3325.81 Estimated Strategy Capacity $13000000.00 Lowest Capacity Asset FDN TJPMW3BHNMUD |
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
import tweepy
import statistics
# import datetime
from datetime import timedelta, datetime
class ScheduledExecutionModel(ExecutionModel):
'''Execution model that submits orders while the current market price is more favorable that the current volume weighted average price.'''
def __init__(self, algorithm, *args, **kwargs):
super().__init__()
'''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.05
# Gets or sets the maximum spread compare to current price in percentage.
self.acceptingSpreadPercent = 0.005
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)
max_quantity = algorithm.CalculateOrderQuantity(symbol, 0.5)
try:
order_percent = round(float(orderSize/max_quantity), 4)
except:
order_percent = 0.0
if (orderSize != 0) and (abs(order_percent) > 0.05):
if algorithm.Portfolio[symbol].Invested:
coef = abs(order_percent)
if coef < 0.25:
coef = int(abs(float(order_percent))/0.05)*10
if self.breakout(symbol, algorithm):
coef = coef * 20
orderSize = OrderSizing.GetOrderSizeForPercentVolume(data.Security, self.MaximumOrderQuantityPercentVolume*coef, unorderedQuantity)
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 SpreadIsFavorable(self, data):
'''Determines if the spread is in desirable range.'''
# Price has to be larger than zero to avoid zero division error, or negative price causing the spread percentage < 0 by error
# Has to be in opening hours of exchange to avoid extreme spread in OTC period
return data.Security.Exchange.ExchangeOpen \
and data.Security.Price > 0 and data.Security.AskPrice > 0 and data.Security.BidPrice > 0 \
and (data.Security.AskPrice - data.Security.BidPrice) / data.Security.Price <= self.acceptingSpreadPercent
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])
def breakout(self, symbol, algorithm):
self.lookback = 20
self.ceiling, self.floor = 30, 10
close = algorithm.History(symbol, 31, Resolution.Daily)["close"]
todayvol = np.std(close[1:31])
yesterdayvol = np.std(close[0:30])
deltavol = (todayvol - yesterdayvol) / todayvol
self.lookback = round(self.lookback * (1 + deltavol))
# Account for upper/lower limit of lockback length
if self.lookback > self.ceiling:
self.lookback = self.ceiling
elif self.lookback < self.floor:
self.lookback = self.floor
# List of daily highs
self.high = algorithm.History(symbol, self.lookback, Resolution.Daily)["high"]
# Buy in case of breakout
#if not self.Securities[symbol].Invested and \
if algorithm.Securities[symbol].Close >= max(self.high[:-1]):
return True
def UpdateTickets(self, algorithm, symbol, orderSize):
close = algorithm.Securities[symbol].Close
#algorithm.StopMarketOrder(symbol, -orderSize, close * 0.95)
algorithm.LimitOrder(symbol, -int(orderSize/2), close * 1.025)
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
def dispose(self, algorithm):
algorithm.SubscriptionManager.RemoveConsolidator(security.Symbol, self.consolidator)
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.Open + input.High + input.Low + input.Close) / 4
medianPrice = statistics.median([input.Open, input.High, input.Low, input.Close])
return True, float(input.Volume), medianPrice
return False, 0.0, 0.0import pandas as pd
import numpy as np
from scipy.optimize import minimize
class myTrailingStopRiskManagementModel:
'''
Credit goes to: Alex Catarino and many of his friends at QuantConnect
https://github.com/QuantConnect/Lean/blob/master/Algorithm.Framework/Risk/TrailingStopRiskManagementModel.py
Description:
Limits the maximum possible loss measured from the highest unrealized profit
'''
def __init__(self, maximumDrawdownPercent = 0.08):
'''initializes the 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()
def setDD(self, maximumDrawdownPercent = 0.08):
'''allows to change the drawdown
Args: maximumDrawdownPercent: The maximum percentage drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown
'''
self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)
def setWTtoZeroIfDDtooHigh(self, algorithm, targets=None):
'''If drawdown is too high, set wt[symbol] to zero
algo.wt[symbol] = weights which will be set to 0 in case drawdown exceeds the maximum
'''
for kvp in algorithm.Securities:
symbol = kvp.Key
security = kvp.Value
# Remove from trailingHighs dict if not invested
if not security.Invested:
self.trailingHighs.pop(symbol, None)
continue
# Add newly invested securities to trailingHighs dict
if symbol not in self.trailingHighs:
self.trailingHighs[symbol] = security.Holdings.AveragePrice
continue
# Check for new highs and update trailingHighs dict
if self.trailingHighs[symbol] < security.High:
self.trailingHighs[symbol] = security.High
continue
# Calc the drawdown
securityHigh = self.trailingHighs[symbol]
drawdown = (security.Low / securityHigh) - 1
# If drawdown is too high, set symbol weight to zero
if drawdown < self.maximumDrawdownPercent:
algorithm.wt[symbol] = 0
return
class myPortfolioOptimizer:
'''
Credit goes to: Emilio Freire / InnoQuantivity
https://innoquantivity.com/blogs/inno-blog/portfolio-optimization-quantconnect-research-algorithm
https://www.quantconnect.com/forum/discussion/8128/portfolio-optimization-research-amp-algorithm-for-better-workflows/p1/comment-22952
Description:
Implementation of a custom optimizer that calculates the weights for each asset to optimize a given objective function
Details:
Optimization can be:
- Equal Weighting
- Maximize Portfolio Return
- Minimize Portfolio Standard Deviation
- Mean-Variance (minimize Standard Deviation given a target return)
- Maximize Portfolio Sharpe Ratio
- Maximize Portfolio Sortino Ratio
- Risk Parity Portfolio
Constraints:
- Weights must be between some given boundaries
- Weights must sum to 1
'''
def __init__(self,
minWeight = 0,
maxWeight = 1):
'''
Description:
Initialize the CustomPortfolioOptimizer
Args:
minWeight(float): The lower bound on portfolio weights
maxWeight(float): The upper bound on portfolio weights
'''
self.minWeight = minWeight
self.maxWeight = maxWeight
def CalcWeights(self, algorithm, symbols, objectiveFunction='riskParity', lookback=63, targetReturn=None):
'''
Description:
Calculate weights from daily returns, return a pandas Series
'''
history = np.log10(algorithm.History(symbols, lookback, Resolution.Daily)['close'].unstack(level = 0))
returnsDf = history.pct_change().dropna()
returnsDf.columns = [algorithm.AddEquity(i).Symbol.Value for i in list(returnsDf.columns)]
weights = self.Optimize(objectiveFunction, returnsDf, targetReturn)
return pd.Series(weights, index=returnsDf.columns, name='weights')
def Optimize(self, objFunction, dailyReturnsDf, targetReturn = None):
'''
Description:
Perform portfolio optimization given a series of returns
Args:
objFunction: The objective function to optimize (equalWeighting, maxReturn, minVariance, meanVariance, maxSharpe, maxSortino, riskParity)
dailyReturnsDf: DataFrame of historical daily arithmetic returns
Returns:
Array of double with the portfolio weights (size: K x 1)
'''
# initial weights: equally weighted
size = dailyReturnsDf.columns.size # K x 1
self.initWeights = np.array(size * [1. / size])
# get sample covariance matrix
covariance = dailyReturnsDf.cov()
# get the sample covariance matrix of only negative returns for sortino ratio
negativeReturnsDf = dailyReturnsDf[dailyReturnsDf < 0]
covarianceNegativeReturns = negativeReturnsDf.cov()
if objFunction == 'equalWeighting':
return self.initWeights
bounds = tuple((self.minWeight, self.maxWeight) for x in range(size))
constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1.0}]
if objFunction == 'meanVariance':
# if no target return is provided, use the resulting from equal weighting
if targetReturn is None:
targetReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, self.initWeights)
constraints.append( {'type': 'eq', 'fun': lambda weights:
self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights) - targetReturn} )
opt = minimize(lambda weights: self.ObjectiveFunction(objFunction, dailyReturnsDf,
covariance, covarianceNegativeReturns,
weights),
x0 = self.initWeights,
bounds = bounds,
constraints = constraints,
method = 'SLSQP')
return opt['x']
def ObjectiveFunction(self, objFunction, dailyReturnsDf, covariance, covarianceNegativeReturns, weights):
'''
Description:
Compute the objective function
Args:
objFunction: The objective function to optimize (equalWeighting, maxReturn, minVariance, meanVariance,
maxSharpe, maxSortino, riskParity)
dailyReturnsDf: DataFrame of historical daily returns
covariance: Sample covariance
covarianceNegativeReturns: Sample covariance matrix of only negative returns
weights: Portfolio weights
'''
if objFunction == 'maxReturn':
f = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
return -f # convert to negative to be minimized
elif objFunction == 'minVariance':
f = self.CalculateAnnualizedPortfolioStd(covariance, weights)
return f
elif objFunction == 'meanVariance':
f = self.CalculateAnnualizedPortfolioStd(covariance, weights)
return f
elif objFunction == 'maxSharpe':
f = self.CalculateAnnualizedPortfolioSharpeRatio(dailyReturnsDf, covariance, weights)
return -f # convert to negative to be minimized
elif objFunction == 'maxSortino':
f = self.CalculateAnnualizedPortfolioSortinoRatio(dailyReturnsDf, covarianceNegativeReturns, weights)
return -f # convert to negative to be minimized
elif objFunction == 'riskParity':
f = self.CalculateRiskParityFunction(covariance, weights)
return f
else:
raise ValueError(f'PortfolioOptimizer.ObjectiveFunction: objFunction input has to be one of equalWeighting,'
+ ' maxReturn, minVariance, meanVariance, maxSharpe, maxSortino or riskParity')
def CalculateAnnualizedPortfolioReturn(self, dailyReturnsDf, weights):
annualizedPortfolioReturns = np.sum( ((1 + dailyReturnsDf.mean())**252 - 1) * weights )
return annualizedPortfolioReturns
def CalculateAnnualizedPortfolioStd(self, covariance, weights):
annualizedPortfolioStd = np.sqrt( np.dot(weights.T, np.dot(covariance * 252, weights)) )
if annualizedPortfolioStd == 0:
raise ValueError(f'PortfolioOptimizer.CalculateAnnualizedPortfolioStd: annualizedPortfolioStd cannot be zero. Weights: {weights}')
return annualizedPortfolioStd
def CalculateAnnualizedPortfolioNegativeStd(self, covarianceNegativeReturns, weights):
annualizedPortfolioNegativeStd = np.sqrt( np.dot(weights.T, np.dot(covarianceNegativeReturns * 252, weights)) )
if annualizedPortfolioNegativeStd == 0:
raise ValueError(f'PortfolioOptimizer.CalculateAnnualizedPortfolioNegativeStd: annualizedPortfolioNegativeStd cannot be zero. Weights: {weights}')
return annualizedPortfolioNegativeStd
def CalculateAnnualizedPortfolioSharpeRatio(self, dailyReturnsDf, covariance, weights):
annualizedPortfolioReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
annualizedPortfolioStd = self.CalculateAnnualizedPortfolioStd(covariance, weights)
annualizedPortfolioSharpeRatio = annualizedPortfolioReturn / annualizedPortfolioStd
return annualizedPortfolioSharpeRatio
def CalculateAnnualizedPortfolioSortinoRatio(self, dailyReturnsDf, covarianceNegativeReturns, weights):
annualizedPortfolioReturn = self.CalculateAnnualizedPortfolioReturn(dailyReturnsDf, weights)
annualizedPortfolioNegativeStd = self.CalculateAnnualizedPortfolioNegativeStd(covarianceNegativeReturns, weights)
annualizedPortfolioSortinoRatio = annualizedPortfolioReturn / annualizedPortfolioNegativeStd
return annualizedPortfolioSortinoRatio
def CalculateRiskParityFunction(self, covariance, weights):
''' Spinu formulation for risk parity portfolio '''
assetsRiskBudget = self.initWeights
portfolioVolatility = self.CalculateAnnualizedPortfolioStd(covariance, weights)
x = weights / portfolioVolatility
riskParity = (np.dot(x.T, np.dot(covariance, x)) / 2) - np.dot(assetsRiskBudget.T, np.log(x))
return riskParityfrom System import *
from QuantConnect import *
import tweepy, statistics
from datetime import datetime, timedelta, date
import numpy as np
from scipy import stats
from helpers import myTrailingStopRiskManagementModel
class DualMomentumWithOutDaysAlphaModel(AlphaModel):
def __init__(self, algorithm, VOLA = 126, BASE_RET = 83, resolution = Resolution.Daily, *args, **kwargs):
super().__init__()
self.VOLA = VOLA
self.BASE_RET = BASE_RET
self.resolution = resolution
self.MKT = algorithm.AddEquity('SPY', resolution).Symbol
self.SLV = algorithm.AddEquity('SLV', resolution).Symbol
self.GLD = algorithm.AddEquity('GLD', resolution).Symbol
self.XLI = algorithm.AddEquity('XLI', resolution).Symbol
self.XLU = algorithm.AddEquity('XLU', resolution).Symbol
self.DBB = algorithm.AddEquity('DBB', resolution).Symbol
self.UUP = algorithm.AddEquity('UUP', resolution).Symbol
self.count = self.BASE_RET
self.outday = 5
pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
for symbol in pairs:
self.consolidator = TradeBarConsolidator(timedelta(hours=1))
self.consolidator.DataConsolidated += self.consolidation_handler
algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
self.history = algorithm.History(pairs, self.VOLA + 1, self.resolution)
# self.history = self.history['close'].unstack(level=0).dropna()
self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), 1)
resolutionString = Extensions.GetEnumString(resolution, Resolution)
self.Name = f"{self.__class__.__name__}({resolutionString})"
# Force alpha to only produce insights Daily at 11.10am
self.set_flag = False
algorithm.Schedule.On(algorithm.DateRules.EveryDay(),
algorithm.TimeRules.AfterMarketOpen('SPY', 60),
self.SetFlag)
def SetFlag(self):
self.set_flag = True
def consolidation_handler(self, sender, consolidated):
self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
self.history = self.history.iloc[-(self.VOLA + 1):]
def Update(self, algorithm, _data):
if algorithm.IsWarmingUp or not self.set_flag:
return []
self.set_flag = False
insights = []
# Volatility
vola = self.history[self.MKT].pct_change().std() * np.sqrt(252)
wait_days = int(vola * self.BASE_RET)
period = int((1.0 - vola) * self.BASE_RET)
r = self.history.pct_change(period).iloc[-1]
exit_market = r[self.SLV] < r[self.GLD] and r[self.XLI] < r[self.XLU] and r[self.DBB] < r[self.UUP]
direction = InsightDirection.Down
if (exit_market):
#algorithm.Plot("In vs Out", "Market", -1)
direction = InsightDirection.Down
self.outday = self.count
else:
if (self.count >= wait_days + self.outday):
#algorithm.Plot("In vs Out", "Market", 1)
direction = InsightDirection.Up
self.count += 1
# algorithm.Plot("Wait Days", "Actual", self.count)
# algorithm.Plot("Wait Days", "Expected", wait_days + self.outday)
algorithm.Plot("Market Volatility", str(self.MKT), float(vola))
insights.append(Insight.Price(self.MKT, self.predictionInterval, direction))
return insightsfrom itertools import groupby
import tweepy
from datetime import datetime, timedelta, date
import time
import pandas as pd
import numpy as np
import re, math
import scipy
from math import ceil
from collections import deque
from itertools import chain
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Data.Market import TradeBar
from QuantConnect.Algorithm.Framework.Execution import StandardDeviationExecutionModel, VolumeWeightedAveragePriceExecutionModel
from QuantConnect.Algorithm.Framework.Risk import MaximumDrawdownPercentPortfolio, MaximumUnrealizedProfitPercentPerSecurity, MaximumDrawdownPercentPerSecurity, TrailingStopRiskManagementModel
from dual_momentum_with_out_days_alpha import DualMomentumWithOutDaysAlphaModel
from trade_execution import ScheduledExecutionModel
from portfolio_management import PortfolioManagementModel
VOLA = 126; BASE_RET = 83; RET = 252; EXCL = 21; LEV = 1.00;
class HorizontalQuantumCoil(QCAlgorithm):
def Initialize(self):
self.Portfolio.MarginCallModel = MarginCallModel.Null
self.SetStartDate(2008, 1, 1)
self.SetCash(100000)
self.added_cash = 115
self.upkeep = 28
self.simulate_live = False
self.SetWarmUp(timedelta(252))
self.Settings.FreePortfolioValuePercentage = 0.05
#self.SetBrokerageModel(BrokerageName.AlphaStreams)
self.SetAlpha(DualMomentumWithOutDaysAlphaModel(self, VOLA, BASE_RET, Resolution.Daily))
stonks = ['FDN', 'QQQ', 'IWM', 'SPY', 'VTI', 'DIA', 'IWF', 'TLT', 'TLH', 'IEI', 'IEF']
# lev_stonks = ['UDOW', 'TQQQ', 'URTY', 'SPXL', 'TMF', 'AGQ', 'UGL']
# stonks = ['ITOT', 'IVV', 'IJH', 'IJR', 'XT', 'IHAK', 'IWFH', 'IDNA', 'IRBO', 'TECB', 'BFTR', 'BTEK', 'BMED']
symbols = []
for stonk in stonks:
val = Symbol.Create(stonk, SecurityType.Equity, Market.USA)
symbols.append(val)
self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
self.UniverseSettings.Resolution = Resolution.Daily
self.SetPortfolioConstruction(PortfolioManagementModel(self, RET, EXCL, LEV, Resolution.Daily))
self.SetExecution(ScheduledExecutionModel(self))
# self.SetRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(maximumUnrealizedProfitPercent = 0.10))
# self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(maximumDrawdownPercent = 0.075))
# self.SetRiskManagement(MaximumDrawdownPercentPortfolio(maximumDrawdownPercent = 0.05, isTrailing = True))
self.createPlots("SPY")
# if self.simulate_live:
# self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), \
# self.TimeRules.BeforeMarketClose("SPY", 0), \
# self.AddCash)
# self.Schedule.On(self.DateRules.MonthStart("SPY"), \
# self.TimeRules.BeforeMarketClose("SPY", 0), \
# self.UpKeep)
def AddCash(self):
self.Portfolio.SetCash(self.Portfolio.Cash + self.added_cash)
def UpKeep(self):
self.Portfolio.SetCash(self.Portfolio.Cash - self.upkeep)
def consolidation_handler(self, sender, consolidated):
self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
self.history = self.history.iloc[-(VOLA + 1):]
def createPlots(self, benchmark):
self.__benchmark = benchmark
self.__plot_every_n_days = 5
self.__plot_every_n_days_i = 0
plot = Chart('Performance')
plot.AddSeries(Series(self.__benchmark, SeriesType.Line, 0, '%'))
plot.AddSeries(Series("Algorithm", SeriesType.Line, 0, '%'))
self.AddChart(plot)
self.ResetPlot()
def ResetPlot(self):
self.year = self.Time.year
self.__cost_portfolio = None
self.__cost_benchmark = None
def CalculateBenchmarkPerformance(self):
price = self.Securities[self.__benchmark].Price
if self.__cost_benchmark == None:
self.__cost_benchmark = price
return 100.0 * ((price / self.__cost_benchmark) - 1.0)
def CalculatePortfolioPerformance(self):
if self.__cost_portfolio == None:
self.__cost_portfolio = self.Portfolio.TotalPortfolioValue
return 100.0 * ((self.Portfolio.TotalPortfolioValue / self.__cost_portfolio) - 1.0)
def OnEndOfDay(self):
if self.IsWarmingUp or not self.Securities[self.__benchmark].HasData:
return
if self.Time.year != self.year:
self.ResetPlot()
self.__plot_every_n_days_i == -1
self.__plot_every_n_days_i += 1
if self.__plot_every_n_days_i % self.__plot_every_n_days != 0:
return
#self.Plot('Performance', self.__benchmark, self.CalculateBenchmarkPerformance())
#self.Plot('Performance', "Algorithm", self.CalculatePortfolioPerformance())
def UpdateTickets(self, symbol):
close = self.Securities[symbol].Close
quantity = self.Portfolio[symbol].Quantity
self.StopMarketOrder(symbol, -quantity, close * 0.98)
self.LimitOrder(symbol, -int(quantity/2), close * 1.05)
def breakout(self, symbol):
self.lookback = 20
self.ceiling, self.floor = 30, 10
close = self.History(symbol, 31, Resolution.Daily)["close"]
todayvol = np.std(close[1:31])
yesterdayvol = np.std(close[0:30])
deltavol = (todayvol - yesterdayvol) / todayvol
self.lookback = round(self.lookback * (1 + deltavol))
# Account for upper/lower limit of lockback length
if self.lookback > self.ceiling:
self.lookback = self.ceiling
elif self.lookback < self.floor:
self.lookback = self.floor
# List of daily highs
self.high = self.History(symbol, self.lookback, Resolution.Daily)["high"]
# Buy in case of breakout
if self.Securities[symbol].Invested and (self.Securities[symbol].Close >= max(self.high[:-1])):
return Truefrom System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Data.Market import TradeBar
import pandas as pd
import numpy as np
from scipy import stats
import statistics
from helpers import myPortfolioOptimizer
class PortfolioManagementModel(PortfolioConstructionModel):
def __init__(self,
algorithm,
RET=252,
EXCL=21,
LEV=1.00,
resolution = Resolution.Daily,
*args, **kwargs):
super().__init__()
self.resolution = resolution
self.RET = RET
self.EXCL = EXCL
self.LEV = LEV
self.VOLA = 126
self.STK1 = algorithm.AddEquity('SPY', resolution).Symbol # SPXL/SPY
self.STK2 = algorithm.AddEquity('QQQ', resolution).Symbol # TQQQ/QQQ
self.STK3 = algorithm.AddEquity('IWM', resolution).Symbol # URTY/IWM
self.STK4 = algorithm.AddEquity('FDN', resolution).Symbol # FDN/FDN
self.STK5 = algorithm.AddEquity('VTI', resolution).Symbol # AGQ/VTI
self.STK6 = algorithm.AddEquity('DIA', resolution).Symbol # AGQ/VTI
self.STK7 = algorithm.AddEquity('IWF', resolution).Symbol # AGQ/VTI
self.BND1 = algorithm.AddEquity('TLH', resolution).Symbol # TMF/TLH
self.BND2 = algorithm.AddEquity('TLT', resolution).Symbol # UGL/TLT
self.BND3 = algorithm.AddEquity('IEI', resolution).Symbol # TMF/TLH
self.BND4 = algorithm.AddEquity('IEF', resolution).Symbol # UGL/TLT
self.STOCKS = [self.STK1, self.STK2, self.STK3, self.STK4, self.STK5, self.STK6, self.STK7]
self.BONDS = [self.BND1, self.BND2, self.BND3, self.BND4]
self.ASSETS = self.STOCKS + self.BONDS
self.data = dict()
for symbol in self.ASSETS:
self.consolidator = TradeBarConsolidator(timedelta(days=1))
self.consolidator.DataConsolidated += self.consolidation_handler
algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
self.history = algorithm.History(self.ASSETS, self.VOLA + 1, self.resolution)
self.pfo = myPortfolioOptimizer(minWeight=0, maxWeight=1)
#self.pfo = myPortfolioOptimizer(minWeight=0, maxWeight=1)
for symbol in self.ASSETS:
algorithm.Securities[symbol].SetLeverage(1)
def consolidation_handler(self, sender, consolidated):
self.history.loc[consolidated.EndTime, consolidated.Symbol] = statistics.median([consolidated.High, consolidated.Low, consolidated.Close]) * consolidated.Volume
self.history = self.history.iloc[-(self.VOLA + 1):]
def OnSecuritiesChanged(self, algorithm, changes):
addedSymbols = []
for security in changes.AddedSecurities:
addedSymbols.append(security.Symbol)
if security.Symbol not in self.data:
self.data[security.Symbol] = SymbolData(security.Symbol, algorithm)
if len(addedSymbols) > 0:
history = algorithm.History(addedSymbols, self.VOLA + 1, self.resolution).loc[addedSymbols]
for symbol in addedSymbols:
try:
self.data[symbol].Warmup(history.loc[symbol])
except:
algorithm.Debug(str(symbol))
continue
def returns_custom(self, symbol, timeframe):
prices = np.log10(algorithm.History(symbol, TimeSpan.FromDays(self.VOLA), self.resolution).close)
return round((prices[-timeframe] - prices[-5]) / prices[-5], 4)
def rel_vol(self, symbol):
volume = self.data[symbol].sma.Current.Value
volumew = self.data[symbol].smaw.Current.Value
volume_ratio = round(float(volumew - volume)/volumew, 4)
return volume_ratio
def custom_filter(self, symbol, filter_type = 'both'):
if filter_type == 'both':
if ((self.data[symbol].roc_slope >= 0) and (self.data[symbol].vol_slope >= 0)) and (self.data[symbol].breakout):
return True
else:
return False
if filter_type == 'either':
if ((self.data[symbol].roc_slope >= 0) and (self.data[symbol].vol_slope >= 0)) or (self.data[symbol].breakout):
return True
else:
return False
if filter_type == 'slope':
if ((self.data[symbol].roc_slope >= 0) and (self.data[symbol].vol_slope >= 0)):
return True
else:
return False
if filter_type == 'breakout':
if self.data[symbol].breakout:
return True
else:
return False
def CreateTargets(self, algorithm, insights):
if algorithm.IsWarmingUp:
return []
targets = []
# We expect at most only one active insight since we only
# generate insights for one equity.
assert len(insights) <= 1
if len(insights) == 1:
insight = insights[0]
# self.bull = insight.Direction == InsightDirection.Up
if insight.Direction == InsightDirection.Down:
self.bull = False
selected = self.BONDS
else:
self.bull = True
selected = self.STOCKS
selected = list()
if self.bull:
stocks = [(symbol, self.data[symbol].median_roc) for symbol in self.STOCKS if self.custom_filter(symbol, filter_type = 'both')]
stocks.sort(key=lambda x: x[1], reverse=True)
for sec, roc in stocks[:1]:
selected.append(sec)
if len(selected) < 2:
#selected.append(stocks[0][0])
stocks = [(symbol, self.data[symbol].median_roc) for symbol in self.STOCKS if self.custom_filter(symbol, filter_type = 'slope')]
stocks.sort(key=lambda x: x[1], reverse=True)
for sec, roc in stocks[:1]:
selected.append(sec)
elif not self.bull:
bonds = [(symbol, self.data[symbol].median_roc) for symbol in self.BONDS if self.custom_filter(symbol, filter_type = 'both')]
bonds.sort(key=lambda x: x[1], reverse=True)
for sec, roc in bonds[:1]:
selected.append(sec)
if len(selected) < 2:
selected.append(self.BND3)
selected.append(self.BND4)
else:
return []
self.weights_maxSharpe = self.pfo.CalcWeights(algorithm, symbols=selected, objectiveFunction = "maxReturn", lookback=21*2)
for asset in self.ASSETS:
if asset in selected:
if self.data[asset].breakout:
weight = self.weights_maxSharpe[self.weights_maxSharpe.index==asset][0]
targets.append(PortfolioTarget.Percent(algorithm, asset, round(float(weight), 3)))
algorithm.Plot("Weight", asset, round(float(weight), 3))
else:
targets.append(PortfolioTarget.Percent(algorithm, asset, 0.5))
algorithm.Plot("Weight", asset, 0.5)
else:
targets.append(PortfolioTarget.Percent(algorithm, asset, 0.0))
algorithm.Plot("Weight", asset, 0.5)
return targets
class SymbolData(object):
def __init__(self, symbol, algorithm):
self.Symbol = symbol
self.lookback = 20
self.ceiling = 30
self.floor = 10
self.breakout = False
self.pwma = 0.00
self.EXCL = 21
self.scale = 0.00
self.is_uptrend = False
self.tolerance = 0.95
self.vol_slope = 0.00
self.roc_slope = 0.00
self.median_roc = 0.00
self.fast = ExponentialMovingAverage(8)
self.fast_ema_window = RollingWindow[float](5)
self.slow = ExponentialMovingAverage(14)
self.slow_ema_window = RollingWindow[float](5)
self.sma = SimpleMovingAverage(21)
self.smaw = SimpleMovingAverage(5)
self.roc = RateOfChange(5)
self.vol = RateOfChange(5)
self.roc_window = RollingWindow[float](5)
self.vol_window = RollingWindow[float](5)
self.prices_window = RollingWindow[float](41)
self.high_window = RollingWindow[float](41)
self.consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily)
algorithm.RegisterIndicator(symbol, self.roc, self.consolidator)
self.consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily)
algorithm.RegisterIndicator(symbol, self.fast, self.consolidator)
self.consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily)
algorithm.RegisterIndicator(symbol, self.slow, self.consolidator)
self.consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily)
algorithm.RegisterIndicator(symbol, self.smaw, self.consolidator)
self.consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily)
algorithm.RegisterIndicator(symbol, self.sma, self.consolidator)
# Warm up
history = algorithm.History(symbol, 126, Resolution.Daily)
if history.empty or 'close' not in history.columns:
return
for index, row in history.loc[symbol].iterrows():
tradeBar = TradeBar(index, row['open'], row['high'], row['low'], row['close'], row['volume'])
self.roc.Update(index, row['close'])
self.vol.Update(index, row['volume'])
self.fast.Update(index, statistics.median([row['high'], row['low'], row['close']])*row['volume'])
self.slow.Update(index, statistics.median([row['high'], row['low'], row['close']])*row['volume'])
self.smaw.Update(index, row['volume'])
self.sma.Update(index, row['volume'])
self.roc_window.Add(self.roc.Current.Value)
if self.roc_window.IsReady:
roc_sum = sum(list(self.roc_window))
roc_len = len(list(self.roc_window))
self.roc_slope = round(float(roc_sum)/roc_len, 4)
self.vol_window.Add(self.vol.Current.Value)
if self.vol_window.IsReady:
vol_sum = sum(list(self.vol_window))
vol_len = len(list(self.vol_window))
self.vol_slope = round(float(vol_sum)/vol_len, 4)
self.prices_window.Add(row['close'])
if self.prices_window.IsReady:
prices = np.log10(list(self.prices_window))
frames = [i+self.EXCL for i in range(-20, 20, 2)]
self.median_roc = round(statistics.median([round(float(prices[-i] - prices[-5]/ prices[-5]), 4) for i in frames]), 4)
# # power_weighted_moving_average
# self.power = 20
# def power_weighted_moving_average(self, prices):
# return self.weighted_average(prices, self.power_weights(len(prices)))
# def power_weights(self, length):
# weights = np.array([])
# for i in range(length):
# w = i + 1
# weights = np.append(weights, w**self.power)
# return weights
# def weighted_average(self, prices, weights):
# products = []
# for i in range(len(prices)):
# products.append(prices[i] * weights[i])
# return sum(products) / sum(weights)
# self.pwma = self.power_weighted_moving_average(prices)
self.high_window.Add(row['high'])
if self.high_window.IsReady and self.prices_window.IsReady:
close = list(self.prices_window)
todayvol = np.std(close[1:31])
yesterdayvol = np.std(close[0:30])
deltavol = (todayvol - yesterdayvol) / todayvol
self.lookback = round(self.lookback * (1 + deltavol))
# Account for upper/lower limit of lockback length
if self.lookback > self.ceiling:
self.lookback = self.ceiling
elif self.lookback < self.floor:
self.lookback = self.floor
high = list(self.high_window)
# Buy in case of breakout
#if not algorithm.Securities[symbol].Invested and \
if algorithm.Securities[symbol].Close >= max(high[:-1]):
self.breakout = True
fast = self.fast.Current.Value
slow = self.slow.Current.Value
self.is_uptrend = (fast > (slow * self.tolerance)) and (row['close'] > (fast * self.tolerance))
if self.is_uptrend:
self.scale = round(float(fast - slow) / ((fast + slow) / 2.0), 4)
else:
self.scale = 0from itertools import groupby
import tweepy
from datetime import datetime, timedelta, date
import time
import pandas as pd
import numpy as np
import re, math
import scipy
from math import ceil
from collections import deque
from itertools import chain
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from QuantConnect.Data.Market import TradeBar
from QuantConnect.Algorithm.Framework.Execution import StandardDeviationExecutionModel, VolumeWeightedAveragePriceExecutionModel
from QuantConnect.Algorithm.Framework.Risk import MaximumDrawdownPercentPortfolio, MaximumUnrealizedProfitPercentPerSecurity, MaximumDrawdownPercentPerSecurity, TrailingStopRiskManagementModel
from dual_momentum_with_out_days_alpha import DualMomentumWithOutDaysAlphaModel
from trade_execution import ScheduledExecutionModel
from portfolio_management import PortfolioManagementModel
VOLA = 126; BASE_RET = 83; RET = 252; EXCL = 21; LEV = 1.00;
class HorizontalQuantumCoil(QCAlgorithm):
def Initialize(self):
self.Portfolio.MarginCallModel = MarginCallModel.Null
self.SetStartDate(2008, 1, 1)
self.SetCash(100000)
self.added_cash = 115
self.upkeep = 28
self.simulate_live = False
self.SetWarmUp(timedelta(252))
self.Settings.FreePortfolioValuePercentage = 0.05
#self.SetBrokerageModel(BrokerageName.AlphaStreams)
self.SetAlpha(DualMomentumWithOutDaysAlphaModel(self, VOLA, BASE_RET, Resolution.Daily))
stonks = ['FDN', 'QQQ', 'IWM', 'SPY', 'VTI', 'DIA', 'IWF', 'TLT', 'TLH', 'IEI', 'IEF']
# lev_stonks = ['UDOW', 'TQQQ', 'URTY', 'SPXL', 'TMF', 'AGQ', 'UGL']
# stonks = ['ITOT', 'IVV', 'IJH', 'IJR', 'XT', 'IHAK', 'IWFH', 'IDNA', 'IRBO', 'TECB', 'BFTR', 'BTEK', 'BMED']
symbols = []
for stonk in stonks:
val = Symbol.Create(stonk, SecurityType.Equity, Market.USA)
symbols.append(val)
self.SetUniverseSelection(ManualUniverseSelectionModel(symbols))
self.UniverseSettings.Resolution = Resolution.Daily
self.SetPortfolioConstruction(PortfolioManagementModel(self, RET, EXCL, LEV, Resolution.Daily))
self.SetExecution(ScheduledExecutionModel(self))
# self.SetRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(maximumUnrealizedProfitPercent = 0.10))
# self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(maximumDrawdownPercent = 0.075))
# self.SetRiskManagement(MaximumDrawdownPercentPortfolio(maximumDrawdownPercent = 0.05, isTrailing = True))
self.createPlots("SPY")
# if self.simulate_live:
# self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday), \
# self.TimeRules.BeforeMarketClose("SPY", 0), \
# self.AddCash)
# self.Schedule.On(self.DateRules.MonthStart("SPY"), \
# self.TimeRules.BeforeMarketClose("SPY", 0), \
# self.UpKeep)
def AddCash(self):
self.Portfolio.SetCash(self.Portfolio.Cash + self.added_cash)
def UpKeep(self):
self.Portfolio.SetCash(self.Portfolio.Cash - self.upkeep)
def consolidation_handler(self, sender, consolidated):
self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
self.history = self.history.iloc[-(VOLA + 1):]
def createPlots(self, benchmark):
self.__benchmark = benchmark
self.__plot_every_n_days = 5
self.__plot_every_n_days_i = 0
plot = Chart('Performance')
plot.AddSeries(Series(self.__benchmark, SeriesType.Line, 0, '%'))
plot.AddSeries(Series("Algorithm", SeriesType.Line, 0, '%'))
self.AddChart(plot)
self.ResetPlot()
def ResetPlot(self):
self.year = self.Time.year
self.__cost_portfolio = None
self.__cost_benchmark = None
def CalculateBenchmarkPerformance(self):
price = self.Securities[self.__benchmark].Price
if self.__cost_benchmark == None:
self.__cost_benchmark = price
return 100.0 * ((price / self.__cost_benchmark) - 1.0)
def CalculatePortfolioPerformance(self):
if self.__cost_portfolio == None:
self.__cost_portfolio = self.Portfolio.TotalPortfolioValue
return 100.0 * ((self.Portfolio.TotalPortfolioValue / self.__cost_portfolio) - 1.0)
def OnEndOfDay(self):
if self.IsWarmingUp or not self.Securities[self.__benchmark].HasData:
return
if self.Time.year != self.year:
self.ResetPlot()
self.__plot_every_n_days_i == -1
self.__plot_every_n_days_i += 1
if self.__plot_every_n_days_i % self.__plot_every_n_days != 0:
return
#self.Plot('Performance', self.__benchmark, self.CalculateBenchmarkPerformance())
#self.Plot('Performance', "Algorithm", self.CalculatePortfolioPerformance())
def UpdateTickets(self):
invested = [x.Key for x in self.Portfolio if x.Value.Invested]
for symbol in invested:
if self.breakout(symbol):
close = self.Securities[symbol].Close
quantity = self.Portfolio[symbol].Quantity
self.StopMarketOrder(symbol, quantity, close * 0.95)
self.LimitOrder(symbol, int(quantity/2), close * 1.05)
def breakout(self, symbol):
self.lookback = 20
self.ceiling, self.floor = 30, 10
close = self.History(symbol, 31, Resolution.Daily)["close"]
todayvol = np.std(close[1:31])
yesterdayvol = np.std(close[0:30])
deltavol = (todayvol - yesterdayvol) / todayvol
self.lookback = round(self.lookback * (1 + deltavol))
# Account for upper/lower limit of lockback length
if self.lookback > self.ceiling:
self.lookback = self.ceiling
elif self.lookback < self.floor:
self.lookback = self.floor
# List of daily highs
self.high = self.History(symbol, self.lookback, Resolution.Daily)["high"]
# Buy in case of breakout
#if not self.Securities[symbol].Invested and \
if self.Securities[symbol].Close >= max(self.high[:-1]):
return Truefrom System import *
from QuantConnect import *
from sklearn.ensemble import ExtraTreesClassifier, ExtraTreesRegressor
from sklearn.model_selection import train_test_split, cross_validate, cross_val_score, cross_val_predict
import tweepy, statistics
from datetime import datetime, timedelta, date
import numpy as np
import pickle
from scipy import stats
from helpers import myTrailingStopRiskManagementModel
class DualMomentumWithOutDaysAlphaModel(AlphaModel):
def __init__(self, algorithm, VOLA = 126, BASE_RET = 83, resolution = Resolution.Daily, *args, **kwargs):
super().__init__()
self.algorithm = algorithm
self.VOLA = VOLA
self.BASE_RET = BASE_RET
self.resolution = resolution
self.MKT = algorithm.AddEquity('SPY', resolution).Symbol
self.SLV = algorithm.AddEquity('SLV', resolution).Symbol
self.GLD = algorithm.AddEquity('GLD', resolution).Symbol
self.XLI = algorithm.AddEquity('XLI', resolution).Symbol
self.XLU = algorithm.AddEquity('XLU', resolution).Symbol
self.DBB = algorithm.AddEquity('DBB', resolution).Symbol
self.UUP = algorithm.AddEquity('UUP', resolution).Symbol
self.count = self.BASE_RET
self.outday = 5
self.data = dict()
pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
for symbol in pairs:
self.consolidator = TradeBarConsolidator(timedelta(days=1))
self.consolidator.DataConsolidated += self.consolidation_handler
algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
self.history = algorithm.History(pairs, self.VOLA + 1, self.resolution)
# Set TrainingMethod to be executed immediately
#algorithm.Train(self.TrainingMethod)
self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), 1)
resolutionString = Extensions.GetEnumString(resolution, Resolution)
self.Name = f"{self.__class__.__name__}({resolutionString})"
# Force alpha to only produce insights Daily at 11.10am
self.set_flag = False
algorithm.Schedule.On(algorithm.DateRules.EveryDay(),
algorithm.TimeRules.AfterMarketOpen('SPY', 60),
self.SetFlag)
#algorithm.Train(algorithm.DateRules.Every(DayOfWeek.Monday), algorithm.TimeRules.At(1, 0), self.TrainingMethod)
def SetFlag(self):
self.set_flag = True
def consolidation_handler(self, sender, consolidated):
self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close
self.history = self.history.iloc[-(self.VOLA + 1):]
def OnSecuritiesChanged(self, algorithm, changes):
addedSymbols = []
for security in changes.AddedSecurities:
addedSymbols.append(security.Symbol)
if security.Symbol not in self.data:
self.data[security.Symbol] = SymbolData(security.Symbol, algorithm)
if len(addedSymbols) > 0:
history = self.algorithm.History(addedSymbols, self.VOLA + 1, Resolution.Daily).loc[addedSymbols]
for symbol in addedSymbols:
try:
self.data[symbol].Warmup(history.loc[symbol])
except:
self.algorithm.Debug(str(symbol))
continue
def TrainingMethod(self):
self.classifier = ExtraTreesClassifier(n_estimators=500,
criterion = "gini",
min_samples_split = 5,
random_state = 1990)
pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
if self.history is None: return
df = self.algorithm.History(pairs, 252, Resolution.Daily)
# Prepare data
returns = df.unstack(level=1).close.transpose().dropna()
returns['Volatility'] = returns[self.MKT].pct_change().rolling(self.VOLA).std() * np.sqrt(252)
returns["Wait_Days"] = returns['Volatility'].apply(lambda x: self.wait_days(x))
returns["Period"] = returns['Volatility'].apply(lambda x: self.period(x))
returns["Exit_Market"] = returns["Period"].apply(lambda x: self.exit_market(x))
returns = returns.dropna()
y_clf = returns["Exit_Market"].apply(lambda x: 1 if True else 0).values
del returns['Exit_Market']
X = returns
#y_clf = X["Exit_Market"].apply(lambda x: 1 if True else 0).values
X_train, X_test, y_train_clf, y_test_clf = train_test_split(X, y_clf, test_size = 0.2, random_state = 1990)
self.classifier.fit(X_train, y_train_clf)
def Update(self, algorithm, _data):
if algorithm.IsWarmingUp or not self.set_flag:
return []
self.set_flag = False
insights = []
#if self.classifier is None: return
# Volatility
vola = self.history[self.MKT].pct_change().std() * np.sqrt(252)
wait_days = int(vola * self.BASE_RET)
period = int((1.0 - vola) * self.BASE_RET)
r = self.history.pct_change(period).iloc[-1]
exit_market = r[self.SLV] < r[self.GLD] and r[self.XLI] < r[self.XLU] and r[self.DBB] < r[self.UUP]
# pairs = [self.MKT, self.SLV, self.GLD, self.XLI, self.XLU, self.DBB, self.UUP]
# data = self.history[pairs]
# data['Volatility'] = vola
# data['Wait_Days'] = int(vola * self.BASE_RET)
# data['Period'] = int((1.0 - vola) * self.BASE_RET)
# exit_market = self.classifier.predict(data.tail(1))
self.algorithm.Debug(self.data[self.MKT].prices)
direction = InsightDirection.Down
if (exit_market):
#algorithm.Plot("In vs Out", "Market", -1)
direction = InsightDirection.Down
self.outday = self.count
else:
if (self.count >= wait_days + self.outday):
#algorithm.Plot("In vs Out", "Market", 1)
direction = InsightDirection.Up
self.count += 1
algorithm.Plot("Exit", str(self.MKT), int(exit_market))
# algorithm.Plot("Wait Days", "Actual", self.count)
# algorithm.Plot("Wait Days", "Expected", wait_days + self.outday)
insights.append(Insight.Price(self.MKT, self.predictionInterval, direction))
return insights
class SymbolData(object):
def __init__(self, symbol, algorithm):
self.Symbol = symbol
self.VOLA = 126
self.prices = RollingWindow[float](126)
self.prices_window = RollingWindow[float](126)
self.algorithm = algorithm
self.consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily)
# Warm up
history = self.algorithm.History(symbol, 126, Resolution.Daily)
if history.empty or 'close' not in history.columns:
return
for index, row in history.loc[symbol].iterrows():
self.prices_window.Add(row['close'])
if self.prices_window.IsReady:
self.prices = list(self.prices_window)
def wait_days(self, x):
try:
val = int(x * self.BASE_RET)
except:
val = np.nan
return val
def period(self, x):
try:
val = int((1.0 - x) * self.BASE_RET)
except:
val = np.nan
return val
def exit_market(self, x):
try:
x = int(x)
r = self.history.pct_change(x).iloc[-1]
cond1 = r[self.SLV] < r[self.GLD]
cond2 = r[self.XLI] < r[self.XLU]
cond3 = r[self.DBB] < r[self.UUP]
exit_market = cond1 and cond2 and cond3
except:
exit_market = np.nan
return exit_market