Test the strategy

Go back to index

In previous section we calculated pair stats for period of Jan to Jun 2019. In this article we will use the pair stats calculated to back test our strategy over next six months ie- July to Dec 2019

from datetime import date
from jugaad_data import nse
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

Mean and Standard deviation from previous section

mean = 0.561384416383328
stdev = 0.043277270170811115

Download data

from_date = date(2019, 7, 1)
to_date = date(2019, 12, 31)
sym_1 = "JINDALSTEL"
sym_2 = "JSWSTEEL"
stock_1 = nse.stock_df(sym_1, from_date, to_date)
stock_2 = nse.stock_df(sym_2, from_date, to_date)

Create dataframe

ratio = pd.DataFrame()
ratio['STOCK_1'] = stock_1['CLOSE']
ratio['STOCK_2'] = stock_2['CLOSE']
ratio['VALUE'] = stock_1['CLOSE'] / stock_2['CLOSE']
ratio['DATE'] = stock_1['DATE']
ratio.sort_values(by=['DATE'], inplace=True) # Make sure dates are in ascending order

Now we use the decision matrix in previous section to run the back test.

There is additional logic to make sure that

We act only on first Long or Short signal

There will multiple ticks which will satisfy Long Signal - ratio < μ - 2σ or Short Signal - ratio > μ - 2σ, but we would want to act only on the first signal and then wait for the exit or stop loss signal.

Track Exit or Stop loss signal only if you have an open position

Again there will be multiple ticks which will statisfy exit signals - ratio > μ - 1σ or ratio < μ + 1σ, but there is no point in taking this trade if you do not have an open position.

Hope this makes it clear

What do we mean by taking a long or short position in pair?

Long position -> We buy stock 1 and sell stock 2

Short position -> We sell stock 1 and buy stock 2

long_position = "CLOSE"
short_position = "CLOSE"
print("Pair trade signals -")
for index, tick in ratio.iterrows():
    if tick['VALUE'] < mean - 2*stdev and long_position == "CLOSE":
        print("{} - LONG\t\t{:.2f}\t{:.2f}\t{:.2f}".format(tick['DATE'], tick['VALUE'], tick['STOCK_1'], tick['STOCK_2']))
        long_position = "OPEN"
        
    if tick['VALUE'] > mean - stdev and long_position == "OPEN":
        print("{} - LONG-EXIT\t\t{:.2f}\t{:.2f}\t{:.2f}".format(tick['DATE'], tick['VALUE'], tick['STOCK_1'], tick['STOCK_2']))
        long_position = "CLOSE"
    
    if tick['VALUE'] < mean - 3*stdev and long_position == "OPEN":
        print("{} - LONG-LOSS\t\t{:.2f}\t{:.2f}\t{:.2f}".format(tick['DATE'], tick['VALUE'], tick['STOCK_1'], tick['STOCK_2']))
        long_position = "CLOSE"

    if tick['VALUE'] > mean + 2*stdev and short_position == "CLOSE":
        print("{} - SHORT\t\t{:.2f}\t{:.2f}\t{:.2f}".format(tick['DATE'], tick['VALUE'], tick['STOCK_1'], tick['STOCK_2']))
        short_position = "OPEN"

    if tick['VALUE'] < mean + stdev and short_position == "OPEN":
        print("{} - SHORT-EXIT\t{:.2f}\t{:.2f}\t{:.2f}".format(tick['DATE'], tick['VALUE'], tick['STOCK_1'], tick['STOCK_2']))
        short_position = "CLOSE"
    
    if tick['VALUE'] > mean + 3 * stdev and short_position == "OPEN":
        print("{} - SHORT-LOSS\t{:.2f}\t{:.2f}\t{:.2f}".format(tick['DATE'], tick['VALUE'], tick['STOCK_1'], tick['STOCK_2']))
        short_position = "CLOSE"
Pair trade signals -
2019-08-08 00:00:00 - LONG		0.45	100.20	221.70
2019-08-13 00:00:00 - LONG-LOSS		0.43	94.80	220.15
2019-08-14 00:00:00 - LONG		0.46	103.80	227.75
2019-11-01 00:00:00 - LONG-EXIT		0.52	123.35	237.60

As you can see, the strategy made a loss in first trade and profit in second, let us look at the chart as well-

fig,ax = plt.subplots(figsize=(15,5))
# Plot the ratio
data_line = ax.plot(ratio['DATE'], ratio['VALUE'], label='Ratio')

# # For drawing mean line
mean_arr = np.array([mean for _ in ratio['VALUE']])
stdev_2_pos_line = ax.plot(ratio['DATE'], mean_arr + 3 * stdev, label='μ + 3σ', linestyle='--')
stdev_2_pos_line = ax.plot(ratio['DATE'], mean_arr + 2 * stdev, label='μ + 2σ', linestyle='--')
stdev_1_pos_line = ax.plot(ratio['DATE'], mean_arr + stdev, label='μ + 1σ', linestyle='--')
mean_line = ax.plot(ratio['DATE'], mean_arr, label='μ', linestyle='--')
stdev_1_neg_line = ax.plot(ratio['DATE'], mean_arr - stdev, label='μ - 1σ', linestyle='--')
stdev_2_neg_line = ax.plot(ratio['DATE'], mean_arr - 2 * stdev, label='μ - 2σ', linestyle='--')
stdev_2_neg_line = ax.plot(ratio['DATE'], mean_arr - 3 * stdev, label='μ - 3σ', linestyle='--')

# Make a legend
legend = ax.legend(bbox_to_anchor=(1.1, 0.75), loc='upper right')

fig.suptitle('Daily ratio', fontsize=20)
plt.savefig('backtest_ratios.png')
plt.show()
plt.close(fig)

As you can see, it did touch μ - 3σ level triggering our stop loss order. It also entered into another long position immediately on next tick and we got our exit signal after 79 days and even before that it almost touched our stop loss level again.

So important lessons learnt

  1. Losses do happen even with pair trading strategies, although extent of losses will be a bit lower as compared to naked long/short
  2. Wait times can be longer, in this case it was 79 days and patience is the key here
  3. As it can be seen clearly in the chart, the mean has definitely shifted over duration and pair stats need to be updated regularly
  4. Obviously we need a lot of improvement to this strategy, but I think this is a good start to get an understanding of basic concepts

Hope this gives you a headstart to implement your own pair trading strategies

Go back to index

Download notebook