Back-testing our strategy - Programming for Finance with Python - part 5

Algorithmic trading with Python Tutorial




In this Finance with Python, Quantopian, and Zipline tutorial, we're going to continue building our query and then our trading algorithm based on this data. To start, we currently are pulling the PB ratio and the PE ratio on all companies. We need some way to filter these down a bit.

Continuing with our before_trading_start method:

def before_trading_start(context):
    context.fundamentals = get_fundamentals(
        query(
            fundamentals.valuation_ratios.pb_ratio,
            fundamentals.valuation_ratios.pe_ratio,
        )
        .filter(
            fundamentals.valuation_ratios.pe_ratio < 14
        )
        .filter(
            fundamentals.valuation_ratios.pb_ratio < 2
        )
        .order_by(
            fundamentals.valuation.market_cap.desc()
        )
        .limit(context.limit)
    )
    
    update_universe(context.fundamentals.columns.values)
    

We limit this by then using .filter, which then limits based on our logic. In this case, our first bit of logic is that we want the pe_ratio to be less than 14. Next, we require the pb_ratio (price to book) to be less than 2. Price to Book ratio is a ratio that compares the current price of the company to its "book" value. Book value is the value of the company's assets, things that could be sold to other companies, for example. A low book value is nice because we can be confident as investors that the price probably wont drop much because the assets of the company are worth more than that, ignoring the actual value of the "brand" and the business networking that they control, as well as revenues. If a company has a price to book value less than one, this means the assets of the company could be sold off, and there'd be more money left over than the company is currently being valued for. This might signal that this is a great investment, or it might signal there is serious trouble afoot.

Next, just in case we still have a massive number of companies being returned, we go ahead and use the .order_by to sort the return by our choice, which is market cap, descending. This means we'll have the highest valued companies first. Then, finally, we limit the return to our context.limit that we set, so 10. This entire query together returns to us the top 10 highest valued companies that have both a PE ratio less than 14 and a PB ratio less than 2.

Finally, we use update_universe to update the universe of plausible companies that we can invest in. In order to get information, like current prices, in our handle_data method as code runs, we need the companies to be in our "universe."

Next up, let's write our handle_data method:

We start with:

def handle_data(context, data):
    cash = context.portfolio.cash
    current_positions = context.portfolio.positions

We start by simply referencing our current cash, and any current positions that we have. Now, we want to iterate through the stocks in our "universe." So every company in our universe has a PE ratio less than 14 and a PB ratio less than 2. Here's how we'll iterate through all of these companies:

    for stock in data:
        current_position = context.portfolio.positions[stock].amount
        stock_price = data[stock].price
        plausible_investment = cash / context.limit
     
        share_amount = int(plausible_investment / stock_price)

We use a for loop to iterate through "data," which contains every stock in our universe as the "key" (data is a python dictionary.)

First, we go to see if we already have a position in this company. Next, we check to see the current value of that company, which we then use to create the plausible investment size, in dollars. We make this value equal to our current cash, divided by context.limit, which is 10. From here, we calculate the share_amount, which is how many shares we'll buy. From here, we're ready to possibly make a purchase:


        try:
            if stock_price < plausible_investment:
                if current_position == 0:
                    if context.fundamentals[stock]['pe_ratio'] < 11:
                        order(stock, share_amount)
 
        except Exception as e:
            print(str(e))
If the current price of the stock is less than our investment size.
If our current position count is 0 (we're not invested already)
If the PE ratio is less than 11,
we place our order.

Full code up to now:

# Put any initialization logic here.  The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
    context.limit = 10

# Will be called on every trade event for the securities you specify. 
def before_trading_start(context):
    context.fundamentals = get_fundamentals(
        query(
            fundamentals.valuation_ratios.pb_ratio,
            fundamentals.valuation_ratios.pe_ratio,
        )
        .filter(
            fundamentals.valuation_ratios.pe_ratio < 14
        )
        .filter(
            fundamentals.valuation_ratios.pb_ratio < 2
        )
        .order_by(
            fundamentals.valuation.market_cap.desc()
        )
        .limit(context.limit)
    )
    
    update_universe(context.fundamentals.columns.values)
    


def handle_data(context, data):
    cash = context.portfolio.cash
    current_positions = context.portfolio.positions
    
    for stock in data:
        current_position = context.portfolio.positions[stock].amount
        stock_price = data[stock].price
        plausible_investment = cash / context.limit
     
        share_amount = int(plausible_investment / stock_price)
        
        try:
            if stock_price < plausible_investment:
                if current_position == 0:
                    if context.fundamentals[stock]['pe_ratio'] < 11:
                        order(stock, share_amount)
                    
                
            
        except Exception as e:
            print(str(e))

We're ready to go ahead and run this now, so let's do that. The results should be something like (depending on the dates you use):

Well, that's not really the greatest. What happened? It looked like we were doing at least okay initially. Looking at our transactions, we should see it. What we did was buy the companies, and that worked at least decently, but we never actually sold. This means we didn't sell companies that went "bad," and we never made room for new purchases.

Let's go ahead and write in some selling logic next.


There exists 2 quiz/question(s) for this tutorial. for access to these, video downloads, and no ads.

The next tutorial:





  • Programming for Finance with Python, Zipline and Quantopian
  • Programming for Finance Part 2 - Creating an automated trading strategy
  • Programming for Finance Part 3 - Back Testing Strategy
  • Accessing Fundamental company Data - Programming for Finance with Python - Part 4
  • Back-testing our strategy - Programming for Finance with Python - part 5
  • Strategy Sell Logic with Schedule Function with Quantopian - Python for Finance 6
  • Stop-Loss in our trading strategy - Python for Finance with Quantopian and Zipline 7
  • Achieving Targets - Python for Finance with Zipline and Quantopian 8
  • Quantopian Fetcher - Python for Finance with Zipline and Quantopian 9
  • Trading Logic with Sentiment Analysis Signals - Python for Finance 10
  • Shorting based on Sentiment Analysis signals - Python for Finance 11
  • Paper Trading a Strategy on Quantopian - Python for Finance 12
  • Understanding Hedgefund and other financial Objectives - Python for Finance 13
  • Building Machine Learning Framework - Python for Finance 14
  • Creating Machine Learning Classifier Feature Sets - Python for Finance 15
  • Creating our Machine Learning Classifiers - Python for Finance 16
  • Testing our Machine Learning Strategy - Python for Finance 17
  • Understanding Leverage - Python for Finance 18
  • Quantopian Pipeline Tutorial Introduction
  • Simple Quantopian Pipeline Strategy