Back to Community
Overall Market P/E Value Strategy

Hey everyone,

I have been plugging away at a couple of my first notebooks & backtests this weekend. Trying to stay in the fundamental/value realm to familiarize myself with the platform to start.

The overall approach was to try to determine expensive companies and cheap companies. I decided to utilize P/E ratios to label companies by taking the overall market (Russell 2000) P/E ratio. Then we can label the companies as cheap if they are below the market average, and expensive if they are above.

The results are fairly poor, but I believe that it could be due to the structure of the strategy. By failing to control for other factors such as sectors, growth estimates, and structural differences, comparing companies to the overall market could be a bad place to start.

Any recommendations are gladly welcomed. I will be posting a backtest of the strategy as well.

Loading notebook preview...
Notebook previews are currently unavailable.
3 responses

Associated backtest:

Clone Algorithm
Backtest from to with initial capital
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
Theory: The overall market's mean P/E ratio is 87.63. 

1. Short companies > 87.63
2. Long companies < 87.63

from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from import morningstar
from quantopian.pipeline.filters.morningstar import Q1500US
def initialize(context):

    # Rebalance every week
    schedule_function(rebalance, date_rules.week_start(), time_rules.market_open(hours=1))
    # Record tracking variables at the end of each day.
    schedule_function(record_vars, date_rules.every_day(), time_rules.market_close())
    # Creating pipeline.
    attach_pipeline(make_pipeline(), 'my_pipeline')
def make_pipeline():
    # Base Universe.
    base_universe = Q1500US()
    # Gathering P/E Ratio.
    pe_ratio = morningstar.valuation_ratios.pe_ratio.latest
    # Short companies above overall market P/E ratio
    expensive = pe_ratio > 87.63
    # Long companies below overall market P/E ratio
    cheap = pe_ratio < 87.63
    #Return pipe:
    return Pipeline(
        columns = {
            'P/E': pe_ratio,
            'Expensive': expensive,
            'Cheap': cheap
        screen = base_universe

    return Pipeline()

def before_trading_start(context, data):
    Setting long and short positions.
    context.output = pipeline_output('my_pipeline')
    # List of long positions.
    context.longs = context.output[context.output['Cheap']].index
    # List of short positions.
    context.shorts = context.output[context.output['Expensive']].index
    # Gathering weights from assign_weights(). 
    context.long_weight, context.short_weight = assign_weights(context)
def assign_weights(context):
    Assign weights to securities that we want to order. Choosing a market neutral position of long & shorts having equal weight.
    # Set long weights to 50%
    long_weight = 0.5 / len(context.longs)
    # Set short weights to -50%
    short_weight = -0.5 / len(context.shorts)
    return long_weight, short_weight

def rebalance(context,data):
    Rebalance weights per week.

    # Exit positions not in either long or shorts.
    for security in context.portfolio.positions:
        if security not in context.longs and security not in context.shorts and data.can_trade(security):
            order_target_percent(security, 0)
    # Set long position orders.
    for security in context.longs:
        if data.can_trade(security):
            order_target_percent(security, context.long_weight)
    # Set short position orders.
    for security in context.shorts:
        if data.can_trade(security):
            order_target_percent(security, context.short_weight)
def record_vars(context, data):
    Recording long positions, short positions, and leverage.
    longs = shorts = 0
    for position in context.portfolio.positions.itervalues():
        if position.amount > 0:
            longs += 1
        elif position.amount < 0:
            shorts += 1
    # Record long, shorts, and leverage
    record(leverage=context.account.leverage, long_count=longs, short_count=shorts)
There was a runtime error.

The P/E ratio is definitely not correct. Also a one year time horizon is way to short for value investing to really work

Instead of taking a fixed number for the P/E I'd suggest grouping the universe into quantiles, (or ranked lowest to highest), and then put the lowest vs the highest P/E ratio. As Thomas Guth said, 1 year is much to short. I would suggest 10 years or more for the predictive nature of fundamental analysis to truly be demonstrated. Especially in the past 2 years you will find fundamental metrics like P/B, P/E, P/CF have lost predictive value due to the algos pulling in energy sector companies that are depressed due to the low price of oil. I've tried sector weighting before and did not find that it helped in the long run. I would caution that I have found that going long with P/E info is more useful by itself than going long/short. I think this is because long a low P/E is more predictive on increased price performance than high P/E is on decreased price performance. Also when combined for a long /short strategy this does not help much as in down turns both are affected randomly imo. As such the long/short does not perform as well as just a straight up long low P/E.