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
Hope this gives you a headstart to implement your own pair trading strategies