Machine Learning in Action in Finance: Using Graphical Lasso to Identify Trading Pairs in International Stock ETFs
By: Jason X. Yang
Tags: Machine Learning, Artificial Intelligence, Stock Market, Data Visualization, Trading

TL;DR: In this post, I will try to tackle the problem of identifying a portfolio of correlated stocks, which is generally the first and most important step of pairs trading strategies. The approach takes advantage of the graphical lasso algorithm, which has proved itself a powerful machine learning solution to many practical problems such as identifying co-varying brain regions, social media network analysis, etc. This is the first in the tutorial series on this topic and focuses on one of its applications; my second post addresses the beautiful theory behind it in gory details, and future posts will demonstrate how the algorithm brings surprising marketing insights. So follow me and stay tuned.

Photo by Markus Spiske on Unsplash

The world of equity investing is alluring and many of us with technical skills have wished to break into it. Yet, squandering our time sifting through the fundamentals of the universe of stocks in, say, SP500 alone is in itself overly dull and menial for the creative minds.

Fortunately, with knowledge in coding and interest in machine learning, you are more than well-equipped to make some buzz in the field of quantitative investing. Below, I will illustrate how to, as the first step to create your own trading strategy, identify among international stock ETF tickers meaningful pairs and clusters to create a portfolio, using a powerful machine learning algorithm called graphical lasso, which will make the first and most important step towards building your own Pairs Trading strategy.

Throughout the tutorial, I will keep the details to the minimally and meaningfully viable to get you started quickly, and you are free to explore more possibilities with this algorithm.

Buckle up and let us hit the road!

Photo by Diego Jimenez on Unsplash

Brief Intro to Graphical Lasso

Remember the regression method called lasso, used to induce a sparse solution to your regression problem by adding an L1 regularization term? Graphical lasso is its extension to the world of graphs.

Instead of estimating coefficients for independent variables in regression problems, graphical lasso estimates the precision (inverse covariance) matrix of your data. Thus, instead of driving many of the coefficients to 0 as in lasso regression, it pushes many values in the matrix to 0.

Why ‘graphical’? Because the precision matrix can be shown to correspond uniquely to an undirected graph(more on this in later sections).

In other words, the goal of graphical lasso is to induce from your data an undirected graph with sparse connections. This fact will come handy later when we try to illustrate the ETF graph and identify possible clusters.

For a more mathematically concrete treatment of the algorithm, please refer to my second article in this series, which analyzes the algorithm step by step.

Data Preparation

In this experiment, let us use the daily closing prices of the tickers. We will use historical data provided by Yahoo Finance. For example, to download the historical data for ticker QQQ to your local machine, you can navigate to https://finance.yahoo.com/quote/QQQ/history?p=QQQ, select the date range, and then click on ‘Download’. In our experiment, let us say we are particularly curious about certain countries and retrieve data for the following country ETFs:

#Setting up the mapping from ticker to country

etfs = {"EWJ":"Japan","EWZ":"Brazil",

"FXI":"China","EWY":"South Korea",

"EWT":"Taiwan","EWH":"Hong Kong",

"EWC":"Canada","EWG":"Germany",

"EWU":"United Kingdom","EWA":"Australia",

"EWW":"Mexico","EWL":"Switzerland",

"EWP":"Spain","EWQ":"France",

"EIDO":"Indonesia","ERUS":"Russia",

"EWS":"Singapore","EWM":"Malaysia",

"EZA":"South Africa","THD":"Thailand",

"ECH":"Chile","EWI":"Italy",

"TUR":"Turkey","EPOL":"Poland",

"EPHE":"Philippines","EWD":"Sweden",

"EWN":"Netherlands","EPU":"Peru",

"ENZL":"New Zealand","EIS":"Israel",

"EWO":"Austria","EIRL":"Ireland","EWK":"Belgium"}

Then we do a little housekeeping to streamline the data format for ease of later processing:

symbols, names = np.array(sorted(etfs.items())).T

Now, you should have downloaded the data files for those country ETFs. The next step is to collate them into one big data file where columns are the ticker names like ‘’EWK’ as above in the sorted order, rows being dates in temporal order. You could do this either using a simple Python script or some other favorite tools of yours.

Let us read in the collated file into a Pandas dataframe:

#Read in series of daily closing prices

#The file 'input.csv' uses the tickers above as columns, and dates #as index in df

df = pd.read_csv("input.csv", index_col='Date')

Data Processing

There is one thing we need to take care of before applying the algorithm:

#Convert price series to log return series

df = np.log1p(df.pct_change()).iloc[1:]

The line of code transforms the time series of daily closing prices into a series of log returns. Note that this step is not mandatory, but generally is a good practice in this context. It can be argued as below:

While stock prices are not normally distributed, log returns aren’t either, but are closer to normality than prices. And applied mathematicians love normality because it relieves so much pain when throwing at the data different analysis tools/algorithms, most of which assuming data normality to function meaningfully.

The bottom line: learn to love Gauss and acquire a taste for his distribution, and do whatever you can to uncover it in your data.

The Core

Now we are ready to apply the algorithm:

#Calling Glasso algorithm

edge_model = covariance.GraphicalLassoCV(cv=10)

df /= df.std(axis=0)

edge_model.fit(df)

#the precision(inverse covariance) matrix that we want

p = edge_model.precision_

Here we leverage the sklearn package in Python for its handy implementation of graphical lasso. Note how I retrieve the resulting precision (inverse covariance) matrix, which is our focus here. The reason we go for the precision (inverse covariance) matrix, instead of the covariance matrix, is what sets graphical lasso apart from other boring algorithms and makes the topic of my next article. For now, it suffices to say that:

(1) Yes, most people do covariance analysis, but that might be because that’s all they know. Now that you have heard of the inverse covariance, please use it to your advantage from now on.

(2) A precision matrix can uniquely define an un-directed graph: the value of its cell (i,j), if equal to 0, means conditional independence between variables i and j, and if non-zero, means the degree of conditional (in)dependence; and these dependence relations map to an un-directed graph such that there is no link between two vertices if they are conditionally independent.

With that said, let us take a look at what it looks like using Seaborn:

sns.heatmap(p)

And we get:

X- and Y- labels follow the sorted order of ETFs

It looks nice but does not actually tell us much yet. The power of graphical lasso lies in its ability to learn the precision matrix and then to map the matrix to a graph.

So let us do that:

#prepare the matrix for network illustration

p = pd.DataFrame(p, columns=cols, index=cols)

links = p.stack().reset_index()

links.columns = ['var1', 'var2','value']

links=links.loc[ (abs(links['value']) > 0.17) & (links['var1'] != links['var2']) ]

#build the graph using networkx lib

G=nx.from_pandas_edgelist(links,'var1','var2', create_using=nx.Graph())

pos = nx.spring_layout(G, k=0.2*1/np.sqrt(len(G.nodes())), iterations=20)

plt.figure(3, figsize=(30, 30))

nx.draw(G, pos=pos)

nx.draw_networkx_labels(G, pos=pos)

plt.show()

The code snippet above converts the precision matrix to a graph and uses the Networkx package in Python to draw it. I have tried to make the nodes as sparse as possible so that the node labels do not overlap, but it is still not quite satisfactory. Try and see it yourself.

To solve that problem, let us do one more step and export the graph into Gephi :

nx.write_gexf(G, 'graph.gexf')

Open the file in Gephi and adjust its layout, and we have the following interesting clustering of the country ETFs:

Clustering: order out of chaos

Naively, you can treat the three clusters as three separate portfolios and go on to figure out the hedge ratios among each of them. Or better still, you might want to try adding the edge weights and illustrating the bond strengths between nodes, and further split the mega-cluster into smaller ones based on that information. Feel free to add in more data and more stock tickers, play around the code and get started on your own research.

The clustering layout is amusing to gaze at. It matches geography quite well, which is not surprising given that geographically clustered countries tend to form closely knit common markets and tend to have roughly co-variate stock markets. Equally interesting is how the East Asian market connects to the European cluster by way of Singapore first, then Australia and Canada. I am in no position to analyze deeper into the connections and will leave it now to amuse or bemuse the curious readers.

Photo by Christine Roy on Unsplash

Please do bear in mind the clusters will change as the world evolves and countries incessantly befriend or unfriend each other. Such is expected of any trading algorithm to account for the paradigm shifts in the world.

Do your own experiments and make this your first step towards building your own trading algorithm. Stay tuned for more on this and other interesting topics on machine learning, finance and many more.

Note from Towards Data Science’s editors: While we allow independent authors to publish articles in accordance with our rules and guidelines, we do not endorse each author’s contribution. You should not rely on an author’s works without seeking professional advice. See our Reader Terms for details.