JavaScript - - By Roman Lubushkin

Visualize Large Date and Time-Based Datasets with AnyStock

I introduced the basics of the AnyChart product family in my previous article on SitePoint. This time I will tell you about how AnyStock is helpful in visualizing stock data as well as any large date and time-based data sets (and how to use it best).

AnyStock is part of the AnyChart JavaScript charting library that is marketed as a set of products:

  • AnyChart – common chart types,
  • AnyMap – geo maps and seat maps,
  • AnyGantt – Project Gantt, Resource, and PERT charts,
  • AnyStock – visualization of big date/time-based data sets, the features of which are the topic of this article.

Because AnyChart is essentially one big JavaScript charting library, the API is common and all charts are configured in pretty much the same way, sharing themes, settings, and ways to load data. That said, most of the things and approaches you’ll learn about in this article can be easily applied to making other charts and maps.

AnyStock stock charts are a special type of charts that can effectively visualize big amounts of date/time-based data. It has many special features such as zoom with Scroller UI, synchronized plots, data grouping, and so on. AnyStock is completely compatible with all other AnyChart charts, and besides having all basic stock features such as grouping, streaming, scrolling, etc., AnyStock stocks also have multiple technical indicators built-in and ability to customize them.

To put it in a nutshell, in this article I will:

AnyStock Quick Start

To start using AnyStock in your HTML page you need three simple things. The first one is a link to the library’s JavaScript file. The second one is a block-level HTML element; here’s a sample HTML template you may use:

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <script src="https://cdn.anychart.com/js/7.14.3/anystock.min.js"></script>    
    <style>
    html, body, #container {
      width: 100%;
      height: 100%;
    }
  </style>    
    <title>AnyStock Basic Example</title>    
  </head>
  <body>
    <div id="container"></div>
    <script>
      // AnyStock code here
    </script>
  </body>
</html>

And here is a basic code to create a simple Japanese Candlestick chart, along with the volume on the second plot and scroller that shows volume as an area:

anychart.onDocumentReady(function() {
    // data
    var data = [
      ['2015-12-24', 511.53, 514.98, 505.79, 506.40, 1050016],
      ['2015-12-25', 512.53, 514.88, 505.69, 505.34, 1050015],
      ['2015-12-26', 511.83, 514.98, 505.59, 506.23, 1050016],
      ['2015-12-27', 511.22, 515.30, 505.49, 506.47, 1250016],
      ['2015-12-28', 510.35, 515.72, 505.23, 505.80, 1250015],
      ['2015-12-29', 510.53, 515.86, 505.38, 508.25, 1250018],
      ['2015-12-30', 511.43, 515.98, 505.66, 507.45, 1250017],
      ['2015-12-31', 511.50, 515.33, 505.99, 507.98, 1250017],
      ['2016-01-01', 511.32, 514.29, 505.99, 506.37, 1250016],
      ['2016-01-02', 511.70, 514.87, 506.18, 506.75, 1250016],
      ['2016-01-03', 512.30, 514.78, 505.87, 508.67, 1250018],
      ['2016-01-04', 512.50, 514.77, 505.83, 508.35, 1250018],
      ['2016-01-05', 511.53, 516.18, 505.91, 509.42, 1050019],
      ['2016-01-06', 511.13, 516.01, 506.00, 509.26, 1050019],
      ['2016-01-07', 510.93, 516.07, 506.00, 510.99, 1050110],
      ['2016-01-08', 510.88, 515.93, 505.22, 509.95, 1350019],
      ['2016-01-09', 509.12, 515.97, 505.15, 510.12, 1350110],
      ['2016-01-10', 508.53, 516.13, 505.66, 510.42, 1350110],
      ['2016-01-11', 508.90, 516.24, 505.73, 510.40, 1350110]
    ];

    // create stock chart
    chart = anychart.stock();
    chart.title("AnyStock: Basic Sample");

    // create data table and load data in it
    table = anychart.data.table();
    table.addData(data); 

    // map data - name fields
    mapping = table.mapAs({open: 1, high: 2, low: 3, close: 4, value: 5});

    // create series to show stock changes
    chart.plot(0).candlestick(mapping).name("Stock name");
    // create second plot and show trading volume there as a line
    chart.plot(1).column(mapping).name("Volume");
    // create series to show trading volume
    chart.scroller().area(mapping);

    // set HTML container and draw chart into it
    chart.container('container').draw();     
});

Data mapping

In the previous article about AnyChart, I demonstrated multiple ways to load data into the library, but an important and astonishing feature called data mapping has been left out.

What is data mapping? Let’s delve into it a little bit, as we have already made good use of this feature in the basic AnyStock sample and will continue to use it in every sample in this article.

As this Wikipedia article on data mapping tells us:

In computing and data management, data mapping is the process of creating data element mappings between two distinct data models. Data mapping is used as the first step for a wide variety of data integration tasks including data transformation or data mediation between a data source and a destination.

One of the ways to translate this definition into layman’s terms: “Data mapping is a rule that tells what is what in the data and prepares some data to be used somewhere else.”

I know it may sound crude and an oversimplification, but it still might be helpful.

Now, let’s look how the data mapping happens in the example above. First, we have an array with numbers and dates that we load into the table:

// data
var data = [
  ['2015-12-24', 511.53, 514.98, 505.79, 506.40, 1050016],
  ['2015-12-25', 512.53, 514.88, 505.69, 505.34, 1050015],
  ['2015-12-26', 511.83, 514.98, 505.59, 506.23, 1050016],
  ['2015-12-27', 511.22, 515.30, 505.49, 506.47, 1250016],
  ['2015-12-28', 510.35, 515.72, 505.23, 505.80, 1250015],
  ['2015-12-29', 510.53, 515.86, 505.38, 508.25, 1250018],
  ['2015-12-30', 511.43, 515.98, 505.66, 507.45, 1250017],
  ['2015-12-31', 511.50, 515.33, 505.99, 507.98, 1250017]
];

// create data table and load data in it
table = anychart.data.table();
table.addData(data); 

There is nothing in this data that explains which columns contain prices or anything else. Open, high, low and close prices can come in any order. So how can the AnyStock library understand what’s what? By using data mappings!

In this case, we need only one:

// map data - name fields
mapping = table.mapAs({open: 1, high: 2, low: 3, close: 4, value: 5});

As we see, the mapAs() method takes an object where we decide what names a field gets and then we pass this mapping to the functions that show series:

// create series to show stock changes
chart.plot(0).candlestick(mapping).name("Stock name");
// create second plot and show trading volume there as line
chart.plot(1).column(mapping).name("Volume");
// create series to show trading volume
chart.scroller().area(mapping);

AnyStock expects a mapping with open, high, low and close fields for a candlestick series and value for area and column and this allows us to use one mapping in this case.

Let’s now move to the next sample where I will show how the data mapping allows us to do more in a more complex case. Our data comes in the JSON format and we have no control over it, which is usually a case when you have access to some kind of API.

We’ll assume data comes like this:

var data = [
  {'date': '2015-12-24', 'o': 511.53, 'h': 514.98, 'l': 505.79, 'c': 506.40, 'vol': 1050016},
  {'date': '2015-12-25', 'o': 512.53, 'h': 514.88, 'l': 505.69, 'c': 505.34, 'vol': 1050015},
  {'date': '2015-12-26', 'o': 511.83, 'h': 514.98, 'l': 505.59, 'c': 506.23, 'vol': 1050016},
  {'date': '2015-12-27', 'o': 511.22, 'h': 515.30, 'l': 505.49, 'c': 506.47, 'vol': 1250016},
  {'date': '2015-12-28', 'o': 510.35, 'h': 515.72, 'l': 505.23, 'c': 505.80, 'vol': 1250015},
  {'date': '2015-12-29', 'o': 510.53, 'h': 515.86, 'l': 505.38, 'c': 508.25, 'vol': 1250018},
  {'date': '2015-12-30', 'o': 511.43, 'h': 515.98, 'l': 505.66, 'c': 507.45, 'vol': 1250017},
  {'date': '2015-12-31', 'o': 511.50, 'h': 515.33, 'l': 505.99, 'c': 507.98, 'vol': 1250017},
  {'date': '2016-01-01', 'o': 511.32, 'h': 514.29, 'l': 505.99, 'c': 506.37, 'vol': 1250016},
  {'date': '2016-01-02', 'o': 511.70, 'h': 514.87, 'l': 506.18, 'c': 506.75, 'vol': 1250016}];

With many other solutions, you’d be forced to create a script that changes the data structure, renames the fields, loops through one data set and forms a new one. This is not the case with AnyChart.

First, we load data into the table and tell it where the argument (timestamp) is:

// create data table and load data in it
table = anychart.data.table('date');
table.addData(data); 

Then we create mappings and, in this case, we will create four of them. Three mappings consider some of the fields as the value, and one takes ‘h’ and ‘l’ fields and considers them as high and low:

// map data
mapping_1 = table.mapAs({value: 'o'});
mapping_2 = table.mapAs({value: 'c'});
mapping_3 = table.mapAs({high: 'h', low: 'l'});
mapping_4 = table.mapAs({value: 'vol'});

Now we are ready to pass these mappings to four series constructors:

// create a series to show open prices line
chart.plot(0).line(mapping_1).name("Open");
// create a series to show close prices line
chart.plot(0).line(mapping_2).name("Close");      
// create a second plot and show trading volume there as a line
chart.plot(0).rangeSplineArea(mapping_3).name("Range");
// create a series in a scroller
chart.scroller().stick(mapping_4);

… and enjoy our AnyStock Advanced Mappings sample on CodePen:

See the Pen AnyStock: Advanced Mapping Sample by SitePoint (@SitePoint) on CodePen.

Neat, right? Data mapping is a flexible tool which saves a lot of effort when you integrate 3rd party sources, which I will demonstrate in the Google Finance and Xignite API samples below.

Loading a CSV File

Before reviewing the next sample, you should understand how to load CSV files with AnyChart. You can actually do this using jQuery or any other AJAX library of your choice, but AnyChart has its own helper script: AnyChart Data Adapter. It allows you to easily load CSV, JSON and XML files, as well as load data from HTML tables to AnyChart.

To learn more, read the Getting Started with AnyChart: Loading CSV Files section from my previous article.

Google Finance

Disclaimer: Google Finance provides data without obligations of any kind and AnyChart has no affiliation with Google. This sample is created to show how data can be obtained from third-party sources only, not as a recommendation to rely on Google Finance as a single (or best) stock prices source.

To load CSV data from Google Finance, you need to know:

With all this at hand, it is just 10 lines of code to have your chart load and show the stock data:

// stock symbol to load
ticker = "MSFT";
anychart.onDocumentReady(function() {
    // create chart
    chart = anychart.stock();
    chart.title("AnyStock: Data from Google Finance");
    // set HTML container and draw chart into it
    chart.container('container').draw(); 

    // load CSV from Google Finance, we use https://crossorigin.me/ to avoid cross origin problems
    // they still might occur, just reload sample to see appropriate result
    anychart.data.loadCsvFile("https://crossorigin.me/https://www.google.com/finance/historical?output=csv&q=" + ticker, function (data) {
      // create data table structure
      table = anychart.data.table();
      // load data received
      table.addData(data); 
      // map data
      mapping = table.mapAs({open: 1, high: 2, low: 3, close: 4, value: 5});
      // create series to show chart prices
      chart.plot(0).ohlc(mapping).name(ticker);
      // create series to show trading volume in scroller
      chart.scroller().column(mapping).name("Volume");
    });
});

Here is a sample on CodePen that demonstrates loading data from Google Finance to AnyStock. I’ve added a Preloader element that comes with the AnyChart charting package, but you can use any other if you prefer:

See the Pen AnyStock: Loading data from Google Finance by SitePoint (@SitePoint) on CodePen.

Watch Visualize Data with D3.js

Illustrate Your Data with JavaScript

Xignite API

Below, we see another example of how data mapping simplifies things.

Xignite, Inc. provides cloud-based financial market data APIs to help companies deliver real-time and reference market data. Xignite’s clients include such firms as Betterment, FutureAdvisor, Motif Investing, Personal Capital, Robinhood, StockTwits, Wealthfront, and Yodlee.

Xignite has tons of APIs, but we will create a sample with one called “GetGlobalHistoricalQuotesAsOf”. It returns a range of historical quotes for a security based on an end date and a number of periods.

We’ve taken a sample set that is typical of what this API may return. Here is what it looks like (only two points are shown for clarity, the sample contains more):

{
    "Security": {
        "CIK": "0001446250",
        "Cusip": "D12096109",
        "Symbol": "BMW.XETR",
        "ISIN": "DE0005190003",
        "Valoren": "324410",
        "Name": "BMW",
        "Market": "XETRA",
        "CategoryOrIndustry": "Invalid",
        "Outcome": "Success",
        "Message": null,
        "Identity": null,
        "Delay": 0
    },
    "StartDate": "10/4/2016",
    "EndDate": "4/2/2017",
    "GlobalQuotes": [
        {
            "Security": null,
            "Date": "10/4/2016",
            "Last": 77.29,
            "Open": 76,
            "High": 77.68,
            "Low": 75.79,
            "Volume": 2802839,
            "LastClose": 74.81,
            "ChangeFromOpen": 1.29,
            "PercentChangeFromOpen": 1.697,
            "ChangeFromLastClose": 2.48,
            "PercentChangeFromLastClose": 3.315,
            "SplitRatio": 1,
            "CummulativeCashDividend": 0,
            "CummulativeStockDividendRatio": 1,
            "Currency": "EUR",
            "AdjustmentMethodUsed": "SplitAndProportionalCashDividend",
            "DataConfidence": "Valid",
            "Outcome": "Success",
            "Message": null,
            "Identity": null,
            "Delay": 0
        },
        {
            "Security": null,
            "Date": "10/5/2016",
            "Last": 78.18,
            "Open": 76.65,
            "High": 78.39,
            "Low": 75.98,
            "Volume": 1831075,
            "LastClose": 77.29,
            "ChangeFromOpen": 1.53,
            "PercentChangeFromOpen": 1.996,
            "ChangeFromLastClose": 0.89,
            "PercentChangeFromLastClose": 1.152,
            "SplitRatio": 1,
            "CummulativeCashDividend": 0,
            "CummulativeStockDividendRatio": 1,
            "Currency": "EUR",
            "AdjustmentMethodUsed": "SplitAndProportionalCashDividend",
            "DataConfidence": "Valid",
            "Outcome": "Success",
            "Message": null,
            "Identity": null,
            "Delay": 0
        }
    ],
    "Outcome": "Success",
    "Message": null,
    "Identity": "Request",
    "Delay": 0.21875
}

Working with this complex JSON in AnyStock is so easy that I decided to play around and showcase some other features of this library, such as an ability to show different data on the same plot, multiple scales, and axes and labels formatting:

anychart.onDocumentReady(function() {
      // create chart and set title
      chart = anychart.stock();    
      chart.title("AnyStock: Data from Xignite API JSON");

      // load data from Xignite
      // see function in sample on Codepen https://codepen.io/anystock/pen/gmqvdy
      data = loadDataFromXignite();
      // get ticker ID from JSON object
      ticker = data.Security.Symbol;
      // create table and specify argument field 
      table = anychart.data.table("Date");
      // load data into table
      table.addData(data.GlobalQuotes); 

      // map data for OHLC
      ohlc_mapping = table.mapAs({ "high": "High", "low": "Low", "open": "Open", "close": "LastClose"});

      // map data for volume
      volume_mapping = table.mapAs({ "value": "Volume"});

      // create series to show chart prices
      price = chart.plot(0).ohlc(ohlc_mapping).name(ticker);

      // Create and tune additional Y scale
      // to show volume on the same plot as prices
      var extraYScale = anychart.scales.linear();
      // make volume always appear in lower 1/8th part of plot
      extraYScale.maximumGap(8);
      // Create and tune additional Y axis
      var extraYAxis = chart.plot(0).yAxis(1);
      extraYAxis.orientation("right");
      extraYAxis.labels().format("{%Value}{scale:(1000000)|(Mln)}");
      extraYAxis.scale(extraYScale); 
      // give space to labels to the right
      chart.padding().right(50);

      // create series to show trading volume        
      volume = chart.plot(0).column(volume_mapping).name("Volume"); 
      // bind it to extra scale
      volume.yScale(extraYScale);

      // set HTML container and draw chart into it
      chart.container('container').draw();   
  });

This sample on CodePen shows how AnyStock uses data from Xignite GetGlobalHistoricalQuotesAsOf API:

See the Pen AnyStock: Mapping data from Xignite API by AnyStock (@anystock) on CodePen.

Conclusion

In this article, I have introduced you to the AnyStock module of the comprehensive AnyChart JavaScript charting library (or the AnyStock library for stock and large date/time based interactive charts as it is marketed). I have shown how you can easily integrate third-party data into financial charts, which is very important because data is a key element when it comes to finances.

AnyStock has a lot of other features, such as drawing tools and annotations, various data grouping options, range selection UI controls, and – the last but not least – built-in technical indicators with ability to customize them, along with ability to change the rendering of default series types.

The last two features mentioned (custom indicators and custom series) are especially neat but working with them requires some degree of understanding of mathematics and data processing. At first, I was considering adding examples of these features in this article but then abandoned this idea: AnyChart (including AnyStock) is an easy tool to use for most of the time, and it has powerful extension points. But ~~with great power comes great responsibility~~ when it comes to advanced topics, extended coding and expertise are required, whereas I don’t want to scare you off with more of information and text in the same article.

That said, I urge you to try the AnyStock basics and look into the beauty of data mapping. You can expect to see a “dig deeper” article soon on how to implement these final features.

As mentioned, I’m the Head of R&D at AnyChart and would be glad to hear any feedback, questions, or thoughts for improvement you may have in the comments.