Advertisement
cjc5013

CR Weekly Mean Reversion

Oct 9th, 2019
242
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.53 KB | None | 0 0
  1. '''
  2. This is code for the "Connors Research Weekly Mean Reversion" Strategy
  3. From "The Alpha Formula"
  4.  
  5. Rules:
  6.  
  7. 1. Long-term Trend Following Regime Filter. SPY’s total return over the last six months (126 trading days) is positive.
  8.  
  9. 2. Liquidity Filter. The stock must be one of the 500 most liquid US stocks. Our 500 most liquid US stock universe is determined every month by taking the 500 US stocks with the highest 200-day average dollar volume.  This is the Q500US universe.
  10.  
  11. 3. The Weekly 2-period RSI of the stock is below 20. This confirms that the stock has overreacted to the downside.
  12.  
  13. 4. All stocks that meet these criteria are then ranked by their trailing 100-day historical volatility. We then BUY on the close, in equal weight, the 10 stocks with the LOWEST historical volatility.
  14.  
  15. 5. SELL the stock on the close if its weekly 2-period RSI is above 80. This is checked at the end of every business Week.
  16.  
  17. 6. SELL the stock on the close if the current price is more than 10% below the entry price. This is checked at the end of every business Day.
  18.  
  19. 7. Any capital that is not allocated is put into SHY (1-3-year US Treasuries ETF).
  20.  
  21.  
  22. '''
  23.  
  24.  
  25. import quantopian.algorithm as algo
  26. from quantopian.pipeline import Pipeline
  27. from quantopian.pipeline.data.builtin import USEquityPricing
  28. from quantopian.pipeline.filters import Q500US
  29.  
  30. import talib as ta
  31. from math import sqrt
  32.  
  33. def initialize(context):
  34.     #Set slippage and attach the algo
  35.     set_slippage(slippage.FixedSlippage(spread = 0.0))
  36.     algo.attach_pipeline(make_pipeline(), 'pipeline')
  37.    
  38.     #Schedule our functions
  39.     schedule_function(trade , date_rules.week_end(), time_rules.market_close(minutes=6))
  40.     schedule_function(stops, date_rules.every_day(), time_rules.market_close(minutes=10))
  41.     schedule_function(take_profits, date_rules.week_end(), time_rules.market_close(minutes=10))
  42.     schedule_function(trade_bonds, date_rules.every_day(), time_rules.market_close(minutes=5))
  43.  
  44.     #Global variables for our TF Regime Filter
  45.     context.spy = sid(8554)
  46.     context.TF_filter = False
  47.     context.TF_Lookback = 126
  48.    
  49.     #Global Variables for amount of securities, RSI level, RSI lookback, Take Profit level and stop percent
  50.     context.Target_securities_to_buy = 10.0
  51.     context.RSI_level = 20
  52.     context.RSI_lookback = 2
  53.     context.RSI_TP_level = 80    
  54.     context.stop_percent = 0.10
  55.    
  56.     #Global Variable controlling bond fund we rotate into
  57.     context.bonds = sid(23911)
  58.    
  59.     #Globla variable controlling vol lookback to ranking (if several stocks are oversold)
  60.     context.vol_lookback = 100
  61.    
  62. def make_pipeline():
  63.  
  64.     # Base universe set to the Q500US
  65.     universe = Q500US()
  66.  
  67.     # This is pipeline code
  68.     # All we are doing is making an object, 'pipe' containing the current 500 most liquid US stocks
  69.     pipe = Pipeline(screen=universe)
  70.     return pipe
  71.  
  72.  
  73. def before_trading_start(context, data):
  74.    
  75.     #Called every day before market open.    
  76.     context.output = algo.pipeline_output('pipeline')
  77.     # These are the securities that we are interested in trading each day.
  78.     context.security_list = context.output.index
  79.        
  80. def trade(context, data):
  81.     #This code controls our trend following regime filter
  82.     #If market is trending higher we set "context.TF_filter" to True
  83.     #If not we set "context.TF_filter" to False
  84.     TF_hist = data.history(context.spy , "close", 253, "1d")
  85.     TF_check = TF_hist.pct_change(context.TF_Lookback).iloc[-1]
  86.  
  87.     if TF_check > 0.0:
  88.         context.TF_filter = True
  89.         print('TF Filter Percentile passed')
  90.     else:
  91.         context.TF_filter = False
  92.         print('TF Filter Percentile not passed')
  93.     ############ Regime Filter code end #################
  94.  
  95.    
  96.     #### Grab prices DataFrame, resample to weekly data
  97.     prices = data.history(context.security_list,"close", 253, "1d")  
  98.     prices_weekly = prices.resample('w').last()
  99.    
  100.     ##### Calculate RSI and filter to stocks under an RSI level using boolean filter
  101.     RSI = prices_weekly.apply(lambda c: ta.RSI(c, context.RSI_lookback)).iloc[-1]
  102.     low_RSI = RSI[RSI < context.RSI_level]
  103.  
  104.     ##### Calculate vol of our stocks that are oversold (for ranking)
  105.     vol = prices[low_RSI.index][-context.vol_lookback:].pct_change().std()    
  106.     vol_sorted = vol.sort_values(ascending=True)
  107.    
  108.     ##### Check current amount of positions (not including bond position
  109.     if context.portfolio.positions[context.bonds].amount == 0:
  110.         num_of_current_positions = len(context.portfolio.positions)
  111.     if context.portfolio.positions[context.bonds].amount > 0:
  112.         num_of_current_positions = len(context.portfolio.positions) - 1
  113.      
  114.     #### Calculate securities we need to buy
  115.     securities_to_buy = context.Target_securities_to_buy - num_of_current_positions
  116.    
  117.    
  118.     #### Loop through our final buy list (sorted by vol) and buy the amount we need
  119.     securities_to_buy_counter = 0
  120.     for x in vol_sorted.index:
  121.         if securities_to_buy_counter < securities_to_buy and x not in context.portfolio.positions and context.TF_filter == True:
  122.             order_percent(x , (1 / context.Target_securities_to_buy))
  123.             print("we bought" , x)
  124.             securities_to_buy_counter = securities_to_buy_counter + 1
  125.            
  126. def stops(context,data):
  127.    
  128.     ## Loop through our portfolio (not including bonds) and see if we need to stop ourselves out
  129.     for x in context.portfolio.positions:
  130.         if (x.sid == context.bonds):
  131.             pass
  132.         else:
  133.             basis = context.portfolio.positions[x].cost_basis
  134.             current_price = data.current(x,'price')
  135.             stop_price = basis * (1.0 - context.stop_percent)
  136.  
  137.             #Percent Stop
  138.             if current_price < stop_price:
  139.                 order_target_percent(x,0)
  140.                 print('GOT STOPPED OUT OF' , x , 'curr_price' , current_price , 'stop_price' , stop_price)
  141.  
  142.                
  143. def take_profits(context, data):
  144.    
  145.     #Grab current positions, grab history, resample to weekly, calculate the RSI
  146.     current_positions = context.portfolio.positions.keys()  
  147.    
  148.     hist_for_TP = data.history(current_positions,"close", 90, "1d") #Get history for RSI calculation - this is only prices
  149.     hist_for_TP_weekly = hist_for_TP.resample('w').last() #Resample to weekly frequency
  150.     RSI_df_for_TP = hist_for_TP_weekly.apply(lambda c: ta.RSI(c, context.RSI_lookback)) #Convert to rolling RSI values
  151.     RSI_for_TP_current = RSI_df_for_TP.iloc[-1] #Get the most current RSI reading for all securities
  152.    
  153.     ## Loop through our portfolio (not including bonds) and see if RSI is above our TP level, if we sell (take our profit)
  154.     for x in context.portfolio.positions:
  155.         if (x.sid == context.bonds):
  156.             pass
  157.         else:
  158.             if RSI_df_for_TP[x].iloc[-1] > context.RSI_TP_level:    
  159.                 order_target_percent(x,0)
  160.                 print('RSI TP got hit' , x , RSI_df_for_TP[x].iloc[-1])
  161.                
  162.                
  163. def trade_bonds(context , data):
  164.     #Allocate any unsed capital to Bonds
  165.     if context.portfolio.positions[context.bonds].amount == 0:
  166.         amount_of_current_positions = len(context.portfolio.positions)
  167.     if context.portfolio.positions[context.bonds].amount > 0:
  168.         amount_of_current_positions = len(context.portfolio.positions) - 1
  169.     percent_bonds_to_buy = (context.Target_securities_to_buy - amount_of_current_positions) * (1.0 / context.Target_securities_to_buy)
  170.     order_target_percent(context.bonds , percent_bonds_to_buy)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement