This is a continuation of Developing an Automated Trading System.
Many of us use very robust charting software, including the popular thinkorswim platform, that does more than I plan to create in my system. The requirement I ran into that could not be met by this software is the ability to chart unique data produced by my system that isn’t available to the third-party platforms, such as back testing results.
Thus, I needed basic charting that allowed me to analyze things in the context of price history. While a fully automated system won’t depend on charts, of course. I — the human — play a role both in its development and improvement, as well as a cohesive role in automation. To balance the human brain vs AI discussion, the goal is a “cyborg” in the beginning that becomes more and more machine as time passes. Parts that are proven to be successful in production will remain in the cyborg while new parts are vigorously tested.
I had a few requirements when comparing charting libraries:
- Extensible free open-source.
- Works with Angular2, our choice for UI.
- Can do price history charting well (stock charts).
- Can easily add lines (studies and other calculations).
- Can update in real-time.
Other bells and whistles were considered, but those were the core requirements. I chose ng2-nvd3 as it met these requirements and had nice bells and whistles such as zooming and resizing capability, and can be user interactive. This is a 3-tier stack:
NVD3 – re-usable charts for d3.js.
ng2-nvd3 – Angular2 component for nvd3.
The center of the stack is NVD3, as ng2-nvd3 just provides an Angular2 interface to it. Interfacing via ng2-nvd3 worked well. You have complete access to NVD3 capability. It also updates the chart when you update the data, as you expect from an Angular2 component. So, this completely met the Angular2 requirement.
NVD3 is a bit limited, though. They have a gallery of charts you can view. It can produce a nice candlestick or OHLC chart with high, low, open and close bars. But, you cannot add lines to these, and the multiChart option does not currently support candlestick or OHLC chart types. The multiChart type includes area, line and bar charting only. I can live with this limitation for now. I just have to chart close prices of the original price history as a line, and additional lines for things such as MAs.
Extensibility. In the long-run I’ll one day want a candlestick charts with lines for MAs and other indicators. I’ll also want lines for fibs, and other types of indicators, such as buy and sell signals, which might be up and down arrows, and other types of notation related to back testing. There are two silver linings to the ng2-nvd3 stack.
On top of this, you can use d3 on your current charts. I’ve already used it for some non-graphical utilities. Your code has access to everything ng2-nvd3 and nvd3 has access to, including, of course, the DOM model generated by it. So, you can easily learn and use D3 yourself to enhance your charts, perhaps to add the buy/sell signals, without even changing the nvd3 code.
The UI consists of 3 Angular2 components. One child for the price history query parameters. Another child for adding studies. And the parent that bring those inputs together and outputs the chart.
This uses both the Angular2 @Input and @Output decorators that allow you to tie components together. Because the chart automatically updates when the data changes due to data binding, including chart configuration, you can continue to add to and modify a chart after creating it using the controls.
Angular2 Charting components
Because each child component requires the user to potentially update multiple fields before the chart can be updated correctly, each one has at least one button (Chart and Add). When a button is pressed, the parent component receives the output and updates the chart. Note that the StudyEntryComponent is in early stages of a WIP. Yet, it can currently be used to add MAs to a chart.
Charting Input Components
As you make modifications, clicking the Chart or Add buttons updates the chart. You can also edit current MAs by selecting it, changing it and then clicking Chart. The next image shows the table that is created as you add or edit MAs along with the resulting chart.
Charting Output – Comparison with MAs
This chart demonstrates several features using nothing but out-of-the-box nvd3.
If you resize the browser window, the chart automatically resizes. While you can’t view the effect in the static image above, trust me, it works. Have doubts? Check out the demos I linked to earlier.
You can compare items using two different Y axis. In this case, the Russell 2000 ($RUT.X) is on the right axis. This currently creates studies for the underlying asset on the chart. So, when we add an MA, it appears for both the S&P 500 ($SPX.X) and the Russell. Being a two dimensional chart, you cannot have more then two Y axis. If you included a third or more, they will share the right axis, which will be extended to handle the full range of possible values. The choice of which axis an item belongs it is something you can control as you setup the data. But, you cannot have a third Y axis. So, you have to factor this into the design and how raw data is handled, with the impact on the Y range being your primary concern. Combining an item that ranges from 0 to 2 with an item that ranges from 2000 to 2200 on one Y axis will result in two flat looking lines far apart.
The user can interactively hide/show any of the lines by clicking the legend. You can see above that $RUT.X 200 EMA-we and $RUT.X 50 SMA-mo are both hidden because their circles in the legend are not filled in.
Another feature that differs from some charting software is that interval of the MAs is not limited to the interval of the chart. While the chart is displaying weekly bars here, we added monthly MAs to the chart. This is important because the algos will typically use one minute bars for historical data, and one or more per second real-time quote updates; yet, needs to be able to calculate MAs with intervals from 5 minutes to monthly.
Round Trip Data Flow
Currently, when it needs to update the chart, it simply does a REST call for price history, which has the ability to add studies via parameters. When those results come back, our UI side transforms the data using Typescript into the representation required to chart it, and simply replaces the data field in the ChartNVD3PriceComponent given to nvd3 to create the chart. Due to data binding, the chart updates the instant this data is updated.
The REST call itself uses the parameters to construct and invoke a third-party API call. Our facade to the API converts the raw data returned to POJOs. Because our interface to the API uses caching, this could be in memory and returned instantly. With price history in POJOs, our service then adds studies to the data as new fields. Then, it converts the POJOs to JSON and returns it as the output of the REST.
Our Angular2 component receives this data, transforms into charting representation, and updates the chart data.
Adding charting to the application gets us started so we can begin to create JSON of back testing results that can be used to produce charts. To add back testing results to charts, in Angular2, we’ll be creating a new UI component for defining back testing requirements, much like the one we created to add studies.
The exception to simply using a one trip REST query might be if the back testing takes longer than it does today due to new complexity and permutations. In that case, I’m likely to redesign it to simply add it to a back testing request queue; and allow the user to monitor the queue and view when available. One advantage of this is that it can be viewed at any time later so long as it is on the list of queries that were previously queued.
WebSockets can be used to update the queue in the browser without the user having to click. You will be able to see, in real-time, the progress of your request.
WebSockets can be used to update the chart in real-time. This will be important when using real-time quotes and monitoring trading. With the exception of the data coming through WebSockets instead of REST, we won’t need to really change how charting works in Angular2, as it currently updates the chart whenever the data changes. The only difference will be how the data changes. Since we already use Angular2 for real-time updates of Level I and II quotes, monitoring of predictions, and order flow, using WebSockets to update a chart does not introduces a new technical feat.