This article is a brief description of the
implementation of a price relative indicator. The price relative indicator compares the
price of one security to another, and is often used to compare the
performance of a particular stock to a market index such as the S&P 500. The indicator is drawn on a separate set of axes from the
main chart, and the calculation is
Price[Ticker]/Price[S&P 500].
As with any typical chart extension, we will define a
handler class to create the chart objects and to recalculate the indicator as
necessary. In particular, an indicator class only requires two functions:
Create to create the objects that make up the chart and hook
them up to the chart manager, and Recalc to actually calculate the
indicator. However there are a couple tricky parts to this particular
indicator:
- Because this indicator is relative to the price of a second ticker symbol, we first need
to make sure the second ticker actually exists in our portfolio, because
Personal Stock Streamer will only retrieve historical data for a ticker that is part of
a portfolio. So if the ticker is not present in the current portfolio
we need to create an invisible ticker and add it to the portfolio.
Also, if an invisible ticker is created we need to make sure to delete it when
the chart is closed.
- We need to make sure that the secondary ticker has
enough historical data to do our calculations, or we need to request that it
be retrieved from the data source. One thing to note here is that
normally we would have to set up an OnHistoryUpdated event handler so that our
object is notified when the historical data arrives, but for chart indicators
we do not need to do that because the chart window already catches that event
and automatically calls our Recalc function.
I'm not going to go through the chart setup because it is relatively straightforward; the setup
code is well-commented, so you can read it to see
how the various components of the chart are created and hooked up.
As for the tricky parts, here is the first part of the
Recalc function:
Set AltTicker = Doc.FindTicker(Nothing,
ChartObject.GetParam(0), 0) ' check if the ticker exists in
the document If AltTicker
Is Nothing Then '
if ticker was not found, create
temporary ticker Set AltTicker = Doc.CreateTicker Set Portfolio = Doc.CurrentPortfolio If Not AltTicker Is Nothing Then
AltTicker.SetProp "Symbol", ChartObject.GetParam(0)
AltTicker.SetProp "Visible", "0"
Portfolio.Insert -1, AltTicker
' set the label on this chart
MyDataSet.Label = MyDataSet.ID + " (" + ChartObject.GetParam(0) + ") "
' make sure this ticker gets deleted when the chart is closed
ChartObject.DeferDeleteTicker(AltTicker)
End If
End If
What we're doing in the code above is finding the
relative ticker (such as the S&P 500) in the document, which is passed
as the first parameter in the chart object. (Programmers start counting at 0, so
the first parameter is actually at the 0th location.) This part of the code
handles the case where the ticker does not exist in the docuemnt, so it creates
the ticker object, sets the Symbol and Visible properties, and inserts it into the current
portfolio. Because the ticker is invisible it doesn't really matter where
you insert it as long as you clean up after yourself, which is what the call to
DeferDeleteTicker() does.
The next part of the code should be pretty
straightforward. It gets the data set from the relative ticker or requests it
from the application if necessary:
If Not AltTicker Is Nothing Then
Set AltCloseDataSet = ChartObject.GetDataSetFromTicker(AltTicker, "Close")
...
' check for a valid data set
If AltCloseDataSet Is Nothing Then
' if there was no data set, set a dummy value so it will at least display the chart
MyDataSet.Data(MyDataSet.Size - 1) = 0
MyDataSet.Label = MyDataSet.ID + " (" + ChartObject.GetParam(0) + ") "
' request historical data from the application
ChartObject.RequestHistoricalDataForTicker(AltTicker)
Once we have the data for the relative ticker, the next
part of the code actually calculates the relative indicator:
Else
' loop through all of the data and calculate the Price Relative indicator
For x = 0 To CloseDataSet.Size - 1
If (CloseDataSet.IsValidData(x) And CloseDataSet.Data(x) > 0) Then
LastValidCloseData = x
If (AltCloseDataSet.IsValidData(x) And AltCloseDataSet.Data(x) > 0) Then
LastValidAltCloseData = x
MyDataSet.Data(x) = CloseDataSet.Data(x) / AltCloseDataSet.Data(x)
End If
End If
Next
...
' check data at the end of the range
If LastValidAltCloseData < LastValidCloseData - 2 Then
ChartObject.RequestHistoricalDataForTicker(AltTicker)
End If
End If
End If
Note how we're keeping track of the last valid data for
the relative ticker in the LastValidAltCloseData variable. What this allows us
to do is to check if perhaps there is some data at the end of our range that is
missing for the relative ticker and tries to request it. This handles the case
where data for that ticker may not be updated for whatever reason.
Other than registering our indicator with the system,
that's pretty much it. As we do more of these types of extensions, you
will notice that there really isn't that much work involved, and a lot can
be accomplished with just a few dozen lines of code.
The full code for this extension is here, and the complete packaged extension is available for download
through the Extension manager under the Tools menu.