def RunPriceMomentum(tickerList:list, startDate:str='1/1/1982', durationInYears:int=36, stockCount:int=9, ReEvaluationInterval:int=20, filterOption:int=3, longHistory:int=365, shortHistory:int=90, minPercentGain=0.05, maxVolatility=.12, portfolioSize:int=30000, returndailyValues:bool=False, verbose:bool=False): #Choose stockCount stocks with the greatest long term (longHistory days) price appreciation, using different filter options defined in the StockPicker class #shortHistory is a shorter time frame (like 90 days) used differently by different filters #ReEvaluationInterval is how often to re-evaluate our choices, ideally this should be very short and not matter, otherwise the date selection is biased. startDate = ToDate(startDate) endDate = AddDays(startDate, 365 * durationInYears) picker = StockPicker(AddDays(startDate, -730), endDate) #Include earlier dates for statistics for t in tickerList: picker.AddTicker(t) tm = TradingModel(modelName='PriceMomentumShort_longHistory_' + str(longHistory) +'_shortHistory_' + str(shortHistory) + '_reeval_' + str(ReEvaluationInterval) + '_stockcount_' + str(stockCount) + '_filter' + str(filterOption) + '_' + str(minPercentGain) + str(maxVolatility), startingTicker='^SPX', startDate=startDate, durationInYears=durationInYears, totalFunds=portfolioSize, tranchSize=portfolioSize/stockCount, verbose=verbose) dayCounter = 0 if not tm.modelReady: print('Unable to initialize price history for PriceMomentum date ' + str(startDate)) return 0 else: while not tm.ModelCompleted(): currentDate = tm.currentDate if dayCounter ==0: print('\n') print(currentDate) c, a = tm.Value() print(tm.modelName, int(c), int(a), int(c+a)) print('available/buy/sell/long',tm.PositionSummary()) candidates = picker.GetHighestPriceMomentum(currentDate, longHistoryDays=longHistory, shortHistoryDays=shortHistory, stocksToReturn=stockCount, filterOption=filterOption, minPercentGain=minPercentGain, maxVolatility=maxVolatility) AlignPositions(tm=tm, targetPositions=candidates, stockCount=stockCount, allocateByPointValue=False) tm.ProcessDay() dayCounter+=1 if dayCounter >= ReEvaluationInterval: dayCounter=0 cv1 = tm.CloseModel(plotResults=False, saveHistoryToFile=((durationInYears>1) or verbose)) if returndailyValues: return tm.GetDailyValue() else: return cv1
def RunModel(modelName: str, modelFunction, ticker: str, startDate: str, durationInYears: int, portfolioSize: int, saveHistoryToFile: bool = True, returndailyValues: bool = False, verbose: bool = False): #Performs the logic of the given model over a period of time to evaluate the performance modelName = modelName + '_' + ticker print('Running model ' + modelName) tm = TradingModel(modelName=modelName, startingTicker=ticker, startDate=startDate, durationInYears=durationInYears, totalFunds=portfolioSize, tranchSize=round(portfolioSize / 10), verbose=verbose) if not tm.modelReady: print('Unable to initialize price history for model for ' + str(startDate)) if returndailyValues: return pd.DataFrame() else: return portfolioSize else: while not tm.ModelCompleted(): modelFunction(tm, ticker) tm.ProcessDay() if tm.AccountingError(): print( 'Accounting error. The numbers do not add up correctly. Terminating model run.', tm.currentDate) tm.PositionSummary() #tm.PrintPositions() break cash, asset = tm.Value() #print('Ending Value: ', cash + asset, '(Cash', cash, ', Asset', asset, ')') tradeCount = len(tm.tradeHistory) RecordPerformance(ModelName=modelName, StartDate=startDate, EndDate=tm.currentDate, StartValue=portfolioSize, EndValue=(cash + asset), TradeCount=tradeCount) if returndailyValues: tm.CloseModel(verbose, saveHistoryToFile) return tm.GetDailyValue( ) #return daily value for model comparisons else: return tm.CloseModel( verbose, saveHistoryToFile ) #return simple closing value to view net effect
def RunBuyHold(ticker: str, startDate:str, durationInYears:int, ReEvaluationInterval:int=20, portfolioSize:int=30000, verbose:bool=False): #Baseline model to compare against. Buy on day one, hold for the duration and then sell modelName = 'BuyHold_' + (ticker) + '_' + startDate[-4:] tm = TradingModel(modelName=modelName, startingTicker=ticker, startDate=startDate, durationInYears=durationInYears, totalFunds=portfolioSize, tranchSize=portfolioSize/10, verbose=verbose) if not tm.modelReady: print('Unable to initialize price history for model BuyHold date ' + str(startDate)) return 0 else: dayCounter =0 while not tm.ModelCompleted(): if dayCounter ==0: i=0 while tm.TranchesAvailable() and i < 100: tm.PlaceBuy(ticker=ticker, price=1, marketOrder=True, expireAfterDays=10, verbose=verbose) i +=1 dayCounter+=1 if dayCounter >= ReEvaluationInterval: dayCounter=0 tm.ProcessDay() cash, asset = tm.Value() print('Ending Value: ', cash + asset, '(Cash', cash, ', Asset', asset, ')') return tm.CloseModel(plotResults=False, saveHistoryToFile=verbose)
def RunPriceMomentum(tickerList: list, startDate: str = '1/1/1982', durationInYears: int = 36, stockCount: int = 9, ReEvaluationInterval: int = 20, filterOption: int = 3, longHistory: int = 365, shortHistory: int = 90, minPercentGain=0.05, maxVolatility=.12, portfolioSize: int = 30000, returndailyValues: bool = False, verbose: bool = False): #Choose the stock with the greatest long term (longHistory days) price appreciation #shortHistory is a shorter time frame (like 90 days) used differently by different filters #ReEvaluationInterval is how often to re-evaluate our choices, ideally this should be very short and not matter, otherwise the date selection is biased. startDate = datetime.datetime.strptime(startDate, '%m/%d/%Y') endDate = startDate + datetime.timedelta(days=365 * durationInYears) picker = StockPicker(startDate, endDate) for t in tickerList: picker.AddTicker(t) tm = TradingModel(modelName='PriceMomentumShort_longHistory_' + str(longHistory) + '_shortHistory_' + str(shortHistory) + '_reeval_' + str(ReEvaluationInterval) + '_stockcount_' + str(stockCount) + '_filter' + str(filterOption) + '_' + str(minPercentGain) + str(maxVolatility), startingTicker='^SPX', startDate=startDate, durationInYears=durationInYears, totalFunds=portfolioSize, tranchSize=portfolioSize / stockCount, verbose=verbose) dayCounter = 0 if not tm.modelReady: print('Unable to initialize price history for PriceMomentum date ' + str(startDate)) return 0 else: while not tm.ModelCompleted(): currentDate = tm.currentDate if dayCounter == 0: print('\n') print(currentDate) c, a = tm.Value() print(tm.modelName, int(c), int(a), int(c + a)) print('available/buy/sell/long', tm.PositionSummary()) tm.SellAllPositions(currentDate) tm.ProcessDay() dayCounter += 1 shortList = picker.GetHighestPriceMomentum( currentDate, longHistoryDays=longHistory, shortHistoryDays=shortHistory, stocksToReturn=stockCount, filterOption=filterOption, minPercentGain=minPercentGain, maxVolatility=maxVolatility) shortList = shortList[:stockCount] print(shortList) if len(shortList) > 0: i = 0 ii = 0 while tm.TranchesAvailable( ) and i < 100: #Over long periods 100 will not be enough, 1000 would be better, but 100 also limits the impact of early gains, after a bit you are trading $300K tm.PlaceBuy(ticker=shortList.index[ii], price=1, marketOrder=True, expireAfterDays=10, verbose=verbose) i += 1 ii += 1 if ii >= len(shortList): ii = 0 tm.ProcessDay() dayCounter += 1 if dayCounter >= ReEvaluationInterval: dayCounter = 0 cv1 = tm.CloseModel(plotResults=False, saveHistoryToFile=((durationInYears > 1) or verbose)) if returndailyValues: return tm.GetDailyValue() else: return cv1
def RunPriceMomentumBlended(tickerList: list, startDate: str = '1/1/1980', durationInYears: int = 29, stockCount: int = 9, ReEvaluationInterval: int = 20, longHistory: int = 365, shortHistory: int = 90, portfolioSize: int = 30000, returndailyValues: bool = False, verbose: bool = False): #Uses blended option for selecting stocks using three different filters startDate = datetime.datetime.strptime(startDate, '%m/%d/%Y') endDate = startDate + datetime.timedelta(days=365 * durationInYears) picker = StockPicker(startDate, endDate) for t in tickerList: picker.AddTicker(t) tm = TradingModel(modelName='PriceMomentum_Blended_longHistory_' + str(longHistory) + '_shortHistory_' + str(shortHistory) + '_reeval_' + str(ReEvaluationInterval) + '_stockcount_' + str(stockCount) + '_filterBlended_134', startingTicker='^SPX', startDate=startDate, durationInYears=durationInYears, totalFunds=portfolioSize, tranchSize=portfolioSize / stockCount, verbose=verbose) dayCounter = 0 if not tm.modelReady: print('Unable to initialize price history for PriceMomentum date ' + str(startDate)) return 0 else: while not tm.ModelCompleted(): currentDate = tm.currentDate if dayCounter == 0: print('\n') print(currentDate) c, a = tm.Value() print(tm.modelName, int(c), int(a), int(c + a)) print('available/buy/sell/long', tm.PositionSummary()) tm.SellAllPositions(currentDate) tm.ProcessDay() dayCounter += 1 shortList1 = picker.GetHighestPriceMomentum( currentDate, longHistoryDays=longHistory, shortHistoryDays=shortHistory, stocksToReturn=int(stockCount / 3), filterOption=1) shortList2 = picker.GetHighestPriceMomentum( currentDate, longHistoryDays=longHistory, shortHistoryDays=shortHistory, stocksToReturn=int(stockCount / 3), filterOption=2) shortList3 = picker.GetHighestPriceMomentum( currentDate, longHistoryDays=longHistory, shortHistoryDays=shortHistory, stocksToReturn=int(stockCount / 3), filterOption=4) #shortList3 = picker.GetHighestPriceMomentum(currentDate, longHistoryDays=longHistory, shortHistoryDays=shortHistory, stocksToReturn=int(stockCount/3), filterOption=6, maxVolatility=.12) shortList = pd.concat([shortList1, shortList2, shortList3]) shortList print(shortList) if len(shortList) > 0: i = 0 ii = 0 while tm.TranchesAvailable() and i < 100: tm.PlaceBuy(ticker=shortList.index[ii], price=1, marketOrder=True, expireAfterDays=10, verbose=verbose) i += 1 ii += 1 if ii >= len(shortList): ii = 0 tm.ProcessDay() dayCounter += 1 if dayCounter >= ReEvaluationInterval: dayCounter = 0 cv1 = tm.CloseModel(plotResults=False, saveHistoryToFile=((durationInYears > 1) or verbose)) if returndailyValues: return tm.GetDailyValue() else: return cv1