Hello, world! It's Leo Smigel and I'm back with the fifth installment of the Quant Quickstart series where I help readers get started with Algorithmic Trading.
In this post, we're going to be building on what we've learned so far to create a value "factor" strategy in Backtrader using data from the Intrinio developer sandbox. More specifically, we're going to be ranking stocks by their price-to-book ratio and then selecting the top N stocks that are the cheapest based on this ratio.
We're going to first grab the data directly from Intrinio, and then load it into Backtrader. There's a fair amount of code to cover, so I'll only go in-depth on code we haven't already seen in previous articles. Let's get to it!
Previously, we've loaded our data into CSV files. From now on, we'll be grabbing the data directly from Intrinio. While all of the examples seen here will using the Intrinio developer sandbox, you'll want to get a subscription once you're ready to create your own strategies.
We start with the usual imports, select our start and end dates, and enter our developer sandbox API key.
We grab the data like we did in Quant Quickstart II. Only this time, we add the data to a Pandas dataframe instead of saving it to a CSV file.
security_prices is a Pandas dataframe of stock prices. Let's do the same for our fundamental, price-to-book, data:
Now we're going to do something new. Let's merge the price and fundamental data. We want our data to be grouped by ticker and then by date. With this in mind, we'll sort both the price data and the fundamental data by ticker and date, and then we'll merge the two dataframes while filling the missing fundamental data as our price data is daily and our fundamental data is less frequent.
The last line of code merges the data on the daily index, and forward and backfills missing data. For example, let's assume we have the price-to-book ratio reported on 1/1/2020. For 1/2/2020 until the price-to-book ratio is reported again will be blank. We can take the value from 1/1/2020 and fill it forward until the next value. Again, pandas is amazing for data manipulation.
After all that downloading and data manipulation, here's what the final product looks like.
With the data in the format we need, let's move on to Backtrader.
We create a class called PandasDataCustom that inherits from backtrader.feeds.PandasData. When we inherit from a parent class, our child class gets all of the functionality the parent has automagically. That's why we only need to add the additional functionality, which in our case is the price-to-book data.
Now let's create our strategy. Again, if any of this is unfamiliar, please review the previous posts.
We start by adding a few params. We are trading every fifth weekday (Fridays) and we create a dictionary to store our price-to-book indicator. We also add a timer that will call rebalance weekly. Again, all stuff we've seen before. Now let's rebalance.
All of the magic occurs within rebalance. We rank the stocks using sorted using an anonymous lambda function which returns a list sorted by price-to-book.
Next, we get the existing positions using a Python listcomp. Notice how we are both unpacking pos and filtering by it.
We then close positions if they are no longer in the top rank. The syntax is very similar to the list comprehension (listcomp) above except instead of returning a list, we return a generator expression (genexp), which allows us to use the for d.
Finally, we buy the new positions. We don't have to keep track of the share count to purchase as we're using self.order_target_percent. Also, the above could be made more efficient by using dictionaries, but this will suffice for now.
With the strategy out of the way, we're greeted with familiar code where we instantiate cerebro, add the data and strategy, and run and plot the results.
More code than usual, but at this point, you should be able to understand it. If not, go back to the previous articles and make sure you catch up. Also, as always, you can download the accompanying code from GitHub.