Back to Community
Anyone recreated Stan Weinstein's "Profit in Bull & Bear Markets" algo?


Has anyone put Stan Weinstein's famous algo together yet??

Outlined here:

It is written to only look at stocks of $3 or more but you can change that.
. . . Relative Strength Indicator (RSI) higher than that from 5 days ago. . . . Closing price is higher than the average in the past 30 days. . . . Volume at least 120% of that from the last 2 months. (The 120% is arbitrary. I used it because it seems to work) . . . Average volume for the last 2 months at least 100,000 shares. . . . Price today higher than 30 day average . . . Price today higher than in the last 3 weeks

Not quite sure where to start with the coding but previously built this screen in excel.



11 responses


I've created an algo that tests for the following qualities:

. . . Relative Strength Indicator (RSI) higher than that from 5 days
ago. . . . Closing price is higher than the average in the past 30
days. . . . Volume at least 120% of that from the last 2 months. (The
120% is arbitrary. I used it because it seems to work) . . . Average
volume for the last 2 months at least 100,000 shares (note that
TC/2000 reports shares in hundreds). . . . Price today higher than 30
day average . . . Price today higher than in the last 3 weeks

Not quite sure what to do about the different stages of the stock, but this seems like a good starting point! You can put in your order logic in

if False in context.w_checks.values():  
        # Order here  


Clone Algorithm
Total Returns
Max Drawdown
Benchmark Returns
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
rsi = ta.RSI(timeperiod=14)

def initialize(context):
    context.spy = sid(8554)
    context.max_notional = 1000000.1
    context.min_notional = -1000000.0
    context.past_rsi = {}
    context.past_prices = {}
    context.three_week_price = {}
    context.past_volume = {}
    # To be used for to see if stock falls into acceptable pattern
    context.w_checks = {'rsi': True, 'closing': True, 'volume_percent':True, 
                        'avg_volume':True, 'price_week': True}

def handle_data(context, data):
    # All the checks belong here in the first
    # RSI > RSI of five days ago
    # Closing price > closing price in last 30 days
    # Current volume > avg volume in last 60 days and at least 120% of the average
    # Current price > price in the last three weeks
    # Relative strength indicator greater than RSI of last five days
    rsi_data = rsi(data)
    current_rsi = rsi_data[context.spy]
    context.past_rsi[get_datetime().date()] = current_rsi  
    test_rsi = past_rsi(context,5)
    if current_rsi > test_rsi:
        context.w_checks['rsi'] = True
        context.w_checks['rsi'] = False
    # Closing price greater than the average closing price in last 30 days
    current_close = data[context.spy].close_price
    context.past_prices[get_datetime().date()] = data[context.spy].price
    current_close_mavg = closing_average(context, 30)
    if current_close > current_close_mavg and current_close_mavg > 0:
        context.w_checks['closing'] = True
        context.w_checks['closing'] = False
    #Volume greater than 120% of the last two months
    #Average volume in the last two months over 100,000
    current_volume = data[context.spy].volume
    context.past_volume[get_datetime().date()] = data[context.spy].volume
    current_volume_mavg = volume_average(context, 60)
        volume_percent = current_volume_mavg*1.2
        volume_percent = 0
    if current_volume > current_volume_mavg and current_volume_mavg > 0 and \
    current_volume > volume_percent:
        context.w_checks['avg_volume'] = True
        context.w_checks['avg_volume'] = False
    # Check if price today is greater than price in the past 3 weeks (21 days)
    context.three_week_price[get_datetime().date()] = data[context.spy].price
    context.w_checks['price_week'] = True
    for x,y in context.three_week_price.items():
        if current_close < y:
            context.w_checks['price_week'] = False
    # Here should be where your order is. If false, then it hasn't passed the checks
    if False in context.w_checks.values(): 
        # Order here
# Returns RSI from the user-specified days, note that if time period has been less than 14
# RSI returns numpy.null
def past_rsi(context, days):
    if len(context.past_rsi) > days:
        last = sorted(context.past_rsi.items(), key=lambda t: t[0])[0]
            del context.past_rsi[last[0]]
    return sorted(context.past_rsi.items(), key=lambda t: t[0])[0][1]

# Returns average closing price over the number of days specified by the user
def closing_average(context, days):
    # If the dictionary's length is over what we want to average over   
    if len(context.past_prices) > days:  
        # Find and remove the oldest entry  
        last = sorted(context.past_prices.items(), key=lambda t: t[0])[0]  
        del context.past_prices[last[0]]

    # The 30-day moving average including most recent price from today  
    mavg = sum(context.past_prices.values())/len(context.past_prices)
    return mavg

# Returns average volume over the number of days specified by the user
def volume_average(context, days):
    if len(context.past_volume) >= days:
        last = sorted(context.past_volume.items(), key=lambda t:t[0])[0]
        del context.past_volume[last[0]]
    mavg = sum(context.past_volume.values())/len(context.past_volume)
    return mavg

# Updates the context.three_week_price dictionary 
def three_week_price(context, days):
    if len(context.three_week_price) >= days:
        last = sorted(context.three_week_price.items(), key=lambda t: t[0])[0]
        del context.three_week_price[last[0]]
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Hi Seong

This is awesome. Thanks for the support. For now the order can run the same as in the description provided. I am still just learning the platform and so I am likely to be a bit slow at first. I am a C# guy so taking a little while here.


You got my interest. I'm reading Stan's book now. He does a great job of giving simple explanations.
In the first few chapters, he advises waiting for the 30 week moving average to be flat or positive to get a good stage 2 entry, so I'm trying to figure out how I can tell. To do so, I need to calculate the average at least a few days back and compare it. If I figure it out, I'll post it.


Hi Richard

It is a great read. The markets have been so fundamentally driven in recent weeks that everytime a talking head said anything about Syria, debt ceiling etc volatility surges. Hopefully things will now settle. Let me know what you are thinking.


Arb and Seong,
Could you please indicate where in Weinsteins book did you find the algorithm? Thanks in advance,

Hey everyone,

I have been following Weinstein's method for months now and have realized this algo is slightly differentiated in a few ways from the methods he states in his book.

First and foremost I noticed that the RSI being used in this algo (RSI Wilder) is completely different than that mentioned in the book (RSI Mansfield). This indicator takes the RS Dorsey formula and breaks it down to compare it to the strength of the S&P 500. I built the indicator in my Thinkorswim platform, but I do not have experience with python yet so all I can do is provide you all will some material which can be used to code this into the algo.

Second, what he does is screen the stocks to find which industry is outperforming the SPX and then finds stocks within those sectors that meet the criteria that has been set (Volume increase, RSMansfield Uptrend, Close above 30 and 10 week MA's). This usually can help indicate which stocks have entered Stage 2 or are already in Stage 2 of his technique. This can hopefully help you guys while I learn Python and begin to build this on my own eventually.

I have also written code for the TOS platform using colored bars on my charts utilizing the MA's that shows me exactly when the Weinstein method is in play, when I should be cautious, and when I should be out.

I definitely think this can be automated if the metrics are entered correctly. My only concern is sometimes its hard to confirm the different stages until you see them with your own eye.

Hey Jaan,
This is precisely what I suspected, but I wasn't sure. Thank you for confirming it and for the explanation. To write a function in Python calculating the Mansfield and Dorsey relative strengths is very simple. The problem is implementing it in the whole strategy. Weinstein measures the relative strength for each stock in relation to its sector. This means that we should use a different index for each sector and only calculate the relative strength for the securities in the sector using the respective index. Therefore, we have to partition the market universe into several sub-universes representing each sector, choosing an index that represents each sector and commence with calculating. I, unfortunately, don´t have an idea of how to do that, since we have no way to know which sector from the symbol of the security. Furthermore, as you mentioned, Weinstein´s definition of phases is somewhat subjective. Due to all of the above, I don´t think that it is wise to develop a fully automated algorithm to trade Weinstein´s methods. Maybe the optimal way to use the relative strength is as an indicator in stock charting tools.

Hi All

I have finally come around to wanting to action this algo and am looking for someone to help me code this up - initally as a screen so we can backtest and then to work with me on some specific market ideas. Anyone have the time to help me do this...?


@Omar, couldn't you use the 11 sector ETF's as an index for the sector? Here is a thread that has the sector to ETF mapping.
And now that we have fundamentals you use the fundamentals.asset_classification.morningstar_sector_code to map a stock to its sector!

What other problem needs to be solved here?

Paul Perry, It seems to be a good idea. However, I still need a list of all the companies divided into sectors. I don't know if this is possible in Quantopian. I will look at the thread you sent and see if it is any help in this point. Thanks for the tip.

Hi does anyone have a script in tos or algo of any kind to share?