Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Quantopian Algorithm, EMA and RSI Indicators

This is my algorithm I developed on Quantopian. It buys stocks based on fundamentals using the the fundamental filter. I wanted to find stocks with low P/E ratios, low book value, and free cash flow. Once these are filtered I use technical analysis to find stocks above their moving averages and an rsi<50 each day. These stocks are sold at the end of the month if they are below there moving averages. The algorithm manages leverage decently, but does have some high leverage fluctuations. This is my best back test over a 13 year period.

19 responses

That hit a leverage of 1.9, see Max Intraday Leverage or PvR for a couple of options/ways to watch out for that.
As a result, profited $1.12M on $1.85M activated/transacted for PvR of 60.6%. Chart shows 112%.

I think it could be interesting if you make the sell a trailing stop/sell, holding that stock as long as the price is going up, selling any time the price goes down a certain amount, locking in that profit and freeing up cash to buy others. I played around with this trailing trades code in your algo resulting in higher returns, even lower beta (suitable for the contest), even lower than you have there, before I messed it up and ran out of steam. Was also doing trailing buy.

Thanks for your feedback,
I don't like the way it sells at the end of the month if it is below their moving average at the end of the month. It doesn't sell well in paper-trading. I would also like to implement something to make it only hold a certain amount of stocks at the same time. I don't think I would buy upwards to 50 stocks. I'll work with it today to see where I can get.

On another note, do you know a way I can share my backtest and code with future employers? I am graduating soon and I am looking for jobs. I want to share my work with them so they see I can code and stuff?

Here is my most profitable backtest. This buys and holds the stock over the time period. I wanted to create fundamentals that limited the drawdown by looking for companies with free cash flow and low p/e ratios. I am currently working on developing a trailing stop loss that gives a better sell signal, then the ema25 at the end of the month.

Hi Eric,

Interesting code I like how you are doing the filters. Unfortunately when I attempt to backtest this it is abnormally slow and eventually runs into many errors (maybe because its from a year back). The errors are as follows:

"It looks like this backtest is using deprecated API methods. Check the 'Log Output' tab for more." 2016-08-15 06:31 DEPRECATION WARNING Line 48: The history method is deprecated. Use data.history instead. Learn more here.
2016-08-15 06:31 DEPRECATION WARNING Line 49: Checking whether an asset is in data is deprecated. Learn more here.
2016-08-15 06:31 DEPRECATION WARNING Line 58: Iterating over the assets in data is deprecated. Learn more here.
2016-08-15 06:31 DEPRECATION WARNING Line 63: The history method is deprecated. Use data.history instead. Learn more here.
2016-08-15 06:31 DEPRECATION WARNING Line 66: The history method is deprecated. Use data.history instead. Learn more here.
2016-08-15 06:31 DEPRECATION WARNING Line 73: data[sid(N)] is deprecated. Use data.current. Learn more here.
2016-08-15 06:31 DEPRECATION WARNING Line 94: Iterating over the assets in data is deprecated. Learn more here.

Would you happen to have any updated version of this code since that might not have any errors? I am looking to play around with something very similar that basically takes a fundamental screen criteria such as yours and if the screen passes, will apply a moving average and RSI filter to identify stocks to buy and short. Please let me know when you have a chance. And once again great job getting this idea going. Thanks Eric.

Sam

So you cannot use this way to upload stocks anymore. Use pipeline. Here is my code for Benjamin Graham Fundamentals. It doesn't have the RSI, but it has the moving averages.

Hi Eric sounds good thanks for this. Had a quick question for you on the code I am working below that has had me stumped now for hours:

Hello I have a quick question. I am attempting to create an argument in code that require the first for loop to return a true value before the second for loop can even take place. And if the first for loop fails to return a true value for the signal, I want the algorithm to not even go to the second for loop and thus have nothing happened. Basically first for loop must return a signal = true before the algo goes to second for loop. and if signal is not true algo takes on no trade. I have my brief code below but does not seem to work as I am getting a syntax error (probably because setting signal =True) doesn't make sense. Any idea as to how I can create this? Basically how can I tell the system that the second for loop only happens if the signal from the first is a true value? Thanks for the help as always!

first for loop

for security, signal in context.signalshort.items(): # screen for signal generated in before trading starts function for shorting based on 50ma below 200ma
if signal and (security not in context.portfolio.positions) and (security in context.shorts.index):
signal = True
continue

   if (not signal) and (security in context.portfolio.positions):  
      if data.can_trade(security) and security not in context.security_list: # if stock is available to be traded and not restricted, but it is not in the context.security_list for longs and shorts, exit out of position. # Recall context.security_list is a list of our long and short baskets  
         order_target_percent(security, 0)  
# second for loop (to only happen if signal is returned as true from first for loop)
for security in context.shorts.index and (signal = True): # for stocks pulled in the indexing for short positions only  
      if get_open_orders(security): #shows a list of all open short orders for a particular stock  
             continue # put in order  
      if data.can_trade(security): # checks to see if the stock is available to be traded and not restricted  
             order_target_percent(security, -context.shorts[security])  

My recommendation would be to avoid using a for loop. Are you trying to have longs and shorts? If you want to do a moving average crossover strategy I would use pipeline.

Conditional Statements, Added Moving Average crossover,

    mean_50 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=50)  
    mean_200 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200)  
    ma_cross = mean_50>mean_200

Then use set screen if you are long only, I am not quite sure how you would do it if you had shorts.

pipe=Pipeline(
columns={"fin_health": fin_health ,
},
screen= (spy & eps & div & pe & ma_cross)

       )  

    attach_pipeline(pipe,'my_pipeline')  

Not quite sure

Yes worked out something very similar to that. Thanks for your help Eric!

This is a good example of pipeline for longs/short strategy.

https://www.quantopian.com/posts/long-short-pipeline-multi-factor

Eric,

I actually saw this; its a great example and I took away a lot from it. There are a couple specific things going on in this example though that have me confused. Maybe you could add some clarification if you have a moment:

  1. It seems that 4 factors are being passed through; however why is the code filtering specifically only on 'Momentum' being >1 or <1 when it is creating what it calls to be the actual long and short baskets (or long.index and short.index) that eventually create the union for context.security_list?

And then it goes to rank them after checking for whether Momentum is >1 or <1. Does this mean Momentum has more 'weight' than volatility, value, and quality in the code and is the driving factor? I am just confused that if Momentum is the driver to create the actual long and short baskets (or indexes) that the author refers to, then what is the purpose of even having the other three factors in there? Do they not serve weight to the pipeline screen? I just do not see their relevance if Momentum is driving the ranks of the long and short baskets that create the ultimate context.security_list

  1. And secondly, when the trades are being triggered for both long and short side, the author used Order Target Percentage. However, for this particular example, how much of each stock is actually being longed or short when a signal occurs to put a trade on? I know what the definition of Order Target Percentage is (in that If there is no existing position in the security, an order is placed for the full target percentage, equivalent to a % value of entire portfolio (cash + open positions). If there is a position in the security, an order is placed for the difference between the target percent and the current percent. Placing a negative target percent order will result in a short position equal to the negative target percent.).

However I am confused on how many actual shares are being added or shorted in this code for each signal that occurs? And specifically when the algo knows when to not long or short (if it is at capacity).

Any clarification on these would be greatly appreciated. And I cannot thank you enough for the guidance; I am relatively new to coding in Python (however have found a tremendous passion in doing it). I really enjoy it and am still learning. Any insight on these two points is greatly appreciated. Thanks again Eric, and hope you have a great rest of your day.

Sam

  1. It's a momentum strategy. You take the mom=ma_long/ma_short. If mom less then 1, it gives a short signal. He only wants to rank stocks to short if the momentum is less than 1. Similarly he only wants to long if mom greater then 1. He then ranks given these conditions. He calls all of the conditions in initialize.

def initialize(context):
pipe = Pipeline()
pipe = attach_pipeline(pipe, name='factors')
He attaches all four to the pipeline and calls them factors.

He uses them when he puts them into results. And then ranks each of the factors by taking the axis=1 mean which is horizontal. Axis=0 is vertical.

Compute final rank and assign long and short baskets.

def before_trading_start(context, data):
results = pipeline_output('factors').dropna()
ranks = results.rank().mean(axis=1).order()

  1. This line of code puts each of the stocks in a dictionary file.
    context.security_list = context.longs.index.union(context.shorts.index).tolist()

Then he can say

for security in context.shorts.index:  
    if get_open_orders(security):  
        continue  
    if data.can_trade(security):  
        order_target_percent(security, -context.shorts[security])  

for security in context.longs.index:  
    if get_open_orders(security):  
        continue  
    if data.can_trade(security):  
        order_target_percent(security, context.longs[security])  

Something that confused me, is the before_trading_start function does not have to be before the initialize. I would probably switch it the other way. So he creates the pipeline in initialize and the weights in before_trading_start.

This is how he gets the weights for how many longs or shorts.I am not sure what /= means?

context.shorts /= context.shorts.sum()  

context.longs /= context.longs.sum()  

Eric,

Yes you are right about him leaving before trading start out of initialize. That didn't make sense to me either.
I understand what you mean when you say the context.shorts and context.longs gets weights for how many longs or shorts.

What still confuses me on this code though is when he calls Order_target_percentage for longs and shorts, how do we know (based on our portfolio capital amount) how many shares to long or short for each of the weights created in the long and short indexes we create? I get that the .index creates the weights for each long or short in the day before trading start function. However, once we have those weights, I do not understand how the program knows immediately how many shares to long and short each day in the 'rebalance' function? That is what is confusing me most I think........

This is how he gets the weights for how many longs or shorts.I am not sure what /= means?

context.shorts /= context.shorts.sum()

context.longs /= context.longs.sum()

Every function in quantopian is usually a function of context and data. You can say context.shorts, and since all the functions have context it knows those are the shorts. If he said notaparameter.shorts , it wouldn't know the function. Only really good programmers usually make functions with other parameters.

def before_trading_start(context, data):

def handle_data(context, data):

def cancel_open_orders(context, data):

def rebalance(context, data):

Ok that makes sense.

But for his particular example when he specifically calls 'Order_Target_Percentage' for either shorts or longs (whichever), how does order_target_percentage in this particular code know exactly how much money is available to either buy or short a name, and how many shares does it know to do so? I mean if I defined order_target_percentage in the code to be 0.30 for example, I assume it means to tell the code never have more than 30% of portfolio in one stock (on long or short side). So for this case, no specific order_target_percentage was assigned; thus I am not clear how it knows how many shares of one particular name to long or short when there is a signal to do so?

I am unexperienced with this type of weighting structure, but it seems better. In the past they have been equally weighted portfolios.
If my leverage is 1.0 and I have 10 stocks in my basket. I used to take 1.0/10 for 0.10 portfolio weight.

Here is the link to the thread on the weighting

https://www.quantopian.com/posts/what-is-this-slash-equals-for-portfolio-weighting?utm_campaign=what-is-this-slash-equals-for-portfolio-weighting&utm_medium=email&utm_source=forums

If you put a print after context.longs, you can see the weights. It is equally weighted at 1/200 for the longs and 1/200 for the shorts making the leverage approximately 2.0.

That makes sense I think. I assume that is what the .head(200) for the context.shorts references and the .tail(200) for the context.longs references?

What else is bizzare though is is why is context.shorts being divided by '1', whereby context.longs is not? See below:

context.shorts = 1 / ranks[results["momentum"] < 1].head(200)
context.longs = ranks[results["momentum"] > 1].tail(200)

Not quite sure, I think it has something to do with the weighting and the momentum being less than 1.

Who test in live ?