Fees & Trade Costs

Open In Colab

Some strategies can fall apart when broker commissions are taken into account. The FlatPerFee and FlatPerShare fees come with simple-back, but you can define your own fees by extending the Fee class.

[ ]:
%%capture
!pip install --upgrade simple_back
[1]:
from simple_back.backtester import BacktesterBuilder

builder = (
   BacktesterBuilder()
   .name('AAPL without Fees')
   .balance(10_000)
   .calendar('NYSE')
   .compare(['AAPL'])
   .live_progress()
   .live_plot()
)
[2]:
bt = builder.build()
for day, event, b in bt['2015-1-1':'2020-1-1']:
    b.pf.liquidate()
    b.long('AAPL', percent=1)
../_images/intro_fees_3_0.png

Flat Fees

As you can see, selling & buying AAPL at every open and close results in the same as simply buying it once and holding it. But for many brokers, this is not realistic. Let’s assume we have to pay a flat commission of 1 dollar per trade. We can do this using FlatPerTrade.

[3]:
from simple_back.fees import FlatPerTrade

bt = (
    builder
    .name('AAPL (fee per trade)')
    .trade_cost(FlatPerTrade(1))
    .build()
)

for day, event, b in bt['2015-1-1':'2020-1-1']:
    b.pf.liquidate()
    b.long('AAPL', percent=1)
../_images/intro_fees_6_0.png

We can also assume a flat commission of 5 cents per share.

[4]:
from simple_back.fees import FlatPerShare

bt = (
    builder
    .name('AAPL (fee per share)')
    .trade_cost(FlatPerShare(.05))
    .build()
)

for day, event, b in bt['2015-1-1':'2020-1-1']:
    b.pf.liquidate()
    b.long('AAPL', percent=1)
../_images/intro_fees_8_0.png

Custom Fees

Let’s now build a custom Fee that charges a percentage of the total cost.

[5]:
from simple_back.fees import Fee
[6]:
class PercentageFee(Fee):

    def __init__(self, fee_percent):
        self.fee_percent = fee_percent

    def nshares(self, price, capital):
        return (capital * (1/(1 + self.fee_percent))) // price

    def cost(self, price, nshares):
        return price * nshares * (1 + self.fee_percent)

For testing we can treat our Fee object like a callable.

[7]:
PercentageFee(.5)(price=10,capital=1000)
[7]:
{'nshares': 66.0, 'total': 990.0, 'fee': 330.0}

The above means that when we have a security with price 10 and our capital is 1000, we can buy 66 shares for a total of 990 and of that 990, 330 will be fees.

Now, we can use our PercentageFee in our backtest.

[8]:
bt = (
    builder
    .name('AAPL (fee per share)')
    .trade_cost(PercentageFee(.0005)) # 0.05 %
    .build()
)

for day, event, b in bt['2015-1-1':'2020-1-1']:
    b.pf.liquidate()
    b.long('AAPL', percent=1)
../_images/intro_fees_16_0.png

Fees are saved in the trade history as well.

[9]:
bt.trades.df
[9]:
date end_date end_event event fee initial_value nshares order_type price profit_loss_abs profit_loss_pct start_price symbol value value_pershare
0 2015-01-02 2015-01-02 close open 4.989624 9984.238150 98.0 long 99.945885 -189.541449 -0.018984 101.829067 AAPL 9794.696701 99.945885
1 2015-01-02 2015-01-05 open close 4.897348 9799.594049 98.0 long 98.995143 -98.070049 -0.010008 99.945885 AAPL 9701.524001 98.995143
2 2015-01-05 2015-01-05 close open 4.850762 9706.374763 98.0 long 97.130241 -187.611106 -0.019329 98.995143 AAPL 9518.763657 97.130241
3 2015-01-05 2015-01-06 open close 4.759382 9523.523038 98.0 long 97.395385 21.224719 0.002229 97.130241 AAPL 9544.747758 97.395385
4 2015-01-06 2015-01-06 close open 4.723676 9452.076049 97.0 long 97.139420 -29.552352 -0.003127 97.395385 AAPL 9422.523697 97.139420
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2510 2019-12-27 2019-12-27 close open 3.910230 7824.369503 27.0 long 288.333313 -39.370053 -0.005032 289.646640 AAPL 7784.999451 288.333313
2511 2019-12-27 2019-12-30 open close 3.892500 7788.891950 27.0 long 287.995045 -13.025743 -0.001672 288.333313 AAPL 7775.866207 287.995045
2512 2019-12-30 2019-12-30 close open 3.887933 7779.754140 27.0 long 290.044617 51.450510 0.006613 287.995045 AAPL 7831.204651 290.044617
2513 2019-12-30 2019-12-31 open close 3.915602 7835.120253 27.0 long 288.462644 -46.628859 -0.005951 290.044617 AAPL 7788.491394 288.462644
2514 2019-12-31 2019-12-31 close open 3.894246 7792.385640 27.0 long 292.163818 96.037456 0.012325 288.462644 AAPL 7888.423096 292.163818

2515 rows × 15 columns

Profit and loss also includes the fees.