Table of Contents
Recap
Incorporating Slippage
Slippage Costs
Cutting It Short
Recap
In our last article we had a look at how to rebalance positions based on an error threshold instead of doing it daily to reduce transactions and trading costs. We're now only adjusting our positions when our current risk deviates more than 10% from the ideal risk.
We're almost finished with our backtest. There are still some kinks we need to iron out before we can really use it for strategy development though. So let's get right to it!
Incorporating Slippage
First, let's quickly reiterate what slippage is:
Slippage is the difference between the price you think you'll be trading at and the price you're actually trading at. Just because you tried to enter a trade immediately after the close, doesn't mean you'll always get that price. Prices tend to be noisy, especially around higher timeframe closes. Lot's of other people use the same timeframe as we are so we're not going to be the first in line to trade right away. Depending on the instruments volatility, liquidity and our size and the exchanges matching mechanism, we're going to get served for different prices than intended, especially when using market orders.
For our model, slippage is not all that important. Since we probably only want to trade the most liquid instruments on the daily timeframe with very little size (total of $10,000 bankroll), we don't care all that much about things like market impact. It is enough for us to simply apply a standard pnl reduction to each trade to account for slippage.
So what amount of slippage should we deduct? Again, slippage depends on the size we're trading, current orderbook liquidity, etc. We're going to continue to assume that the current orderbook supports our trades without blasting through it. We mainly want to simulate not getting filled right at the top but maybe one level below that because we weren't first in line.
Assuming slippage of 0.01% to 0.05% or 1 to 5 basis points (bps) respectively is going to be enough. If you want to take it up a notch to purposely simulate higher volatility environments or stress test your model you can let it spike to 0.1% or even 0.25%.
To apply the slippage correctly, which is basically price moving against us, we need to know the direction of the position we're taking and then adjust the close
price accordingly:
[...]
slippage_percent = 0.1
slippage_percent_dec = slippage_percent / 100
for index, row in df.iterrows():
[...]
# simulate trading
if df.index.get_loc(index) > 0:
[...]
# we're trading
if contract_deviation > rebalance_err_threshold:
if ideal_pos_contracts > 0:
position_direction = 1
else:
position_direction = -1
df.at[index, 'position_direction'] = position_direction
slippage_amount_per_contract = row['close'] * slippage_percent_dec
close_slipped = row['close'] + slippage_amount_per_contract if position_direction >= 0 else row['close'] - slippage_amount_per_contract
df.at[index, 'close_slipped'] = close_slipped
[...]
The above code re-calculates the price we would've executed at based on our configured slippage of 0.1% (10 bps) if we had traded 1 contract. Next we need to adjust this figure to the amount of contracts actually traded:
# we're trading
if contract_deviation > rebalance_err_threshold:
if ideal_pos_contracts > 0:
position_direction = 1
else:
position_direction = -1
df.at[index, 'position_direction'] = position_direction
slippage_amount_per_contract = row['close'] * slippage_percent_dec
close_slipped = row['close'] + slippage_amount_per_contract if position_direction >= 0 else row['close'] - slippage_amount_per_contract
df.at[index, 'close_slipped'] = close_slipped
slippage_paid = abs(contract_diff) * slippage_amount_per_contract * contract_unit
df.at[index, 'slippage_paid'] = slippage_paid
rebalanced_pos_contracts = ideal_pos_contracts
notional_traded = contract_diff * notional_per_contract
else:
slippage_paid = 0
rebalanced_pos_contracts = current_pos_contracts
notional_traded = 0
Deducting the costs from our performance now becomes really easy, we just substract it from our pnl:
# Calculating Performance
instr_raw_returns = df['instr_price_returns']
strat_raw_usd_returns = df['rebalanced_pos_contracts'].shift(1) * instr_raw_returns
df['strat_raw_usd_returns'] = strat_raw_usd_returns.fillna(0)
# Deduct fees
strat_usd_returns = df['strat_raw_usd_returns'] - df['fees_paid']
df['strat_usd_returns_after_fees'] = strat_usd_returns
df['strat_usd_returns_postcost'] = df['strat_usd_returns_after_fees']
# Deduct slippage
strat_usd_returns = df['strat_usd_returns_after_fees'] - df['slippage_paid']
df['strat_usd_returns_after_slippage'] = strat_usd_returns
df['strat_usd_returns_postcost'] = df['strat_usd_returns_after_slippage']
[...]
# Deduct funding
# Calculate SR
Slippage Costs
Let's run our backtest again.
# Strategy Total Return 948.9002953840393
# Strategy Avg. Annual Return 65.07865610957803
# Strategy Daily Volatility 1.8181518825213718
# Strategy Sharpe Ratio 1.8735357472190997
# Fees paid 1038.6238698915147
# Slippage paid 1888.4070361663905
# Funding paid 3130.3644113437113
# Strategy Ann. Turnover 37.672650094739545
# Pre-Cost SR 1.9914208916281093
# Post-Cost SR 1.8735357472190994
# Risk-Adjusted Costs 0.11788514440900988
# [...]
We paid a total of $1,888 in slippage. As you can see this is far more than the $1,038 fees paid. This is due to the fact that we set the slippage fairly high to 0.1%. I like to stay on the more conservative side and keep it this high for now.
Our risk adjusted costs increased from ~ 0.0731 SR units to ~0.1179; about ~61.3%. Definitely nothing to sneeze at!
Since our model is using market orders, we also have to pay the bid-ask spread. For this model, where we want to trade only the most liquid instruments, we're going to assume that the bid-ask spread is already accounted for via our slippage calculations.
Cutting It Short
This is it for this week. I'm still quite tangled up in some personal and work matters so this article too is a rather short one. As soon as these get sorted out, the output will increase again. Promise!
- Hōrōshi バガボンド
Newsletter
Once a week I share Systematic Trading concepts that have worked for me and new ideas I'm exploring.
Recent Posts




