Tutorial
December 15, 2021

Building a Heatmap of the Stock Market – Part Two: Squarified Treemaps in Elm

This article is part of a two-part series that shows you how to build heatmap visualizations and use them with ETF data.

Cloud icon

Taro Kuriyama

https://iexcloud.io/community/blog/building-a-heatmap-of-the-stock-market-part-two-squarified-treemaps-in-elm
@iexcloud
https://iexcloud.io/community/blog/building-a-heatmap-of-the-stock-market-part-two-squarified-treemaps-in-elm
@iexcloud

This article is part of a two-part series that shows you how to build heatmap visualizations and use them with ETF data.

In the world of investments, heatmaps are a popular way to analyze how specific tickers are performing relative to each other within a portfolio of stocks.

In the previous post, we covered some reasons for using a heatmap and how to acquire data for portfolio prices from IEX Cloud.

This post will cover how to serve the data to an Elm application, which will construct a heatmap using Scalable Vector Graphics (SVG) in the browser.

A live, interactive example of the heatmap that we’ll build in this post can be seen here in this dashboard.

Treemap example

Why Elm

The Elm programming language is a statically typed, functional programming language for the web, which compiles to JavaScript. In addition to the standard benefits of functional programming, it features an especially friendly compiler, making it easier to debug and refactor code. A typical typo results in something like this, with helpful suggestions:


Serving Data: The Code Gen Approach

Let's assume a database holds the data we need.

The standard paradigm for serving data is to make a network IO request (e.g. HTTP GET from a backend server, returning some JSON). An alternative is to embed the data in code, using code generation to automatically refresh the code. As might be expected, such alternatives involve trade-offs.

For example, network IO allows greater flexibility, as data can be called asynchronously from a variety of sources. On the other hand, such data needs to be either pre-generated as static resources or served up dynamically, and the network IO may fail for various reasons. Additionally, the data still needs to be parsed once it's received successfully.

With code gen, on the other hand, all the work is done up front. Effort is required to develop the code gen logic. And it's likely that the data can only be loaded synchronously with the web application. On the other hand, the data may benefit from compression (e.g. the gzip'ed .js.gz source file in the case of Elm) and is guaranteed to be available immediately in the native data types used by the application.

In this particular case, the target Elm code output is simple, and the data size is relatively small (especially compressed), so the code gen approach follows.

The full code gene logic is beyond the scope of this article, but as an example, the following Python function generates an Elm module declaration:


Calling module generates a string of Elm code.


With some careful recursion for nested data structures, such small functions can be combined to produce a complete Elm file like this one (which is the actual data source for the heatmap described in this post).

Squarified Treemaps

Although a heatmap may assume theoretically any visual form, a squarified treemap is common. A treemap is comprised of groups of rectangles, where the area of each rectangle corresponds to its weight (in this case, the weight of a stock in a portfolio).

Squares are relatively easy for the human eye to compare by area, so the algorithm attempts to minimize the aspect ratios of all rectangles (i.e. to "squarify" them using a greedy heuristic algorithm).

Here, we'll look at the basic ideas behind the algorithm. The full algorithm is described in this paper by Bruls, Huizing, and van Wijk, and a complete implementation in Elm is available here.

Partitioning

We can think of the squarified treemap as the partitioning of a rectangle into smaller rectangles. Given a list of areas and a rectangle to fill, we want to partition the rectangle proportionally according to the list of areas (or‚ as cells).

Bruls et al. use the abstraction of a "row" to group cells together, where a row may be a horizontal or vertical subdivision of the larger rectangle. The treemap is constructed one row at a time.

The type signature for partition corresponds directly to the above.


partition is implemented as a fold over the input areas. The function has some additional logic to handle the use of non-empty lists and the order of accumulated data from the fold.


As for the actual folding logic, it implements the pseudocode from Bruls et al. almost directly. (Note that the paper contains a typo, described at the end of this section). The main idea is to keep adding cells to the current row, so long as the row’s worst aspect ratio does not worsen.


There is some housekeeping required to update the working dimensions as new rows are added. The example figure from Bruls et al. is helpful in visualizing the algorithm's steps:

Treemap subdivision example figure


We can test partition against the figure depicted, where the rectangle to partition has dimensions 6 x 4.


Because the algorithm always follows the shorter length of the remaining subrectangle, the above minimal representation of the partition result is all that’s needed to render the treemap.

In addition to the full source code, interested readers will find additional implementation notes in this post.

Note

For readers who intend to follow the Bruls et al. paper, there is a typo in the pseudocode in Section 3.2 (acknowledged by the authors).


The comparison operator <= should instead be >=.

Rendering the Squarified Treemap

Now that we have the squarified treemap algorithm, we can feed it some data and render an SVG.

The shape of data is as follows.


In this specific case, each GridSeries is a sector of the portfolio, and each sector contains a list of GridTriple that hold (stock name, previous period price, this period price).

So we have a two-level treemap:

  1. The treemap is broken down into sectors
  2. Each sector is broken down into its constituent stocks

Here is a concrete data snippet:


The following function shows the core of the rendering logic:


The gist is:

  1. Create a Squarified Treemap of sectors (groups and groupCells)
  2. For each sector, create its Squarified Treemap (subtrees)
  3. Render the sector treemaps
  4. Render the sectors (the borders)
  5. Render the interactive tooltips

The order of rendering is important, as SVGs do not currently support z-indexing. Elements rendered last are placed on top.

The complete rendering code is available here.

Wrapping Up

The Elm code, once compiled, can be embedded as a JavaScript source in any web application. And with the code gen approach, it's completely stand-alone (just recompile with an updated data file!).

For interested readers, here are a few links to further resources:


About the Author

Taro has worked in the exchanges, algorithmic trading, and quantitative investing space for over a decade. He’s also a hobbyist programmer, with particular interest in data visualization and functional programming.


IEX Cloud Services LLC makes no promises or guarantees herein regarding results from particular products and services, and neither the information, nor any opinion expressed here, constitutes a solicitation or offer to buy or sell any securities or provide any investment advice or service. Any views or opinions expressed here in this blog do not represent the official opinions of IEX Group or IEX Cloud.