def RunTradingModelBuyHold(tm: TradingModel, ticker: str): currentPrices = tm.GetPriceSnapshot() if tm.verbose: print(currentPrices.snapShotDate, currentPrices.nextDayTarget) if not currentPrices == None: if tm.TranchesAvailable() > 0 and tm.FundsAvailable( ) > currentPrices.high: tm.PlaceBuy(ticker, currentPrices.low, True)
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 RunTradingModelBuyHold(tm: TradingModel, ticker: str): #Baseline model, buy and hold currentPrices = tm.GetPriceSnapshot() if tm.verbose: print(currentPrices.snapShotDate, currentPrices.nextDayTarget) if not currentPrices == None: for i in range(tm._tranchCount): available, buyPending, sellPending, longPositions = tm.PositionSummary( ) if tm.TranchesAvailable() > 0 and tm.FundsAvailable( ) > currentPrices.high: tm.PlaceBuy(ticker, currentPrices.low, True) if available == 0: break
def AlignPositions(tm:TradingModel, targetPositions:pd.DataFrame, stockCount:int, allocateByPointValue:bool= False, verbose:bool = False): #Helper function. Performs necessary Buy/Sells to get from current positions to target positions print(targetPositions) TotalTranches = tm._tranchCount targetPositions=pd.DataFrame(targetPositions.groupby(['Ticker','pointValue']).size()).reset_index() targetPositions.set_index(['Ticker'], inplace=True) targetPositions.rename(columns={0:'TargetHoldings'}, inplace=True) targetPositions.sort_values(by=['TargetHoldings', 'pointValue'], axis=0, ascending=False, inplace=True, kind='quicksort', na_position='last') targetPositions = targetPositions[:stockCount] print(targetPositions) if allocateByPointValue: TotalPoints = targetPositions['pointValue'].sum() scale = TotalTranches/TotalPoints print('scale', TotalPoints, TotalTranches, scale) targetPositions.loc[:, 'TargetHoldings'] = round(targetPositions.loc[:, 'TargetHoldings'] * targetPositions.loc[:, 'pointValue'] * scale) else: TotalTargets = targetPositions['TargetHoldings'].sum() scale = TotalTranches/TotalTargets print('scale', TotalTargets, TotalTranches, scale) targetPositions.loc[:, 'TargetHoldings'] = round(targetPositions.loc[:, 'TargetHoldings'] * scale) print(targetPositions) currentPositions = tm.GetPositions(asDataFrame=True) if len(currentPositions) > 0: #evaluate the difference between current holdings and target, act accordingly targetPositions = targetPositions.join(currentPositions, how='outer') targetPositions.fillna(value=0, inplace=True) targetPositions['Difference'] = targetPositions['TargetHoldings'] - targetPositions['CurrentHoldings'] print(targetPositions) for i in range(len(targetPositions)): sells = int(targetPositions.iloc[i]['Difference']) if sells < 0: t = targetPositions.index.values[i] print('Sell ' + str(abs(sells)) + ' ' + t) for _ in range(abs(sells)): tm.PlaceSell(ticker=t, price=1, marketOrder=True, expireAfterDays=10, verbose=verbose) tm.ProcessDay(withIncrement=False) for i in range(len(targetPositions)): buys = int(targetPositions.iloc[i]['Difference']) if buys > 0: t = targetPositions.index.values[i] print('Buy ' + str(buys) + ' ' + t) for _ in range(buys): tm.PlaceBuy(ticker=t, price=1, marketOrder=True, expireAfterDays=10, verbose=verbose) tm.ProcessDay(withIncrement=False) elif len(targetPositions) > 0: for i in range(len(targetPositions)): buys = int(targetPositions.iloc[i]['TargetHoldings']) if buys > 0: t = targetPositions.index.values[i] print(t) print(buys) print('Buy ' + str(buys) + ' ' + t) for _ in range(buys): tm.PlaceBuy(ticker=t, price=1, marketOrder=True, expireAfterDays=10, verbose=verbose) tm.ProcessDay(withIncrement=False) print(tm.GetPositions(asDataFrame=True)) print(tm.PositionSummary())
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, totalFunds: int, saveHistoryToFile: bool = True, returndailyValues: bool = False, verbose: bool = False): modelName = modelName + '_' + ticker tm = TradingModel(modelName=modelName, startingTicker=ticker, startDate=startDate, durationInYears=durationInYears, totalFunds=totalFunds, verbose=verbose) if not tm.modelReady: print('Unable to initialize price history for model for ' + str(startDate)) if returndailyValues: return pandas.DataFrame() else: return totalFunds else: while not tm.ModelCompleted(): tm.ProcessDay() modelFunction(tm, ticker) if tm.AccountingError(): print( 'Accounting error. The numbers do not add up correctly.') break if returndailyValues: tm.CloseModel(verbose, saveHistoryToFile) return tm.GetDailyValue() #return daily value else: return tm.CloseModel(verbose, saveHistoryToFile) #return closing value
def RunTradingModelQLearn(ticker: str, startDate: str, durationInYears: int, totalFunds: int, verbose: bool = False, saveHistoryToFile: bool = True, returndailyValues: bool = False): Actions = [ 'Hold', 'BuyMarket', 'BuyAgressiveLeve10', 'BuyAgressiveLeve11', 'BuyAgressiveLeve12', 'SellMarket', 'SellAgressiveLeve10', 'SellAgressiveLeve11', 'SellAgressiveLeve12' ] modelName = 'QLearn' + '_' + ticker tm = TradingModel(modelName, ticker, startDate, durationInYears, totalFunds, verbose) if not tm.modelReady: print('Unable to initialize price history for model for ' + str(startDate)) if returndailyValues: return pandas.DataFrame() else: return totalFunds else: while not tm.ModelCompleted(): tm.ProcessDay() currentPrices = tm.GetPriceSnapshot() if not currentPrices == None: pass #do stuff if returndailyValues: tm.CloseModel(verbose, saveHistoryToFile) return tm.GetdailyValue() #return daily value else: return tm.CloseModel(verbose, saveHistoryToFile) #return closing value
def RunTradingModelSeasonal(tm: TradingModel, ticker: str): SellMonth = 4 #April BuyMonth = 10 #October currentPrices = tm.GetPriceSnapshot() if not currentPrices == None: low = currentPrices.low high = currentPrices.high m = tm.currentDate.month available, buyPending, sellPending, longPositions = tm.PositionSummary( ) if m >= SellMonth and m <= BuyMonth: if longPositions > 0: tm.PlaceSell(ticker, high, True) else: if available > 0 and tm.FundsAvailable() > high: tm.PlaceBuy(ticker, low, True)
def RunTradingModelSeasonal(tm: TradingModel, ticker: str): #Buy in November, sell in May SellMonth = 5 BuyMonth = 11 currentPrices = tm.GetPriceSnapshot() if not currentPrices == None: low = currentPrices.low high = currentPrices.high m = tm.currentDate.month for i in range(tm._tranchCount): available, buyPending, sellPending, longPositions = tm.PositionSummary( ) if m >= SellMonth and m <= BuyMonth: if longPositions > 0: tm.PlaceSell(ticker, high, True) else: break else: if available > 0 and tm.FundsAvailable() > high: tm.PlaceBuy(ticker, low, True) else: break
def RunTradingModelFirstHalfOfMonth(tm: TradingModel, ticker: str): #From Robert Ariel's observations, most gains are in the first half of the month BuyDay = 25 #Buy at the end of the month, after the 25th SellDay = 15 #Sell mid month, after the 15th currentPrices = tm.GetPriceSnapshot() if not currentPrices == None: low = currentPrices.low high = currentPrices.high d = tm.currentDate.day for i in range(tm._tranchCount): available, buyPending, sellPending, longPositions = tm.PositionSummary( ) if d >= BuyDay or d < 3: if available > 0 and tm.FundsAvailable() > high: tm.PlaceBuy(ticker, low, True) else: break elif d >= SellDay: if longPositions > 0: tm.PlaceSell(ticker, high, True) else: break else: break
def RunModel(modelName: str, modelFunction, ticker: str, startDate: str, durationInYears: int, portfolioSize: int, saveHistoryToFile: bool = True, returndailyValues: bool = False, verbose: bool = False): modelName = modelName + '_' + ticker tm = TradingModel(modelName=modelName, startingTicker=ticker, startDate=startDate, durationInYears=durationInYears, totalFunds=portfolioSize, 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(): tm.ProcessDay() modelFunction(tm, ticker) if tm.AccountingError(): print( 'Accounting error. The numbers do not add up correctly. Terminating model run.' ) tm.PositionSummary() #tm.PrintPositions() break 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: datetime, durationInYears: int, ReEvaluationInterval: int = 20, portfolioSize: int = 30000, verbose: bool = False): 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: tm.SellAllPositions(tm.currentDate) 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 RunTradingModelBuyHold(ticker: str, startDate: str, durationInYears: int, totalFunds: int, verbose: bool = False, saveHistoryToFile: bool = True, returndailyValues: bool = False): modelName = 'BuyHold' + '_' + ticker tm = TradingModel(modelName, ticker, startDate, durationInYears, totalFunds, verbose) if not tm.modelReady: print('Unable to initialize price history for model for ' + str(startDate)) if returndailyValues: return pandas.DataFrame() else: return totalFunds else: while not tm.ModelCompleted(): tm.ProcessDay() currentPrices = tm.GetPriceSnapshot() if not currentPrices == None: if tm.TraunchesAvailable( ) and tm.FundsAvailable() > currentPrices.high: tm.PlaceBuy(ticker, currentPrices.low, True) if tm.AccountingError(): break if returndailyValues: tm.CloseModel(verbose, saveHistoryToFile) return tm.GetdailyValue() #return daily value else: return tm.CloseModel(verbose, saveHistoryToFile) #return closing value
def RunTradingModelSwingTrend(ticker: str, startDate: str, durationInYears: int, totalFunds: int, verbose: bool = False, saveHistoryToFile: bool = True, returndailyValues: bool = False): #Give it a date range, some money, and a stock, it will execute a strategy and return the results #minDeviationToTrade = .025 minActionableSlope = 0.002 trendState = 'Flat' #++,--,+-,-+,Flat prevTrendState = '' trendDuration = 0 modelName = 'SwingTrend' + '_' + ticker tm = TradingModel(modelName, ticker, startDate, durationInYears, totalFunds, verbose) if not tm.modelReady: print('Unable to initialize price history for model for ' + str(startDate)) if returndailyValues: return pandas.DataFrame() else: return totalFunds else: while not tm.ModelCompleted(): tm.ProcessDay() p = tm.GetPriceSnapshot() if not p == None: available, buyPending, sellPending, longPositions = tm.GetPositionSummary( ) maxPositions = available + buyPending + sellPending + longPositions targetBuy = p.nextDayTarget * (1 + p.fiveDayDeviation / 2) targetSell = p.nextDayTarget * (1 - p.fiveDayDeviation / 2) if p.longEMASlope >= minActionableSlope and p.shortEMASlope >= minActionableSlope: #++ Positive trend, 70% long trendState = '++' if p.low > p.channelHigh: #Over Bought if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell * (1.03), False, 10) elif p.low < p.channelLow: #Still early if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy, True) if trendDuration > 1 and buyPending < 3: tm.PlaceBuy(ticker, targetBuy, True) else: if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy, False) if buyPending < 5 and longPositions < maxPositions: tm.PlaceBuy(ticker, targetBuy, False) elif p.longEMASlope >= minActionableSlope and p.shortEMASlope < minActionableSlope: #+- Correction or early downturn trendState = '+-' if p.low > p.channelHigh: #Over Bought, try to get out if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 3) elif p.low < p.channelLow and p.high > p.channelLow: #Deep correction if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 3) else: pass elif p.longEMASlope < -minActionableSlope and p.shortEMASlope < -minActionableSlope: #-- Negative trend, aim for < 30% long trendState = '--' if p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy * .95, False, 2) elif p.low < p.channelLow and p.high > p.channelLow: #Straddle Low, early down or up pass else: if sellPending < 5 and longPositions > 3: tm.PlaceSell(ticker, targetSell, True) if trendDuration > 1: tm.PlaceSell(ticker, targetSell, True) if sellPending < 5 and longPositions > 3: tm.PlaceSell(ticker, targetSell, False, 2) tm.PlaceSell(ticker, targetSell, False, 2) elif p.longEMASlope < ( -1 * minActionableSlope) and p.shortEMASlope < ( -1 * minActionableSlope): #-+ Bounce or early recovery trendState = '-+' if p.high < p.channelLow: #Over sold pass elif p.low < p.channelLow and p.high > p.channelLow: #Straddle Low if sellPending < 3 and longPositions > 3: tm.PlaceSell(ticker, targetSell, False, 3) else: pass else: #flat, aim for 70% long trendState = 'Flat' if p.low > p.channelHigh: #Over Bought if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell * (1.03), False, 10) elif p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 8: tm.PlaceBuy(ticker, targetBuy, False, 5) if buyPending < 4: tm.PlaceBuy(ticker, targetBuy, False, 5) else: pass if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 5) if buyPending < 3 and longPositions < maxPositions: tm.PlaceBuy(ticker, targetBuy, False, 5) if trendState == prevTrendState: trendDuration = trendDuration + 1 else: trendDuration = 0 if tm.AccountingError(): break if returndailyValues: tm.CloseModel(verbose, saveHistoryToFile) return tm.GetdailyValue() #return daily value else: return tm.CloseModel(verbose, saveHistoryToFile) #return closing value
def RunTradingModelSeasonal(ticker: str, startDate: str, durationInYears: int, totalFunds: int, verbose: bool = False, saveHistoryToFile: bool = True, returndailyValues: bool = False): modelName = 'Seasonal' + '_' + ticker tm = TradingModel(modelName, ticker, startDate, durationInYears, totalFunds, verbose) if not tm.modelReady: print('Unable to initialize price history for model for ' + str(startDate)) if returndailyValues: return pandas.DataFrame() else: return totalFunds else: while not tm.ModelCompleted(): tm.ProcessDay() currentPrices = tm.GetPriceSnapshot() if not currentPrices == None: low = currentPrices.low high = currentPrices.high m = tm.currentDate.month available, buyPending, sellPending, longPositions = tm.GetPositionSummary( ) if m >= 11 or m <= 4: #Buy if Nov through April, else sell if available > 0 and tm.FundsAvailable() > high: tm.PlaceBuy(ticker, low, True) else: if longPositions > 0: tm.PlaceSell(ticker, high, True) if tm.AccountingError(): break if returndailyValues: tm.CloseModel(verbose, saveHistoryToFile) return tm.GetdailyValue() #return daily value else: return tm.CloseModel(verbose, saveHistoryToFile) #return closing value
def RunTradingModelSwingTrend(tm: TradingModel, ticker: str): #Combines trending model with targeted "swing" buys, attempting to gain better deals by anticipating daily price fluctuations minActionableSlope = 0.002 prevTrendState, trendDuration = tm.GetCustomValues() if prevTrendState == None: prevTrendState = '' if trendDuration == None: trendDuration = 0 p = tm.GetPriceSnapshot() if not p == None: for i in range(tm._tranchCount): available, buyPending, sellPending, longPositions = tm.PositionSummary( ) maxPositions = available + buyPending + sellPending + longPositions targetBuy = p.nextDayTarget * (1 + p.fiveDayDeviation / 2) targetSell = p.nextDayTarget * (1 - p.fiveDayDeviation / 2) if p.longEMASlope >= minActionableSlope and p.shortEMASlope >= minActionableSlope: #++ Positive trend, 70% long trendState = '++' if p.low > p.channelHigh: #Over Bought if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell * (1.03), False, 10) elif p.low < p.channelLow: #Still early if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy, True) if trendDuration > 1 and buyPending < 3: tm.PlaceBuy(ticker, targetBuy, True) else: if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy, False) if buyPending < 5 and longPositions < maxPositions: tm.PlaceBuy(ticker, targetBuy, False) elif p.longEMASlope >= minActionableSlope and p.shortEMASlope < minActionableSlope: #+- Correction or early downturn trendState = '+-' if p.low > p.channelHigh: #Over Bought, try to get out if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 3) elif p.low < p.channelLow and p.high > p.channelLow: #Deep correction if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 3) else: pass elif p.longEMASlope < -minActionableSlope and p.shortEMASlope < -minActionableSlope: #-- Negative trend, aim for < 30% long trendState = '--' if p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy * .95, False, 2) elif p.low < p.channelLow and p.high > p.channelLow: #Straddle Low, early down or up pass else: if sellPending < 5 and longPositions > 3: tm.PlaceSell(ticker, targetSell, True) if trendDuration > 1: tm.PlaceSell(ticker, targetSell, True) if sellPending < 5 and longPositions > 3: tm.PlaceSell(ticker, targetSell, False, 2) tm.PlaceSell(ticker, targetSell, False, 2) elif p.longEMASlope < ( -1 * minActionableSlope) and p.shortEMASlope < ( -1 * minActionableSlope): #-+ Bounce or early recovery trendState = '-+' if p.high < p.channelLow: #Over sold pass elif p.low < p.channelLow and p.high > p.channelLow: #Straddle Low if sellPending < 3 and longPositions > 3: tm.PlaceSell(ticker, targetSell, False, 3) else: pass else: #flat, aim for 70% long trendState = 'Flat' if p.low > p.channelHigh: #Over Bought if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell * (1.03), False, 10) elif p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 8: tm.PlaceBuy(ticker, targetBuy, False, 5) if buyPending < 4: tm.PlaceBuy(ticker, targetBuy, False, 5) else: pass if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 5) if buyPending < 3 and longPositions < maxPositions: tm.PlaceBuy(ticker, targetBuy, False, 5) if trendState == prevTrendState: trendDuration = trendDuration + 1 else: trendDuration = 0 tm.SetCustomValues(prevTrendState, trendDuration)
def RunTradingModelTrending(tm: TradingModel, ticker: str): #This compares the slope of short term (6 day) and long term (18 day) exponential moving averages to determine buying opportunities. Positive, negative, or flat slopes #trend states: ++,--,+-,-+,Flat minActionableSlope = 0.002 prevTrendState, trendDuration = tm.GetCustomValues() if prevTrendState == None: prevTrendState = '' if trendDuration == None: trendDuration = 0 p = tm.GetPriceSnapshot() if not p == None: available, buyPending, sellPending, longPositions = tm.PositionSummary( ) maxPositions = available + buyPending + sellPending + longPositions targetBuy = p.nextDayTarget * (1 + p.fiveDayDeviation / 2) targetSell = p.nextDayTarget * (1 - p.fiveDayDeviation / 2) for i in range(tm._tranchCount): if p.longEMASlope >= minActionableSlope and p.shortEMASlope >= minActionableSlope: #++ Positive trend, 100% long trendState = '++' if p.low > p.channelHigh: #Over Bought pass elif p.low < p.channelLow: #Still early if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy, True) if trendDuration > 1 and buyPending < 3: tm.PlaceBuy(ticker, targetBuy, True) else: if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy, True) if buyPending < 5 and longPositions < maxPositions: tm.PlaceBuy(ticker, targetBuy, False, 3) elif p.longEMASlope >= minActionableSlope and p.shortEMASlope < minActionableSlope: #+- Correction or early downturn trendState = '+-' if p.low > p.channelHigh: #Over Bought, try to get out if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell * .98, False, 3) elif p.low < p.channelLow and p.high > p.channelLow: #Deep correction if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 3) else: pass elif p.longEMASlope < -minActionableSlope and p.shortEMASlope < -minActionableSlope: #-- Negative trend, get out trendState = '--' if p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy * .95, False, 2) elif p.low < p.channelLow and p.high > p.channelLow: #Straddle Low, possible early up pass else: if trendDuration > 2: if sellPending < 5 and longPositions > 5: tm.PlaceSell(ticker, targetSell, True) if sellPending < 5 and longPositions > 0: tm.PlaceSell(ticker, targetSell, True) if sellPending < 5 and longPositions > 3: tm.PlaceSell(ticker, targetSell, False, 2) tm.PlaceSell(ticker, targetSell, False, 2) elif p.longEMASlope < ( -1 * minActionableSlope) and p.shortEMASlope < ( -1 * minActionableSlope): #-+ Bounce or early recovery trendState = '-+' if p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy * .95, False, 2) elif p.low < p.channelLow and p.high > p.channelLow: #Straddle Low if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy * .95, False, 2) else: pass else: #flat, aim for 70% long trendState = 'Flat' if p.low > p.channelHigh: #Over Bought pass elif p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 8: tm.PlaceBuy(ticker, targetBuy, False, 5) if buyPending < 4: tm.PlaceBuy(ticker, targetBuy, False, 5) else: pass if buyPending < 3 and longPositions < maxPositions: tm.PlaceBuy(ticker, targetBuy, False, 5) tm.SetCustomValues(trendState, trendDuration) if trendState == prevTrendState: trendDuration = trendDuration + 1 else: trendDuration = 0 tm.SetCustomValues(prevTrendState, trendDuration)
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
def RunTradingModelSwingTrend(tm: TradingModel, ticker: str): #Give it a date range, some money, and a stock, it will execute a strategy and return the results #minDeviationToTrade = .025 minActionableSlope = 0.002 prevTrendState, trendDuration = tm.GetCustomValues() if prevTrendState == None: prevTrendState = '' if trendDuration == None: trendDuration = 0 p = tm.GetPriceSnapshot() if not p == None: available, buyPending, sellPending, longPositions = tm.PositionSummary( ) maxPositions = available + buyPending + sellPending + longPositions targetBuy = p.nextDayTarget * (1 + p.fiveDayDeviation / 2) targetSell = p.nextDayTarget * (1 - p.fiveDayDeviation / 2) if p.longEMASlope >= minActionableSlope and p.shortEMASlope >= minActionableSlope: #++ Positive trend, 70% long trendState = '++' if p.low > p.channelHigh: #Over Bought if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell * (1.03), False, 10) elif p.low < p.channelLow: #Still early if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy, True) if trendDuration > 1 and buyPending < 3: tm.PlaceBuy(ticker, targetBuy, True) else: if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy, False) if buyPending < 5 and longPositions < maxPositions: tm.PlaceBuy(ticker, targetBuy, False) elif p.longEMASlope >= minActionableSlope and p.shortEMASlope < minActionableSlope: #+- Correction or early downturn trendState = '+-' if p.low > p.channelHigh: #Over Bought, try to get out if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 3) elif p.low < p.channelLow and p.high > p.channelLow: #Deep correction if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 3) else: pass elif p.longEMASlope < -minActionableSlope and p.shortEMASlope < -minActionableSlope: #-- Negative trend, aim for < 30% long trendState = '--' if p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 6: tm.PlaceBuy(ticker, targetBuy * .95, False, 2) elif p.low < p.channelLow and p.high > p.channelLow: #Straddle Low, early down or up pass else: if sellPending < 5 and longPositions > 3: tm.PlaceSell(ticker, targetSell, True) if trendDuration > 1: tm.PlaceSell(ticker, targetSell, True) if sellPending < 5 and longPositions > 3: tm.PlaceSell(ticker, targetSell, False, 2) tm.PlaceSell(ticker, targetSell, False, 2) elif p.longEMASlope < (-1 * minActionableSlope) and p.shortEMASlope < ( -1 * minActionableSlope): #-+ Bounce or early recovery trendState = '-+' if p.high < p.channelLow: #Over sold pass elif p.low < p.channelLow and p.high > p.channelLow: #Straddle Low if sellPending < 3 and longPositions > 3: tm.PlaceSell(ticker, targetSell, False, 3) else: pass else: #flat, aim for 70% long trendState = 'Flat' if p.low > p.channelHigh: #Over Bought if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell * (1.03), False, 10) elif p.high < p.channelLow: #Over sold if buyPending < 3 and longPositions < 8: tm.PlaceBuy(ticker, targetBuy, False, 5) if buyPending < 4: tm.PlaceBuy(ticker, targetBuy, False, 5) else: pass if sellPending < 3 and longPositions > 7: tm.PlaceSell(ticker, targetSell, False, 5) if buyPending < 3 and longPositions < maxPositions: tm.PlaceBuy(ticker, targetBuy, False, 5) if trendState == prevTrendState: trendDuration = trendDuration + 1 else: trendDuration = 0 tm.SetCustomValues(prevTrendState, trendDuration)
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