Overall Statistics |
Total Trades 727 Average Win 0.51% Average Loss -0.49% Compounding Annual Return 27.492% Drawdown 41.100% Expectancy 0.603 Net Profit 110.148% Sharpe Ratio 0.82 Probabilistic Sharpe Ratio 29.263% Loss Rate 22% Win Rate 78% Profit-Loss Ratio 1.05 Alpha 0 Beta 0 Annual Standard Deviation 0.281 Annual Variance 0.079 Information Ratio 0.82 Tracking Error 0.281 Treynor Ratio 0 Total Fees $6483.08 Estimated Strategy Capacity $150000.00 Lowest Capacity Asset OBCI R735QTJ8XC9X |
class ConstantEtfAlphaModel(AlphaModel): # Constant Alpha Model for ETFs def __init__(self, etfTickers): self.Name = 'ETF' self.etfSymbols = [Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in etfTickers] self.nextUpdateTime = datetime.min def Update(self, algorithm, data): insights = [] if algorithm.Time < self.nextUpdateTime: return insights expiry = Expiry.EndOfYear(algorithm.Time) for symbol in self.etfSymbols: if symbol not in data.Bars: continue insights.append(Insight.Price(symbol, expiry, InsightDirection.Up)) if all([algorithm.Portfolio[symbol].TotalSaleVolume > 0 for symbol in self.etfSymbols]): self.nextUpdateTime = expiry return insights def OnSecuritiesChanged(self, algorithm, changes): pass
import operator import numpy as np from collections import namedtuple import functools class MomentumAlphaModel(AlphaModel): def __init__(self, algorithm, numberOfMonthsUntilFullyInvested, maxNumberOfStocksToHold_Momentum, maxNumberOfStocksToHold_Momentum_VolatilityCondition, numberOfTradingDaysInvestedIfInProfit, numberOfTradingDaysInvestedIfInLoss): self.Name = "Momentum" self.symbolDataDict = {} self.nextInsightUpdate = datetime.min self.numberOfMonthsUntilFullyInvested = numberOfMonthsUntilFullyInvested self.maxNumberOfStocksToHold = maxNumberOfStocksToHold_Momentum self.numberOfStocksToAddPerMonth = int(maxNumberOfStocksToHold_Momentum/numberOfMonthsUntilFullyInvested) insightDuration = namedtuple('InsightDuration', 'Profit, Loss') self.insightDuration = insightDuration(Profit=int(365/252*numberOfTradingDaysInvestedIfInProfit), Loss=int(365/252*numberOfTradingDaysInvestedIfInLoss)) self.IsInitRun = True self.monthsSinceStart = 0 self.month = None self.scheduledEvent = None self.insightCollection = InsightCollection() def Update(self, algorithm, data): insights = [] # check if it's time to emit insights if algorithm.Time < self.nextInsightUpdate: return insights if self.month != algorithm.Time.month: self.monthsSinceStart += 1 self.month = algorithm.Time.month # if this is the first run and we're in live mode, we must load the insights from the object store if self.IsInitRun: insightsFromObjectStore = self.LoadInsightsFromObjectStore(algorithm) insights.extend(insightsFromObjectStore) self.monthsSinceStart += len(insightsFromObjectStore) self.IsInitRun = False # check if we can add more holdings for this strategy targetCount = min(24, self.monthsSinceStart*24/12) count = 0 count += len([symbol for symbol, symbolData in self.symbolDataDict.items() if symbolData.Holdings.Invested and symbolData.InsightExpiry >= algorithm.Time]) if count > targetCount -24/12: return insights # create the ranking (Momentum & Kaufman Efficiency Ratio) and select the best N stocks where we're not invested yet; default is N=2 filteredSymbolDataDict = {symbol : symbolData for symbol, symbolData in self.symbolDataDict.items() if (symbol in data.Bars and symbolData.IsReady and symbolData.Momentum > .01 and (not symbolData.Holdings.Invested))} sortedByMomentum = [*map(operator.itemgetter(0), sorted(filteredSymbolDataDict.items(), key = lambda x: x[1].Momentum, reverse=True))] sortedByKER = [*map(operator.itemgetter(0), sorted(filteredSymbolDataDict.items(), key = lambda x: x[1].KER ))] totalRank = {symbol : sortedByMomentum.index(symbol) + sortedByKER.index(symbol) for symbol, _ in filteredSymbolDataDict.items()} sortedByTotalRank = sorted(totalRank.items(), key = lambda x: x[1]) selection = [x[0] for x in sortedByTotalRank if not algorithm.Portfolio[x[0]].Invested][:self.numberOfStocksToAddPerMonth] if not selection: return insights # create insights for the selected stocks for symbol in selection: symbolData = self.symbolDataDict[symbol] if not algorithm.Portfolio[symbol].Invested: symbolData.InsightExpiry = algorithm.Time + timedelta(days=min(self.insightDuration)) insights.append(Insight.Price(symbol, symbolData.InsightExpiry, InsightDirection.Up)) count += 1 else: if algorithm.Time > symbolData.InsightExpiry: # we want to extend the insight duration if we are in profit if symbolData.Holdings.UnrealizedProfit > 0: td = timedelta(days=self.insightDuration.Profit - self.insightDuration.Loss) if td.days > 0: symbolData.InsightExpiry = algorithm.Time + td insights.append(Insight.Price(symbol, symbolData.InsightExpiry, InsightDirection.Up)) continue else: insights.append(Insight.Price(symbol, timedelta(1), InsightDirection.Flat)) count -= 1 # adjust the time for nextInsightUpdate to avoid unnecessary computations if count >= targetCount: self.nextInsightUpdate = Expiry.EndOfMonth(algorithm.Time) - timedelta(days=1) self.insightCollection.AddRange(insights) return insights def OnSecuritiesChanged(self, algorithm, changes): # tracks the changes in our universe of securities if self.scheduledEvent is None: self.scheduledEvent = algorithm.Schedule.On(algorithm.DateRules.MonthEnd(), algorithm.TimeRules.Midnight, functools.partial(self.PersistInsightCollection, algorithm) ) for security in changes.RemovedSecurities: symbolData = self.symbolDataDict.pop(security.Symbol, None) if symbolData is not None: symbolData.Dispose(algorithm) for security in changes.AddedSecurities: if not security.IsTradable or security.Fundamentals is None: continue symbol = security.Symbol if symbol not in self.symbolDataDict: self.symbolDataDict[symbol] = SymbolData(algorithm, security) def PersistInsightCollection(self, algorithm): # stores all active insights from this alpha model in an object store if not algorithm.LiveMode: return activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime) insightDict = {insight.Id : [insight.Symbol, insight.Period, insight.Direction, insight.SourceModel, insight.GeneratedTimeUtc, insight.CloseTimeUtc] for insight in activeInsights} df = pd.DataFrame(insightDict.values(), index=insightDict.keys(), columns=['Symbol','Period','Direction','SourceModel','GeneratedTimeUtc','CloseTimeUtc']) algorithm.ObjectStore.Save(self.Name + "InsightCollection", df.to_json(orient='split', default_handler = str)) def LoadInsightsFromObjectStore(self, algorithm): # loads all insights from the object store if we are in live mode # ensures we track the insight timings correctly between redeployments if not algorithm.LiveMode: return [] insights = [] insightCollection = InsightCollection() key = self.Name + "InsightCollection" if not algorithm.ObjectStore.ContainsKey(key): return insights storedInsightsDf = pd.read_json(algorithm.ObjectStore.Read(key), orient='split') storedInsightsDf.loc[:, 'Period'] = pd.to_timedelta(storedInsightsDf.Period, unit='ms') storedInsightsDf.loc[:, 'GeneratedTimeUtc'] = pd.to_datetime(storedInsightsDf.GeneratedTimeUtc, unit='ms', utc=True) storedInsightsDf.loc[:, 'CloseTimeUtc'] = pd.to_datetime(storedInsightsDf.CloseTimeUtc, unit='ms', utc=True) filteredInsightsDf = storedInsightsDf.loc[(storedInsightsDf.SourceModel.eq(self.Name) & storedInsightsDf.CloseTimeUtc.gt(algorithm.UtcTime) & storedInsightsDf.Direction.eq(1) & storedInsightsDf.GeneratedTimeUtc.lt(algorithm.UtcTime))] for row in filteredInsightsDf.itertuples(): symbol = SymbolCache.GetSymbol(row.Symbol) if symbol not in self.symbolDataDict: if symbol not in algorithm.ActiveSecurities.Keys: continue security = algorithm.Securities[symbol] if not (security.IsTradable and symbol in algorithm.CurrentSlice.Bars): continue self.symbolDataDict[symbol] = SymbolData(algorithm, security) self.symbolDataDict[symbol].InsightExpiry = row.CloseTimeUtc.to_pydatetime() insight = Insight.Price(symbol, row.CloseTimeUtc.to_pydatetime(), row.Direction) insights.append(insight) return insights class SymbolData: def __init__(self, algorithm, security): self.algorithm = algorithm self.Security = security self.Symbol = security.Symbol self.Holdings = security.Holdings self.InsightExpiry = datetime.min self.SetupIndicators(algorithm) def SetupIndicators(self, algorithm): resolution = Resolution.Daily period = self.Security.Exchange.TradingDaysPerYear delay_period = int(period/12) self.momp = MomentumPercent(period) self.momentum = IndicatorExtensions.Of(Delay(delay_period), self.momp) algorithm.RegisterIndicator(self.Symbol, self.momp, resolution) algorithm.WarmUpIndicator(self.Symbol, self.momp, resolution) self.ker = KaufmanEfficiencyRatio(period) self.ker = IndicatorExtensions.Of(Delay(delay_period), self.ker) algorithm.RegisterIndicator(self.Symbol, self.ker, resolution) algorithm.WarmUpIndicator(self.Symbol, self.ker, resolution) self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution) def Dispose(self, algorithm): algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.Consolidator) @property def Momentum(self): return self.momentum.Current.Value @property def KER(self): return self.ker.Current.Value @property def IsReady(self): return self.momp.IsReady and self.ker.IsReady
from collections import namedtuple import operator import functools class ValueAlphaModel(AlphaModel): def __init__(self, algorithm, numberOfMonthsUntilFullyInvested, maxNumberOfStocksToHold_Value, maxNumberOfStocksToHold_Value_VolatilityCondition, min_ROA, min_PE_Ratio, numberOfTradingDaysInvestedIfInProfit, numberOfTradingDaysInvestedIfInLoss): self.Name = "Value" self.symbolDataDict = {} self.nextUpdateTime = datetime.min self.numberOfMonthsUntilFullyInvested = numberOfMonthsUntilFullyInvested self.maxNumberOfStocksToHold = maxNumberOfStocksToHold_Value self.maxNumberOfStocksToHold_Value_VolatilityCondition = maxNumberOfStocksToHold_Value_VolatilityCondition self.numberOfStocksToAddPerMonth = int(maxNumberOfStocksToHold_Value/numberOfMonthsUntilFullyInvested) self.min_ROA = min_ROA self.min_PE_Ratio = min_PE_Ratio insightDuration = namedtuple('InsightDuration', 'Profit, Loss') self.insightDuration = insightDuration(Profit=int(365/252*numberOfTradingDaysInvestedIfInProfit), Loss=int(365/252*numberOfTradingDaysInvestedIfInLoss)) self.monthsSinceStart = 0 self.month = None self.IsInitRun = True self.scheduledEvent = None self.insightCollection = InsightCollection() self.pcm = algorithm.pcm self.riskModel = None def Update(self, algorithm, data): insights = [] # check if it's time to emit insights if algorithm.Time < self.nextUpdateTime: return insights if self.month != algorithm.Time.month: self.monthsSinceStart += 1 self.month = algorithm.Time.month # if this is the first run and we're in live mode, we must load the insights from the object store if self.IsInitRun: insightsFromObjectStore = self.LoadInsightsFromObjectStore(algorithm) insights.extend(insightsFromObjectStore) self.monthsSinceStart += len(insightsFromObjectStore) self.IsInitRun = False targetCount = min(self.maxNumberOfStocksToHold, self.monthsSinceStart*self.numberOfStocksToAddPerMonth) # adjust the target holdings count if volatility condition is met if self.riskModel is not None and self.riskModel.SafeMode: targetCount = self.riskModel.NumberOfStocksBySourceModel[self.Name] count = len([symbol for symbol, symbolData in self.symbolDataDict.items() if symbolData.Holdings.Invested and symbolData.InsightExpiry >= algorithm.Time]) # check if we can add more holdings for this strategy if count > targetCount - self.numberOfStocksToAddPerMonth: return insights # create ranking for this strategy (based on EVtoEBIT) filteredSymbolDataDict = {symbol : symbolData for symbol, symbolData in self.symbolDataDict.items() if (symbol in data.Bars and symbolData.ROA > self.min_ROA and symbolData.PE_Ratio > self.min_PE_Ratio and (not symbolData.Holdings.Invested))} sortedByEVToEBIT = sorted(filteredSymbolDataDict.items(), key = lambda x: x[1].EVtoEBIT) selection = [*map(operator.itemgetter(0), sortedByEVToEBIT)][:self.numberOfStocksToAddPerMonth] if not selection: return insights # create insights for the selected stocks for symbol in selection: symbolData = self.symbolDataDict[symbol] if not algorithm.Portfolio[symbol].Invested: symbolData.InsightTime = algorithm.Time symbolData.InsightExpiry = algorithm.Time + timedelta(days=min(self.insightDuration)) insights.append(Insight.Price(symbol, symbolData.InsightExpiry, InsightDirection.Up)) count +=1 else: if algorithm.Time >= symbolData.InsightExpiry - timedelta(1): if symbolData.Holdings.UnrealizedProfit > 0 or self.riskModel.SafeMode: td = timedelta(days=self.insightDuration.Profit - self.insightDuration.Loss) if td.days > 0: symbolData.InsightExpiry = algorithm.Time + td insights.append(Insight.Price(symbol, symbolData.InsightExpiry, InsightDirection.Up)) continue else: insights.append(Insight.Price(symbol, timedelta(1), InsightDirection.Flat)) count -= 1 # adjust the time for nextInsightUpdate to avoid unnecessary computations if count >= targetCount: self.nextInsightUpdate = Expiry.EndOfMonth(algorithm.Time) - timedelta(days=1) return insights def OnSecuritiesChanged(self, algorithm, changes): if self.riskModel is None and self.pcm.riskModel is not None: self.riskModel = self.pcm.riskModel if self.scheduledEvent is None: self.scheduledEvent = algorithm.Schedule.On(algorithm.DateRules.MonthEnd(), algorithm.TimeRules.Midnight, functools.partial(self.PersistInsightCollection, algorithm) ) for security in changes.RemovedSecurities: symbolData = self.symbolDataDict.pop(security.Symbol, None) if symbolData is not None: symbolData.Dispose(algorithm) for security in changes.AddedSecurities: if not security.IsTradable or security.Fundamentals is None: continue symbol = security.Symbol if symbol not in self.symbolDataDict: self.symbolDataDict[symbol] = SymbolData(algorithm, security) def PersistInsightCollection(self, algorithm): # stores all active insights from this alpha model in an object store if not algorithm.LiveMode: return activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime) insightDict = {insight.Id : [insight.Symbol, insight.Period, insight.Direction, insight.SourceModel, insight.GeneratedTimeUtc, insight.CloseTimeUtc] for insight in activeInsights} df = pd.DataFrame(insightDict.values(), index=insightDict.keys(), columns=['Symbol','Period','Direction','SourceModel','GeneratedTimeUtc','CloseTimeUtc']) algorithm.ObjectStore.Save(self.Name + "InsightCollection", df.to_json(orient='split', default_handler = str)) def LoadInsightsFromObjectStore(self, algorithm): if not algorithm.LiveMode: return [] insights = [] insightCollection = InsightCollection() key = self.Name + "InsightCollection" if not algorithm.ObjectStore.ContainsKey(key): return [] storedInsightsDf = pd.read_json(algorithm.ObjectStore.Read(key), orient='split') storedInsightsDf.loc[:, 'Period'] = pd.to_timedelta(storedInsightsDf.Period, unit='ms') storedInsightsDf.loc[:, 'GeneratedTimeUtc'] = pd.to_datetime(storedInsightsDf.GeneratedTimeUtc, unit='ms', utc=True) storedInsightsDf.loc[:, 'CloseTimeUtc'] = pd.to_datetime(storedInsightsDf.CloseTimeUtc, unit='ms', utc=True) filteredInsightsDf = storedInsightsDf.loc[(storedInsightsDf.SourceModel.eq(self.Name) & storedInsightsDf.CloseTimeUtc.gt(algorithm.UtcTime) & storedInsightsDf.Direction.eq(1) & storedInsightsDf.GeneratedTimeUtc.lt(algorithm.UtcTime))] for row in filteredInsightsDf.itertuples(): symbol = SymbolCache.GetSymbol(row.Symbol) if symbol not in self.symbolDataDict: security = algorithm.Securities[symbol] if not (security.IsTradable and symbol in algorithm.CurrentSlice.Bars): continue self.symbolDataDict[symbol] = SymbolData(algorithm, security) self.symbolDataDict[symbol].InsightExpiry = row.CloseTimeUtc.to_pydatetime() insight = Insight.Price(symbol, row.CloseTimeUtc.to_pydatetime(), row.Direction) insights.append(insight) return insights class SymbolData: def __init__(self, algorithm, security): self.algorithm = algorithm self.Security = security self.Symbol = security.Symbol self.Holdings = security.Holdings self.InsightExpiry = datetime.min self.InsightTime = datetime.min self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, Resolution.Daily) def Dispose(self, algorithm): algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.Consolidator) @property def ROA(self): return self.Security.Fundamentals.OperationRatios.ROA.OneYear @property def PE_Ratio(self): return self.Security.Fundamentals.ValuationRatios.PERatio @property def EVtoEBIT(self): return self.Security.Fundamentals.ValuationRatios.EVtoEBIT class RiskModel: def __init__(self, algorithm, security, momentumBudget, momentumBudgetVolCond, valueBudget, valueBudgetVolCond, riskManagementEnabled, maxDrawdown, maxVIX, numberOfTradingDaysBeforeReset): self.algorithm = algorithm self.Security = security self.Symbol = security.Symbol self.portfolioHigh = algorithm.Portfolio.TotalPortfolioValue self.SafeMode = False self.safeModeExpiryTime = datetime.min self.momentumBudget = momentumBudget self.momentumBudgetDefault = momentumBudget self.momentumBudgetVolCond = momentumBudgetVolCond self.valueBudget = valueBudget self.valueBudgetDefault = valueBudget self.valueBudgetVolCond = valueBudgetVolCond self.riskManagementEnabled = riskManagementEnabled self.maxDrawdown = -abs(maxDrawdown) self.maxVIX = maxVIX self.numberOfTradingDaysBeforeReset = timedelta(days=numberOfTradingDaysBeforeReset) self.budgetBySourceModel = defaultdict(float) self.budgetBySourceModel.update({'Momentum':momentumBudget, 'Value':valueBudget}) self.hour = None algorithm.Schedule.On(algorithm.DateRules.EveryDay(self.Symbol), algorithm.TimeRules.Every(timedelta(hours=1)), self.Update) @property def BudgetBySourceModel(self): if not self.riskManagementEnabled: return self.budgetBySourceModel self.budgetBySourceModel.update({"Momentum":self.momentumBudget, "Value":self.valueBudget}) return self.budgetBySourceModel def Update(self): if not self.SafeMode and self.algorithm.Time > self.safeModeExpiryTime: self.momentumBudget = self.momentumBudgetDefault self.valueBudget = self.valueBudgetDefault self.SafeMode = self.Security.Price > self.maxVIX and self.CurrentDrawdown < self.maxDrawdown if self.SafeMode: self.momentumBudget = self.momentumBudgetVolCond self.valueBudget = self.valueBudgetVolCond self.algorithm.Debug(f"{self.algorithm.Time} | ACHTUNG !!! Volatility-Condition erfüllt!") else: if self.SafeMode and self.Security.Price < self.maxVIX and self.CurrentDrawdown > self.maxDrawdown: self.safeModeExpiryTime = self.algorithm.Time + self.numberOfTradingDaysBeforeReset self.SafeMode = False self.algorithm.Debug(f"{self.algorithm.Time} | Volatility-Condition wieder aufgehoben!") self.algorithm.Debug(f"Safe-Mode verfällt am {self.safeModeExpiryTime}.") @property def CurrentDrawdown(self): currentTPV = self.algorithm.Portfolio.TotalPortfolioValue if currentTPV > self.portfolioHigh: self.portfolioHigh = currentTPV return currentTPV/self.portfolioHigh - 1
############ # def GetLatestLivePortfolioStateFromObjectStore(self, algorithm): # if not ( self.IsInitRun and algorithm.ObjectStore.ContainsKey('PortfolioState')): #algorithm.LiveMode and ## <--- wieder hinzufügen!!! # return [] # portfolioState = pd.read_json(algorithm.ObjectStore.Read('PortfolioState')) # if portfolioState is None or portfolioState.empty: # return [] # portfolioState.loc[:,'Entry'] = pd.to_datetime(portfolioState.Entry.clip(lower=0).replace(0, np.nan), unit='ms') # portfolioState.loc[:, 'Exit'] = pd.to_datetime(portfolioState.Exit.clip(lower=0).replace(0, np.nan), unit='ms') # previousLiveHoldings = portfolioState.loc[portfolioState.Exit.isnull()].index.map(SymbolCache.GetSymbol).tolist() # self.IsInitRun = False # return previousLiveHoldings # self.positionChangesDict = defaultdict(PortfolioSnapshot) # self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(0,0), self.UpdateCharts) # def UpdateCharts(self): # numHoldings = len([holding for _, holding in self.Portfolio.items() if holding.Invested]) # self.Plot('Portfolio Holdings', 'Number of Holdings', numHoldings) # def OnOrderEvent(self, orderEvent): # # if not self.LiveMode: # # return # if orderEvent.Status == OrderStatus.Filled: # symbol = orderEvent.Symbol # if not self.Portfolio[symbol].Invested and symbol in self.positionChangesDict: # # das heißt es handelt sich hierbei um einen Exit # self.positionChangesDict.update({symbol : self.positionChangesDict[symbol]._replace(Exit = self.Time, Quantity = 0)}) # return # self.positionChangesDict.update({symbol : self.positionChangesDict[symbol]._replace(Entry = self.Time, Quantity = orderEvent.Quantity)}) # def OnEndOfAlgorithm(self): # if not self.LiveMode: # return # for key in list([x.Key for x in self.ObjectStore.GetEnumerator()]): # if key.endswith('InsightCollection'): # self.ObjectStore.Delete(key) # hier die positionChangedDict im ObjectStore speichern, falls LiveMode # if not self.LiveMode: # return # if self.ObjectStore.ContainsKey('PortfolioState'): # self.ObjectStore.Delete('PortfolioState') # self.ObjectStore.Save('PortfolioState', pd.DataFrame(self.positionChangesDict.values(), index=self.positionChangesDict.keys()).to_json(default_handler=str)) ############# # def GetObjectStoreSymbols(self, algorithm): # SymbolsFromObjectStore(self, algorithm): # if not (self.IsInitRun and algorithm.ObjectStore.ContainsKey('PortfolioState')): # return 0 # portfolioState = pd.read_json(algorithm.ObjectStore.Read('PortfolioState')) # if portfolioState is None or portfolioState.empty: # return 0 # portfolioState.loc[:, 'Entry'] = pd.to_datetime(portfolioState.Entry.clip(lower=0).replace(0, np.nan), unit='ms') # portfolioState.loc[:, 'Exit'] = pd.to_datetime(portfolioState.Exit.clip(lower=0).replace(0, np.nan), unit='ms') # latestPortfolioState = portfolioState.loc[portfolioState.Exit.isnull() & portfolioState.Quantity.gt(0)] \ # .index.map(SymbolCache.GetSymbol).tolist() # # .apply(lambda x: PortfolioSnapshot(Entry = x.Entry, Exit = x.Exit, Quantity = x.Quantity), axis=1) \ # # .rename(lambda x: SymbolCache.GetSymbol(x)) \ # # .to_dict() # self.IsInitRun = False # return latestPortfolioState # return len([symbol for symbol in latestPortfolioState if algorithm.Portfolio[symbol].Invested]) ##################### ######### # currentTPV = self.algorithm.Portfolio.TotalPortfolioValue # if currentTPV > self.portfolioHigh: # self.portfolioHigh = currentTPV # drawdown = currentTPV/self.portfolioHigh - 1 # # targets.extend(self.CreateTargetsForLivePortfolioStateRecovery(algorithm)) # if not self.latestLivePortfolioState: # self.latestLivePortfolioState = self.GetLatestLivePortfolioStateFromObjectStore(algorithm) # for symbol, portfolioSnapshot in list(self.latestLivePortfolioState.items()): # if algorithm.Time - portfolioSnapshot.Entry > timedelta(days=265): # target = PortfolioTarget(symbol, 0) # targets.append(target) # del self.latestLivePortfolioState[symbol] # # self.latestLivePortfolioState.pop(symbol) # continue # else: # if not algorithm.Portfolio[symbol].Invested: # target = PortfolioTarget(symbol, portfolioSnapshot.Quantity) # residualBuyingPower -= abs(target.Quantity*algorithm.Securities[symbol].AskPrice) # targets.append(target) # # if algorithm.Portfolio[symbol].Invested and (algorithm.Time - portfolioSnapshot.Entry < timedelta(days=265)): # # continue # # if not algorithm.Portfolio[symbol].Invested and (algorithm.Time - portfolioSnapshot.Entry < timedelta(days=265)): # # target = PortfolioTarget(symbol, portfolioSnapshot.Quantity) # # residualBuyingPower -= abs(target.Quantity*algorithm.Securities[symbol].Price) # # if target is None: # # self.symbolsToIgnore.add(symbol) # # continue # # targets.append(target) ### # def GetLatestLivePortfolioStateFromObjectStore(self, algorithm): # if not ( self.IsInitRun and algorithm.ObjectStore.ContainsKey('PortfolioState')): #algorithm.LiveMode and # return {} # portfolioState = pd.read_json(algorithm.ObjectStore.Read('PortfolioState')) # if portfolioState is None or portfolioState.empty: # return {} # portfolioState.loc[:, 'Entry'] = pd.to_datetime(portfolioState.Entry.clip(lower=0).replace(0, np.nan), unit='ms') # portfolioState.loc[:, 'Exit'] = pd.to_datetime(portfolioState.Exit.clip(lower=0).replace(0, np.nan), unit='ms') # # previousLiveHoldings = portfolioState.loc[portfolioState.Exit.isnull()].index.map(SymbolCache.GetSymbol).tolist() # # previousLiveHoldings = portfolioState.Quantity.rename(lambda x: SymbolCache.GetSymbol(x)).to_dict() # # exit = null means there wasn't an exit yet, i.e. we have an active position for that security # latestPortfolioState = portfolioState.loc[portfolioState.Exit.isnull() & portfolioState.Quantity.gt(0)] \ # .apply(lambda x: PortfolioSnapshot(Entry = x.Entry, Exit = x.Exit, Quantity = x.Quantity), axis=1) \ # .rename(lambda x: SymbolCache.GetSymbol(x)) \ # .to_dict() # # algorithm.ObjectStore.Delete('PortfolioState') # self.IsInitRun = False # return latestPortfolioState ############ # def GetObjectStoreRelatedSymbols(self, algorithm): # SymbolsFromObjectStore(self, algorithm): # if not (self.IsInitRun and algorithm.ObjectStore.ContainsKey('PortfolioState')): # LIVE_MODE ! # return 0 # portfolioState = pd.read_json(algorithm.ObjectStore.Read('PortfolioState')) # if portfolioState is None or portfolioState.empty: # return 0 # portfolioState.loc[:, 'Entry'] = pd.to_datetime(portfolioState.Entry.clip(lower=0).replace(0, np.nan), unit='ms') # portfolioState.loc[:, 'Exit'] = pd.to_datetime(portfolioState.Exit.clip(lower=0).replace(0, np.nan), unit='ms') # latestPortfolioState = portfolioState.loc[portfolioState.Exit.isnull() & portfolioState.Quantity.gt(0)] \ # .index.map(SymbolCache.GetSymbol).tolist() # # .apply(lambda x: PortfolioSnapshot(Entry = x.Entry, Exit = x.Exit, Quantity = x.Quantity), axis=1) \ # # .rename(lambda x: SymbolCache.GetSymbol(x)) \ # # .to_dict() # self.IsInitRun = False # return latestPortfolioState # # CHECK = len([symbol for symbol in latestPortfolioState if algorithm.Portfolio[symbol].Invested]) ###DEBUG # # return len([symbol for symbol in latestPortfolioState if algorithm.Portfolio[symbol].Invested])
import math from helper_tools import sign class CustomExecutionModel(ExecutionModel): def __init__(self): self.targetCollection = PortfolioTargetCollection() self.tol = .1 def Execute(self, algorithm, targets): self.targetCollection.AddRange(targets) if self.targetCollection.Count > 0: for target in self.targetCollection.OrderByMarginImpact(algorithm): orderQuantity = OrderSizing.GetUnorderedQuantity(algorithm, target) if orderQuantity == 0: continue symbol = target.Symbol security = algorithm.Securities[symbol] holding = algorithm.Portfolio[symbol] if security.IsTradable: marginRemaining = algorithm.Portfolio.MarginRemaining # we only need to check if the order would increase the margin used if holding.Quantity*orderQuantity >= 0 and marginRemaining < security.Price*abs(orderQuantity): buyingPower = min(1, security.Leverage)*marginRemaining*(1 - self.tol) orderQuantity = min(math.floor(buyingPower/security.Price), abs(orderQuantity))*sign(orderQuantity) if orderQuantity != 0: algorithm.MarketOrder(symbol, orderQuantity) self.targetCollection.ClearFulfilled(algorithm)
from collections import defaultdict, namedtuple PortfolioSnapshot = namedtuple('PortfolioSnapshot', 'Entry, Exit, Quantity') PortfolioSnapshot.__new__.__defaults__ = (datetime.min, )*(len(PortfolioSnapshot._fields) - 1) + (0, ) # faster alternative for itertools.groupby def GroupBy(iterable, key = lambda x: x): d = defaultdict(list) for item in iterable: d[key(item)].append(item) return d.items() def sign(x): return 1 if x > 0 else(-1 if x < 0 else 0)
from helper_tools import PortfolioSnapshot from datetime import datetime, timedelta, timezone from collections import namedtuple, defaultdict # ---------------------------------------------------------- # Import the Framework Modules for this strategy from selection import CustomUniverseSelectionModel from alpha_mom import MomentumAlphaModel from alpha_value import ValueAlphaModel from alpha_etf import ConstantEtfAlphaModel from portfolio import MultiSourcesPortfolioConstructionModel from execution import CustomExecutionModel # ---------------------------------------------------------- class MomentumValueStrategy(QCAlgorithm): def Initialize(self): ######################################################################## # -------------------------------------------------------------------- # Modifiable code block # -------------------------------------------------------------------- ######################################################################## # General Settings # -------------------- self.SetStartDate(2019, 1, 1) # self.SetEndDate(2021, 2, 1) self.SetCash(1_000_000) numberOfSymbolsCoarse = 4000 numberOfSymbolsFine = 500 numberOfMonthsUntilFullyInvested = 12 numberOfTradingDaysInvestedIfInProfit = 265 numberOfTradingDaysInvestedIfInLoss = 245 self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage) self.UniverseSettings.Resolution = Resolution.Daily self.UniverseSettings.Leverage = 1 self.Settings.FreePortfolioValuePercentage = 0.025 self.DebugMode = False # Momentum Strategy Params # ------------------------- maxNumberOfStocksToHold_Momentum = 24 maxNumberOfStocksToHold_Momentum_VolatilityCondition = 0 minMomentumPercent = 0.01 maxBudget_Momentum = 0.5 maxBudget_Momentum_VolatilityCondition = 0 # Value Strategy Params # ---------------------- maxNumberOfStocksToHold_Value = 24 maxNumberOfStocksToHold_Value_VolatilityCondition = 48 min_ROA = 0.15 min_PE_Ratio = 5 maxBudget_Value = 0.5 maxBudget_Value_VolatilityCondition = 1 # Risk Management / Volatility Condition Params # ---------------------------------------------- riskManagementEnabled = False maxDrawdown = 0.2 maxVIX = 35 numberOfTradingDaysBeforeReset = 265 # ETF Investing Params # ------------------------ etfTickers = ["XSVM", "RFV"] minPortfolioBufferToEnableEtfInvesting = 0.12 ######################################################################## # ---------------------------------------------------------------------- # End of modifiable code block # ---------------------------------------------------------------------- ######################################################################## self.pcm = MultiSourcesPortfolioConstructionModel(maxBudget_Momentum, maxBudget_Momentum_VolatilityCondition, maxNumberOfStocksToHold_Momentum, maxNumberOfStocksToHold_Momentum_VolatilityCondition, maxBudget_Value, maxBudget_Value_VolatilityCondition, maxNumberOfStocksToHold_Value, maxNumberOfStocksToHold_Value_VolatilityCondition, numberOfTradingDaysBeforeReset, riskManagementEnabled, maxDrawdown, maxVIX, minPortfolioBufferToEnableEtfInvesting) self.AddUniverseSelection(CustomUniverseSelectionModel(numberOfSymbolsCoarse, numberOfSymbolsFine, etfTickers)) self.AddAlpha(MomentumAlphaModel(self, numberOfMonthsUntilFullyInvested, maxNumberOfStocksToHold_Momentum, maxNumberOfStocksToHold_Momentum_VolatilityCondition, numberOfTradingDaysInvestedIfInProfit, numberOfTradingDaysInvestedIfInLoss)) self.AddAlpha(ValueAlphaModel(self, numberOfMonthsUntilFullyInvested, maxNumberOfStocksToHold_Value, maxNumberOfStocksToHold_Value_VolatilityCondition, min_ROA, min_PE_Ratio, numberOfTradingDaysInvestedIfInProfit, numberOfTradingDaysInvestedIfInLoss)) self.AddAlpha(ConstantEtfAlphaModel(etfTickers)) self.SetPortfolioConstruction(self.pcm) self.SetExecution(CustomExecutionModel())
from collections import defaultdict from datetime import datetime, timedelta, timezone import math import functools from helper_tools import GroupBy, PortfolioSnapshot class MultiSourcesPortfolioConstructionModel(PortfolioConstructionModel): def __init__(self, momentumBudget, momentumBudgetVolCond, momentumNumHoldings, momentumNumHoldingsVolCond, valueBudget, valueBudgetVolCond, valueNumHoldings, valueNumHoldingsVolCond, numberOfTradingDaysBeforeReset, riskManagementEnabled, maxDrawdown, maxVIX, minPortfolioBufferToEnableEtfInvesting): self.insightCollection = InsightCollection() self.removedSymbols = [] self.nextExpiryTime = datetime.min.replace(tzinfo=timezone.utc) self.updateFreq = timedelta(1) self.symbolsToIgnore = set() self.riskModel = None self.vix = Symbol.Create("VIX", SecurityType.Index, Market.USA) self.momentumBudget = momentumBudget self.momentumBudgetVolCond = momentumBudgetVolCond self.numberOfStocksMomentum = momentumNumHoldings self.numberOfStocksMomentumVolCond = momentumNumHoldingsVolCond self.valueBudget = valueBudget self.valueBudgetVolCond = valueBudgetVolCond self.numberOfStocksValue = valueNumHoldings self.numberOfStocksValueVolCond = valueNumHoldingsVolCond self.numberOfTradingDaysBeforeReset = numberOfTradingDaysBeforeReset self.riskManagementEnabled = riskManagementEnabled self.maxDrawdown = maxDrawdown self.maxVIX = maxVIX self.etfInvestingEnabled = True self.minPortfolioBufferToEnableEtfInvesting = minPortfolioBufferToEnableEtfInvesting self.scheduledEvent = None self.IsInitRun = True self.prevRiskMode = False def CreateTargets(self, algorithm, insights): targets = [] if not self.CanUpdateTargets(algorithm, insights): return targets self.insightCollection.AddRange(insights) targets.extend(self.CreateFlatTargetsForRemovedSecurities()) targets.extend(self.CreateFlatTargetsForExpiredInsights(algorithm)) lastActiveInsights = self.GetLastActiveInsights(algorithm) if self.ShouldUpdateTargets(algorithm, lastActiveInsights): targets.extend(self.UpdatePortfolioTargets(algorithm, lastActiveInsights)) self.UpdateNextExpiryTime(algorithm) return targets def CanUpdateTargets(self, algorithm, insights): return len(insights) > 0 or algorithm.UtcTime > self.nextExpiryTime def CreateFlatTargetsForRemovedSecurities(self): if len(self.removedSymbols) == 0: return [] flatTargets = [PortfolioTarget(symbol, 0) for symbol in self.removedSymbols] self.insightCollection.Clear(self.removedSymbols) self.removedSymbols = [] return flatTargets def CreateFlatTargetsForExpiredInsights(self, algorithm): flatTargets = [] expiredInsights = self.insightCollection.RemoveExpiredInsights(algorithm.UtcTime) if len(expiredInsights) == 0: return flatTargets for symbol, _ in GroupBy(expiredInsights, key = lambda insight: insight.Symbol): if not self.insightCollection.HasActiveInsights(symbol, algorithm.UtcTime): flatTargets.append(PortfolioTarget(symbol, 0)) continue return flatTargets def GetLastActiveInsights(self, algorithm): activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime) lastActiveInsights = [] for k,g in GroupBy(activeInsights, key = lambda insight: (insight.Symbol, insight.SourceModel)): lastActiveInsights.append(sorted(g, key = lambda insight: insight.GeneratedTimeUtc)[-1]) return lastActiveInsights def ShouldUpdateTargets(self, algorithm, lastActiveInsights): if algorithm.UtcTime > self.nextExpiryTime: return True for insight in lastActiveInsights: if insight.Symbol in self.symbolsToIgnore: continue holding = algorithm.Portfolio[insight.Symbol] if insight.Direction != InsightDirection.Flat and (not holding.Invested): return True if insight.Direction == InsightDirection.Up and (not holding.IsLong): return True if insight.Direction == InsightDirection.Down and (not holding.IsShort): return True if insight.Direction == InsightDirection.Flat and holding.Invested: return True else: continue return False def UpdateNextExpiryTime(self, algorithm): self.nextExpiryTime = self.insightCollection.GetNextExpiryTime() if self.nextExpiryTime is None: self.nextExpiryTime = algorithm.UtcTime + self.updateFreq self.nextExpiryTime = min(self.nextExpiryTime, algorithm.UtcTime + self.updateFreq) def UpdatePortfolioTargets(self, algorithm, lastActiveInsights): targets = [] residualBuyingPower = algorithm.Portfolio.MarginRemaining/algorithm.Portfolio.TotalPortfolioValue - algorithm.Settings.FreePortfolioValuePercentage # create PortfolioTargets for Momentum and Value Alpha Model Insights, taking into account Volatility Condition for insight in lastActiveInsights: if (insight.SourceModel == 'ETF') or (insight.Symbol in self.symbolsToIgnore): continue if (self.prevRiskMode == self.riskModel.SafeMode) and (algorithm.Portfolio[insight.Symbol].Invested) and (insight.Direction != InsightDirection.Flat): continue count = self.riskModel.NumberOfStocksBySourceModel[insight.SourceModel] budget = self.riskModel.BudgetBySourceModel[insight.SourceModel] if count*budget == 0: target = PortfolioTarget(insight.Symbol, 0) targets.append(target) continue weight = budget * insight.Direction / count target = PortfolioTarget.Percent(algorithm, insight.Symbol, weight) if target is None: self.symbolsToIgnore.add(insight.Symbol) continue targets.append(target) residualBuyingPower -= abs(weight) self.prevRiskMode = self.riskModel.SafeMode # create portfolio targets for ETFs, if necessary if self.etfInvestingEnabled: etfInsightSymbols = [insight.Symbol for insight in lastActiveInsights if insight.SourceModel == 'ETF' and insight.Direction != InsightDirection.Flat] if len(etfInsightSymbols) == 0: return targets currentEtfAllocation = sum([holding.AbsoluteHoldingsValue for symbol,holding in algorithm.Portfolio.items() if symbol in etfInsightSymbols])/algorithm.Portfolio.TotalPortfolioValue if (currentEtfAllocation + residualBuyingPower > self.minPortfolioBufferToEnableEtfInvesting): etfWeight = (currentEtfAllocation + residualBuyingPower - algorithm.Settings.FreePortfolioValuePercentage)/len(etfInsightSymbols) else: etfWeight = 0 self.etfInvestingEnabled = False for insight in lastActiveInsights: if insight.SourceModel == 'ETF' and insight.Symbol not in self.symbolsToIgnore: target = PortfolioTarget.Percent(algorithm, insight.Symbol, etfWeight*insight.Direction) if target is None: self.symbolsToIgnore.add(insight.Symbol) continue targets.append(target) return targets def OnSecuritiesChanged(self, algorithm, changes): if self.scheduledEvent is None: self.scheduledEvent = algorithm.Schedule.On(algorithm.DateRules.EveryDay(), algorithm.TimeRules.Midnight, functools.partial(self.UpdateCharts, algorithm)) for security in changes.RemovedSecurities: symbol = security.Symbol if symbol.Equals(self.vix): continue self.removedSymbols.append(symbol) if symbol in self.symbolsToIgnore: self.symbolsToIgnore.remove(symbol) for security in changes.AddedSecurities: if security.Symbol.Equals(self.vix) and self.riskModel is None: self.riskModel = RiskModel(algorithm, security, self.momentumBudget, self.momentumBudgetVolCond, self.numberOfStocksMomentum, self.numberOfStocksMomentumVolCond, self.valueBudget, self.valueBudgetVolCond, self.numberOfStocksValue, self.numberOfStocksValueVolCond, self.riskManagementEnabled, self.maxDrawdown, self.maxVIX, self.numberOfTradingDaysBeforeReset) def UpdateCharts(self, algorithm): activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime) # lastActiveInsights = [] holdingsCountBySourceModel = defaultdict(int) for k,g in GroupBy(activeInsights, key = lambda insight: insight.SourceModel): holdingsCountBySourceModel[k] = len({x.Symbol for x in g if x.Direction != InsightDirection.Flat and algorithm.Portfolio[x.Symbol].Invested}) algorithm.Plot('Portfolio Holdings', k, holdingsCountBySourceModel[k]) totalCount = len([holding for _,holding in algorithm.Portfolio.items() if holding.Invested]) algorithm.Plot('Portfolio Holdings', 'Total', totalCount) class RiskModel: def __init__(self, algorithm, security, momentumBudget, momentumBudgetVolCond, numberOfStocksMomentum, numberOfStocksMomentumVolCond, valueBudget, valueBudgetVolCond, numberOfStocksValue, numberOfStocksValueVolCond, riskManagementEnabled, maxDrawdown, maxVIX, numberOfTradingDaysBeforeReset): self.algorithm = algorithm self.Security = security self.Symbol = security.Symbol self.portfolioHigh = algorithm.Portfolio.TotalPortfolioValue self.SafeMode = False self.safeModeExpiryTime = datetime.min self.momentumBudget = momentumBudget self.momentumBudgetDefault = momentumBudget self.momentumBudgetVolCond = momentumBudgetVolCond self.numberOfStocksMomentum = numberOfStocksMomentum self.numberOfStocksMomentumDefault = numberOfStocksMomentum self.numberOfStocksMomentumVolCond = numberOfStocksMomentumVolCond self.valueBudget = valueBudget self.valueBudgetDefault = valueBudget self.valueBudgetVolCond = valueBudgetVolCond self.numberOfStocksValue = numberOfStocksValue self.numberOfStocksValueDefault = numberOfStocksValue self.numberOfStocksValueVolCond = numberOfStocksValueVolCond self.numberOfStocksBySourceModel = {'Momentum': numberOfStocksMomentum, 'Value': numberOfStocksValue} self.riskManagementEnabled = riskManagementEnabled self.maxDrawdown = -abs(maxDrawdown) self.maxVIX = maxVIX self.numberOfTradingDaysBeforeReset = timedelta(days=numberOfTradingDaysBeforeReset) self.budgetBySourceModel = defaultdict(float) self.budgetBySourceModel.update({'Momentum':momentumBudget, 'Value':valueBudget}) self.hour = None algorithm.Schedule.On(algorithm.DateRules.EveryDay(self.Symbol), algorithm.TimeRules.Every(timedelta(hours=1)), self.Update) @property def BudgetBySourceModel(self): if not self.riskManagementEnabled: return self.budgetBySourceModel self.budgetBySourceModel.update({"Momentum":self.momentumBudget, "Value":self.valueBudget}) return self.budgetBySourceModel @property def NumberOfStocksBySourceModel(self): if not self.riskManagementEnabled: return self.numberOfStocksBySourceModel self.numberOfStocksBySourceModel.update({'Momentum':self.numberOfStocksMomentum, 'Value':self.numberOfStocksValue}) return self.numberOfStocksBySourceModel def Update(self): if not self.SafeMode and self.algorithm.Time > self.safeModeExpiryTime: self.momentumBudget = self.momentumBudgetDefault self.valueBudget = self.valueBudgetDefault self.numberOfStocksMomentum = self.numberOfStocksMomentumDefault self.numberOfStocksValue = self.numberOfStocksValueDefault self.SafeMode = self.Security.Price > self.maxVIX and self.CurrentDrawdown < self.maxDrawdown if self.SafeMode: self.momentumBudget = self.momentumBudgetVolCond self.valueBudget = self.valueBudgetVolCond self.numberOfStocksMomentum = self.numberOfStocksMomentumVolCond self.numberOfStocksValue = self.numberOfStocksValueVolCond self.algorithm.Debug(f"{self.algorithm.Time} | Volatility Condition triggered!\nAdjusting Portfolio Targets and permitted Number of Stocks for each Alpha Model.") else: if self.SafeMode and self.Security.Price < self.maxVIX and self.CurrentDrawdown > self.maxDrawdown: self.safeModeExpiryTime = self.algorithm.Time + self.numberOfTradingDaysBeforeReset self.SafeMode = False self.algorithm.Debug(f"{self.algorithm.Time} | Volatility Condition removed!\nKeep Risk Management Settings until {self.safeModeExpiryTime}.") # self.algorithm.Debug(f"Safe-Mode verfällt am {self.safeModeExpiryTime}.") @property def CurrentDrawdown(self): currentTPV = self.algorithm.Portfolio.TotalPortfolioValue if currentTPV > self.portfolioHigh: self.portfolioHigh = currentTPV return currentTPV/self.portfolioHigh - 1
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel class CustomUniverseSelectionModel(FundamentalUniverseSelectionModel): def __init__(self, maxNumberOfSymbolsCoarse, maxNumberOfSymbolsFine, etfTickers): self.nextSelectionUpdate = datetime.min self.maxNumberOfSymbolsCoarse = maxNumberOfSymbolsCoarse self.maxNumberOfSymbolsFine = maxNumberOfSymbolsFine self.etfSymbols = [Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in etfTickers] self.vix = Symbol.Create("VIX", SecurityType.Index, Market.USA) self.IsInitRun = True super().__init__(True, None) def SelectCoarse(self, algorithm, coarse): if algorithm.Time < self.nextSelectionUpdate: return Universe.Unchanged tpv = algorithm.Portfolio.TotalPortfolioValue coarseFiltered = [c for c in coarse if (c.HasFundamentalData and (5 < c.Price < tpv/100) and (algorithm.Time - c.Symbol.ID.Date).days > 365)] sortedByDollarVolume = sorted(coarseFiltered, key = lambda c: c.DollarVolume, reverse = False) return [c.Symbol for c in sortedByDollarVolume][:self.maxNumberOfSymbolsCoarse] def SelectFine(self, algorithm, fine): filteredFine = [f for f in fine if (f.CompanyReference.CountryId == "USA" and f.CompanyReference.PrimaryExchangeID in {"NYS", "NAS"} and (algorithm.Time - f.SecurityReference.IPODate).days > 180 and f.CompanyReference.IndustryTemplateCode not in {"U", "I", "B"} and f.MarketCap > 5e7 )] sortedByMarketCap = sorted(filteredFine, key = lambda f: f.MarketCap, reverse=False) self.nextSelectionUpdate = Expiry.EndOfMonth(algorithm.Time) - timedelta(days=1) selection = [f.Symbol for f in sortedByMarketCap][:self.maxNumberOfSymbolsFine] + self.etfSymbols + [self.vix] + self.CurrentHoldings(algorithm) selection.extend(self.LoadSymbolsFromObjectStore(algorithm)) return selection def LoadSymbolsFromObjectStore(self, algorithm): # load symbols from the object store which insights haven't expired yet to ensure we have a data feed for those symbols if not (algorithm.LiveMode and self.IsInitRun): return [] suffix = 'InsightCollection' keys = [x.Key for x in algorithm.ObjectStore if x.Key.endswith(suffix)] dfList = [] symbols = [] for key in keys: storedInsightsDf = pd.read_json(algorithm.ObjectStore.Read(key), orient='split') storedInsightsDf.loc[:, 'Period'] = pd.to_timedelta(storedInsightsDf.Period, unit='ms') storedInsightsDf.loc[:, 'GeneratedTimeUtc'] = pd.to_datetime(storedInsightsDf.GeneratedTimeUtc, unit='ms', utc=True) storedInsightsDf.loc[:, 'CloseTimeUtc'] = pd.to_datetime(storedInsightsDf.CloseTimeUtc, unit='ms', utc=True) filteredInsightsDf = storedInsightsDf.loc[(storedInsightsDf.SourceModel.eq(key.split(suffix)[0]) & storedInsightsDf.CloseTimeUtc.gt(algorithm.UtcTime) & storedInsightsDf.Direction.eq(1) & storedInsightsDf.GeneratedTimeUtc.lt(algorithm.UtcTime))] if not filteredInsightsDf.empty: dfList.append(filteredInsightsDf) if len(dfList) > 0: df = pd.concat(dfList) symbols = df.Symbol.apply(SymbolCache.GetSymbol).unique().tolist() self.IsInitRun = False return symbols def CurrentHoldings(self, algorithm): return [security.Key for security in algorithm.ActiveSecurities if security.Value.Holdings.Invested]