This article shares the simplest example of implementing backtesting in quantitative investment. This example only illustrates the principle of backtesting implementation. In actual situations, it can be more complex than this example.
- Building buy and sell signals
Build a buy signalsignal=1
, sell signalsignal=0
, andNaN
for others.
Note that we assume full position trading here, and only the first signal in a continuous sequence has actual operational significance. Once the first signal is executed, even though there are still signals afterwards, there is no more money to buy because of full position trading. The same applies to selling.
- Converting signals to holding status
Build a holding statuskeep=1
, empty positionkeep=0
. Assuming full position trading,keep=signal
.
# Since we assume full position trading, signal=keep, where 1 represents holding and 0 represents empty position.
test_kl['keep'] = test_kl['signal']
Fill the NaN values in the keep column with the previous element, maintaining consistency.
# Fill the NaN values in the keep column with the previous element, making keep represent the final trading holding status.
test_kl['keep'].fillna(method='ffill', inplace=True)
- Calculate benchmark returns
The concept of benchmark returns here is the daily returns from holding stocks from the first day to the last day. The calculation of daily returns is: (today's closing price - previous day's closing price) / previous day's closing price.
test_kl['benchmark_profit2'] =
test_kl['close'] / test_kl['close'].shift(1) - 1
test_kl['benchmark_profit'] =
np.log(test_kl['close'] / test_kl['close'].shift(1))
- Calculate strategy returns
Strategy returns are the holding status multiplied by benchmark returns. The holding status only has data of 1 and 0, and after multiplication with benchmark returns, it acts like a filter that automatically filters out data of 0.
test_kl['trend_profit'] = test_kl['keep'] * test_kl['benchmark_profit']
- Visualize the comparison of returns
Cumulatively add benchmark returns and strategy returns on a daily basis and compare them.
test_kl[['benchmark_profit','trend_profit']].cumsum().
plot(grid=True, figsize=14, 7));