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.0
import 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 riskParity
from 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 insights
from 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 True
from 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 = 0
from 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 True
from 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