Developing Sizers in Backtrader – Part 2

This is the second post covering the development of sizers in Backtrader. The first post provided an introduction to the basics, looked at the anatomy of a sizer and provided some basic sizing algorithms. This post by contrast, will dive a bit deeper into the subject by taking a look at comminfo and how it can be used to fine-tune your sizing algorithms.

Note: Since this post is primarily focused on how to apply comminfo to sizing algorithms, readers may wish to read the related commission’s scheme post: Backtrader: Commission Schemes

Comminfo

Each time you make a buy or sell call within a strategy, the sizer class is passed the strategy’s comminfo instance. This allows you to access commission attributes (variables) such as commission, mult and marginas well as comminfo’s own methods (functions). Having access to commission parameters and methods can allow the sizer to account for the exact commission that would be charged to open and close a position.

Some further reading on commissions can be found here:

  1. Official Docs: https://www.backtrader.com/docu/commission-schemes/commission-schemes.html
  2. Backtest-Rookies: Backtrader: Commission Scheme

As you look through the documentation, you might notice that most comminfo methods require a size parameter when called. Given that we are trying to calculate the size, one might argue that these methods are not immediately useful. However, don’t discard them just yet.  They can be used to make verifications or adjustments to your sizing once you have an initial idea of the size you want to return.

Accounting for Commissions

If we want to properly control our total risk, taking account of commissions is essential. As such, our sizing algorithm should factor in this cost so we don’t induce death by a thousand paper cuts. Additionally, if we don’t factor in these costs, we can easily end up returning a size that we don’t enough cash to open. This results in missed trades and sometimes a bit of head scratching! An example of this is below. If you run the code, with the basic max risk sizer developed in part 1 and crank up the commissions, you will the issue.

Important Note: The data in this tutorial uses the Quandl API. With this API you are limited to the number calls you can make per day. To have unlimited access to their free data, sign up for an API key here and add the apikeykeyword to the quandl data call like so:

Reference: https://www.quandl.com/?modal=register

Now run the code with the following commands. Take care to replace the file name with whatever you save the code as.

Example 1: python .\sizers-part2-perc-commission-too-high.py

Example 2: python .\sizers-part2-perc-commission-too-high.py --commperc 10

You will see that in the second example, no trades are taken. That is because we don’t have enough cash in the account to pay for the number of shares returned by the sizer when a large commission is added. It is an extreme example, but this can also happen in less extreme circumstances. For example when trying to go “all in” or when you don’t have much cash left due to existing positions in the market.

This slideshow requires JavaScript.

Moving On

Getting back on track, we will now extend the maxRiskSizerto account for commissions. The maxRiskSizeris designed to calculate the maximum size position you can take without exceeding a certain percentage of the cash available in your account. It would, therefore, be even better if it could take the commission into account.

Example Code

Commentary

The code in the example allows you to run the same script in two modes. To do this, we use the argparsemodule to specify different options when running the script (for more on that see: Using argparse to change strategy parameters). In this case, we are using it so that we can easily compare the differences between the position sizes returned from the sizer. We also use it to adjust the commission size to further influence the returned sizes.

Run the script with the “–help” option to find out more about what each option does. The following output shall be returned.

Note: To see how the sizer is arriving at its final value running with the --debugflag is recommended.

The code example contains two sample sizers, one fixed commission scheme and a strategy. The two sizers have the same objective. That is to provide the maximum size that can be purchased within a certain risk tolerance. Where they differ is that maxRiskSizerCommshas been extended to make an adjustment to the final price based on the amount of commission expected for a round trip (to buy and later sell the instrument). It also is able to change its algorithm depending on whether the commission scheme is stock-like or futures-like (percentage based or a fixed commission). (See: https://www.backtrader.com/docu/commission-schemes/commission-schemes.html#commissions-stocks-vs-futures)

The maxRiskSizerCommsis the focus of this tutorial and where the magic happens. However, the basic sizer from part 1  has been included so that you can easily compare the differences. maxRiskSizersCommsarrives at its sizing calculation using the following steps

  1. It calculates the maximum cash we are willing to risk
  2. Then get the current commission value.
  3. Next, it will check whether the commission scheme is stocklike.
  4. If so, we deem commissionto be a percentage value. As such, we then apply a percentage based commission to the price. Note that the percentage is doubled to account for the ultimate sale commission of the same size.
  5. If not, we deem commissionto be a fixed value. As such, we then double the commission (again to account for the sale) and simply deduct it from the total we are willing to risk.
  6. Finally, we divide the amount we are willing to risk by the price (or adjusted price in step 4) of the Instrument.

Note: That when working with the comminfoobject, one thing to watch out for is to use the parameter shortcut p when accessing the commission scheme attributes.  This one stumped me for a few minutes. E.g to access the commission value, use comminfo.p.commissioninstead of comminfo.commission. If you do the latter, you will raise the following AttributeError.

Result

Assuming you shall save the example code assizers-part2-commission.py, the code can be run with the commands in the following examples.

Note: If you are running on Windows and only have one version of Python installed, you will need to replace python3with just python in the examples below.

Example 1

The first example will run the script using the sizer that does not take commissions into account and uses default values for riskandcommission. Note that when the commtype is not specified, the default commission type is stock-like.

python3 .\sizers-part2-commission.py --debug

Example 2

The second example will swap out the sizer with the maxRiskSizerCommssizer. As such, it will now account for the commission.

python3 .\sizers-part2-commission.py --debug --commsizer

Notice that we now have some more debug output and the returned size is smaller?

Example 3

The final example changes the commission type to fixed and sets a $5 commission to each trade.

python3 .\sizers-part2-commission.py --debug --commsizer --commtype Fixed --commission 5

From the output, we can see that the max risk was adjusted from 100 to 90 before the size calculation was made. Also, the commission adjusted price is now ‘N/A’ due to the fact we do not need to make a price adjustment for a fixed commission.

Finally, one more thing that might be worth pointing out. Notice that when using strategy.close()the sizer is not called? This is to be expected as the framework does not need to consult the sizer to know what the current open position size is. It just takes the current open position size and uses that. This simplifies sizer development as you do not need to code a condition for a close()method call.