In the previous tutorial, we looked at replicating SaxoTraderGO charts using Excel's built in stock chart tool. One important difference still exists however: the chart is static and needs to be updated manually. Using a similar implementation as in Tutorial 5, we will now attempt to continuously refresh the chart as new data comes in. This sound easier than it is, as Excel will automatically recalculate all of the values that are computed on the Data sheet when we refresh the source data.
This chart features Bollinger Bands, which are fairly simple to recalculate. However, consider a situation where historic chart data is fed into many individual indicators, each of which require numerous components to be calculated separately. Assuming the chart pulls out a maximum of 1200 datapoints, a total of roughly 6000 cells are updated each time only to refresh the underlying data. Depending on the amount of calculations linked to this data, a simple refresh can quickly cascade into a series of updates across many more cells (easily over 20000). This puts serious pressure on Excel, especially considering we would ideally like to update our chart every 3 seconds. In addition, the vast majority of the recalculations is not required, as the historic data does not change (and therefore, any indicators linked to this do not change either). In essence, we are only interested in updating the most recently available value and compute the changes in the indicator values accordingly.
Prechecks
To overcome this issue, we implement a precheck, which pulls out only the latest data from the /charts/ endpoint and compares it against the data that is in the graph. If the timestamp of the horizon on the latest response from the OpenAPI matches the timestamp of the latest data in the chart, we can use some simple VBA logic to only update the latest datapoints. This reduces the amount of recalculations substantially, as Excel will only recompute dependent cells that are linked to the these datapoints.
If the timestamp is different, a full refresh is triggered, which pulls down new data from the OpenAPI for the entire time window. This will only happen once per minute at most (when the horizon is set to 1 minute).
VBA implementation
To achieve the above result, the following bits of VBA are linked up to the buttons labeled Start Chart and Stop Chart:
Sub StartChart()
'perform checks
If chartupdate = True Then
Exit Sub
End If
'start automatic updates
chartupdate = True
Call AutoUpdateChart
End Sub
Sub StopChart()
chartupdate = False
End Sub
When the AutoUpdateChart subroutine is called, the following steps are executed:
- Check if routine is still allowed to run.
- Call the precheck subroutine to only pull out the latest datapoints from the OpenAPI.
- If the precheck finds a difference in the timestamps, trigger a full refresh of the chart data.
- Loop this function every 3 seconds.
Sub AutoUpdateChart()
'only keep updating if chartupdate flag is still true
If chartupdate = True Then
'initialize fullupdate flag as False
fullupdate = False
fields = "Data[].Time,Data[].OpenBid,Data[].HighBid,Data[].LowBid,Data[].CloseBid"
'run precheck
'this function will set the fullupdate flag to true if a full refresh is required
Call RunPreCheck([Uic], [AssetType], [Horizon], Now, [Max])
'if full refresh requried, call function
If fullupdate = True Then
Call RefreshAllData([Uic], [AssetType], [Horizon], Now, [Max])
End If
[Uic].Select
'loop this function
IntervalTime = TimeValue("00:00:03")
Application.OnTime Now() + IntervalTime, "ThisWorkbook.AutoUpdateChart"
End If
End Sub
The precheck subroutine performs the usual API calls and error checking that we have implemented in earlier charting tutorials. It also compares the value of the returned timestamp (in cell Data!L2) against the current one (in cell Data!A61). If the value is the same, the last values are updated using the response from the API that is already available on the Data sheet (see image). This vastly reduces the pressure on both the OpenAPI, as we only request a single datapoint unless the current horizon expires, and Excel, because it will recompute the values in row 61 only.
Private Sub RunPreCheck(Uic As Integer, AssetType As String, _
Horizon As Integer, t As String, Count As Integer)
'this function checks the timestamp on the latest data from the API
'if the timestamp is the same ONLY the last line is updated
Dim checkcells As Range
If [Symbol] = "Not Found" Then GoTo inputerror
prequery = "/openapi/chart/v1/charts?AssetType=" & AssetType & "&Uic=" _
& Uic & "&Horizon=" & Horizon & "&Time=" & Format(t, "yyyy/mm/dd hh:mm") _
& "&Mode=UpTo" & "&Count=" & 1
checkdata = Application.Run("OpenApiGet", prequery, fields)
If TypeName(checkdata) <> "Variant()" Then GoTo unexperror
Set checkcells = Range("Data!L2:P2")
checkcells.ClearContents
checkcells = checkdata
'check timestamp
If Range("Data!L2").Value = Range("Data!A61").Value Then
Range("Data!A61:E61") = checkdata 'only update 1 row if timestamp is the same
Else
'set flag to True to trigger entire update of the sheet
fullupdate = True
End If
Exit Sub
inputerror: 'capture input error
MsgBox "Error: Could not complete request." & vbNewLine & _
"It looks like that combination of UIC / AssetType does not exist."
Exit Sub
unexperror: 'capture unexpected error
MsgBox "An unexpected error occurred." & vbNewLine & _
"This could be caused by data access rights." & vbNewLine & _
"Returned error: " & data
Exit Sub
End Sub
And finally, a full refresh is triggered only when required by the precheck, which sets the fullupdate flag to True. This will recompute all values, which is necessary because all time blocks are 'shifted' as the latest one is added on the top.
Private Sub RefreshAllData(Uic As Integer, AssetType As String, _
Horizon As Integer, t As String, Count As Integer)
Dim datacells As Range
Dim chartdata As Range
Dim dmax As Integer
query = "/openapi/chart/v1/charts?AssetType=" & AssetType & "&Uic=" _
& Uic & "&Horizon=" & Horizon & "&Time=" & Format(t, "yyyy/mm/dd hh:mm") _
& "&Mode=UpTo" & "&Count=" & Count
data = Application.Run("OpenApiGet", query, fields)
Range("Data!A2:E1201").ClearContents
dmax = UBound(data)
Set datacells = Range("Data!A2:E" & 1 + dmax)
datacells = data
Set chartdata = Range("Data!A2:I" & 1 + dmax)
ActiveSheet.ChartObjects("CandleChart").Activate
ActiveChart.SetSourceData Source:=chartdata
Exit Sub
End Sub

