Imports Microsoft.VisualBasic
Imports ChartDirector
Imports System
Namespace RTT_ChartFinance
'''////////////////////////////////////////////////////////////////////////////////////////////////
' Copyright 2008 Advanced Software Engineering Limited
'
' ChartDirector FinanceChart class library
' - Requires ChartDirector Ver 5.0 or above
'
' You may use and modify the code in this file in your application, provided the code and
' its modifications are used only in conjunction with ChartDirector. Usage of this software
' is subjected to the terms and condition of the ChartDirector license.
'''////////////////////////////////////////////////////////////////////////////////////////////////
'''
''' Represents a Financial Chart
'''
Public Class cFinanceChart
Inherits MultiChart
Private m_totalWidth As Integer = 0
Private m_totalHeight As Integer = 0
Private m_antiAlias As Boolean = True
Private m_logScale As Boolean = False
Private m_axisOnRight As Boolean = True
Private m_leftMargin As Integer = 40
Private m_rightMargin As Integer = 40
Private m_topMargin As Integer = 30
Private m_bottomMargin As Integer = 30
Private m_plotAreaBgColor As Integer = &HFFFFFF
Private m_plotAreaBorder As Integer = &H888888
Private m_plotAreaGap As Integer = 2
Private m_majorHGridColor As Integer = &HDDDDDD
Private m_minorHGridColor As Integer = &HDDDDDD
Private m_majorVGridColor As Integer = &HDDDDDD
Private m_minorVGridColor As Integer = &HDDDDDD
Private m_legendFont As String = "normal"
Private m_legendFontSize As Double = 8
Private m_legendFontColor As Integer = Chart.TextColor
Private m_legendBgColor As Integer = CInt(&H80CCCCCC)
Private m_yAxisFont As String = "normal"
Private m_yAxisFontSize As Double = 8
Private m_yAxisFontColor As Integer = Chart.TextColor
Private m_yAxisMargin As Integer = 14
Private m_xAxisFont As String = "normal"
Private m_xAxisFontSize As Double = 8
Private m_xAxisFontColor As Integer = Chart.TextColor
Private m_xAxisFontAngle As Double = 0
Private m_timeStamps As Double() = Nothing
Private m_highData As Double() = Nothing
Private m_lowData As Double() = Nothing
Private m_openData As Double() = Nothing
Private m_closeData As Double() = Nothing
Private m_volData As Double() = Nothing
Private m_volUnit As String = ""
Private m_extraPoints As Integer = 0
Private m_yearFormat As String = "{value|yyyy}"
Private m_firstMonthFormat As String = "<*font=bold*>{value|mmm yy}"
Private m_otherMonthFormat As String = "{value|mmm}"
Private m_firstDayFormat As String = "<*font=bold*>{value|d mmm}"
Private m_otherDayFormat As String = "{value|d}"
Private m_firstHourFormat As String = "<*font=bold*>{value|d mmm" & vbLf & "h:nna}"
Private m_otherHourFormat As String = "{value|h:nna}"
Private m_timeLabelSpacing As Integer = 50
Private m_generalFormat As String = "P3"
Private m_toolTipMonthFormat As String = "[{xLabel|mmm yyyy}]"
Private m_toolTipDayFormat As String = "[{xLabel|mmm d, yyyy}]"
Private m_toolTipHourFormat As String = "[{xLabel|mmm d, yyyy hh:nn:ss}]"
Private m_mainChart As XYChart = Nothing
Private m_currentChart As XYChart = Nothing
'''
''' Create a FinanceChart with a given width. The height will be automatically determined
''' as the chart is built.
'''
''' Width of the chart in pixels
Public Sub New(ByVal width As Integer)
MyBase.New(width, 1)
m_totalWidth = width
End Sub
'''
''' Enable/Disable anti-alias. Enabling anti-alias makes the line smoother. Disabling
''' anti-alias make the chart file size smaller, and so can be downloaded faster
''' through the Internet. The default is to enable anti-alias.
'''
''' True to enable anti-alias. False to disable anti-alias.
Public Sub enableAntiAlias(ByVal antiAlias As Boolean)
m_antiAlias = antiAlias
End Sub
'''
''' Set the margins around the plot area.
'''
''' The distance between the plot area and the chart left edge.
''' The distance between the plot area and the chart top edge.
''' The distance between the plot area and the chart right edge.
''' The distance between the plot area and the chart bottom edge.
Public Sub setMargins(ByVal leftMargin As Integer, ByVal topMargin As Integer, ByVal rightMargin As Integer, ByVal bottomMargin As Integer)
m_leftMargin = leftMargin
m_rightMargin = rightMargin
m_topMargin = topMargin
m_bottomMargin = bottomMargin
End Sub
'''
''' Add a text title above the plot area. You may add multiple title above the plot area by
''' calling this method multiple times.
'''
''' The alignment with respect to the region that is on top of the
''' plot area.
''' The text to add.
''' The TextBox object representing the text box above the plot area.
Public Function addPlotAreaTitle(ByVal alignment As Integer, ByVal text As String) As ChartDirector.TextBox
Dim ret As ChartDirector.TextBox = addText(m_leftMargin, 0, text, "bold", 10, Chart.TextColor, _
alignment)
ret.setSize(m_totalWidth - m_leftMargin - m_rightMargin + 1, m_topMargin - 1)
ret.setMargin(0)
Return ret
End Function
'''
''' Set the plot area style. The default is to use pale yellow 0xfffff0 as the background,
''' and light grey 0xdddddd as the grid lines.
'''
''' The plot area background color.
''' Major horizontal grid color.
''' Major vertical grid color.
''' Minor horizontal grid color. In current version, minor
''' horizontal grid is not used.
''' Minor vertical grid color.
Public Sub setPlotAreaStyle(ByVal bgColor As Integer, ByVal majorHGridColor As Integer, ByVal majorVGridColor As Integer, ByVal minorHGridColor As Integer, ByVal minorVGridColor As Integer)
m_plotAreaBgColor = bgColor
m_majorHGridColor = majorHGridColor
m_majorVGridColor = majorVGridColor
m_minorHGridColor = minorHGridColor
m_minorVGridColor = minorVGridColor
End Sub
'''
''' Set the plot area border style. The default is grey color (888888), with a gap
''' of 2 pixels between charts.
'''
''' The color of the border.
''' The gap between two charts.
Public Sub setPlotAreaBorder(ByVal borderColor As Integer, ByVal borderGap As Integer)
m_plotAreaBorder = borderColor
m_plotAreaGap = borderGap
End Sub
'''
''' Set legend style. The default is Arial 8 pt black color, with light grey background.
'''
''' The font of the legend text.
''' The font size of the legend text in points.
''' The color of the legend text.
''' The background color of the legend box.
Public Sub setLegendStyle(ByVal font As String, ByVal fontSize As Double, ByVal fontColor As Integer, ByVal bgColor As Integer)
m_legendFont = font
m_legendFontSize = fontSize
m_legendFontColor = fontColor
m_legendBgColor = bgColor
End Sub
'''
''' Set x-axis label style. The default is Arial 8 pt black color no rotation.
'''
''' The font of the axis labels.
''' The font size of the axis labels in points.
''' The color of the axis labels.
''' The rotation of the axis labels.
Public Sub setXAxisStyle(ByVal font As String, ByVal fontSize As Double, ByVal fontColor As Integer, ByVal fontAngle As Double)
m_xAxisFont = font
m_xAxisFontSize = fontSize
m_xAxisFontColor = fontColor
m_xAxisFontAngle = fontAngle
End Sub
'''
''' Set y-axis label style. The default is Arial 8 pt black color, with 13 pixels margin.
'''
''' The font of the axis labels.
''' The font size of the axis labels in points.
''' The color of the axis labels.
''' The margin at the top of the y-axis in pixels (to leave
''' space for the legend box).
Public Sub setYAxisStyle(ByVal font As String, ByVal fontSize As Double, ByVal fontColor As Integer, ByVal axisMargin As Integer)
m_yAxisFont = font
m_yAxisFontSize = fontSize
m_yAxisFontColor = fontColor
m_yAxisMargin = axisMargin
End Sub
'''
''' Set whether the main y-axis is on right of left side of the plot area. The default is
''' on right.
'''
''' True if the y-axis is on right. False if the y-axis is on left.
Public Sub setAxisOnRight(ByVal b As Boolean)
m_axisOnRight = b
End Sub
'''
''' Determines if log scale should be used for the main chart. The default is linear scale.
'''
''' True for using log scale. False for using linear scale.
Public Sub setLogScale(ByVal b As Boolean)
m_logScale = b
If m_mainChart IsNot Nothing Then
If m_logScale Then
m_mainChart.yAxis().setLogScale()
Else
m_mainChart.yAxis().setLinearScale()
End If
End If
End Sub
'''
''' Set the date/time formats to use for the x-axis labels under various cases.
'''
''' The format for displaying labels on an axis with yearly ticks. The
''' default is "yyyy".
''' The format for displaying labels on an axis with monthly ticks.
''' This parameter applies to the first available month of a year (usually January) only, so it can
''' be formatted differently from the other labels.
''' The format for displaying labels on an axis with monthly ticks.
''' This parameter applies to months other than the first available month of a year.
''' The format for displaying labels on an axis with daily ticks.
''' This parameter applies to the first available day of a month only, so it can be formatted
''' differently from the other labels.
''' The format for displaying labels on an axis with daily ticks.
''' This parameter applies to days other than the first available day of a month.
''' The format for displaying labels on an axis with hourly
''' resolution. This parameter applies to the first tick of a day only, so it can be formatted
''' differently from the other labels.
''' The format for displaying labels on an axis with hourly.
''' resolution. This parameter applies to ticks at hourly boundaries, except the first tick
''' of a day.
Public Sub setDateLabelFormat(ByVal yearFormat As String, ByVal firstMonthFormat As String, ByVal otherMonthFormat As String, ByVal firstDayFormat As String, ByVal otherDayFormat As String, ByVal firstHourFormat As String, _
ByVal otherHourFormat As String)
If yearFormat IsNot Nothing Then
m_yearFormat = yearFormat
End If
If firstMonthFormat IsNot Nothing Then
m_firstMonthFormat = firstMonthFormat
End If
If otherMonthFormat IsNot Nothing Then
m_otherMonthFormat = otherMonthFormat
End If
If firstDayFormat IsNot Nothing Then
m_firstDayFormat = firstDayFormat
End If
If otherDayFormat IsNot Nothing Then
m_otherDayFormat = otherDayFormat
End If
If firstHourFormat IsNot Nothing Then
m_firstHourFormat = firstHourFormat
End If
If otherHourFormat IsNot Nothing Then
m_otherHourFormat = otherHourFormat
End If
End Sub
'''
''' This method is for backward compatibility - use setDataLabelFormat instead.
'''
Public Sub setTimeLabelFormats(ByVal yearFormat As String, ByVal firstMonthFormat As String, ByVal otherMonthFormat As String, ByVal firstDayFormat As String, ByVal otherDayFormat As String, ByVal firstHourFormat As String, _
ByVal otherHourFormat As String)
setDateLabelFormat(yearFormat, firstMonthFormat, otherMonthFormat, firstDayFormat, otherDayFormat, firstHourFormat, _
otherHourFormat)
End Sub
'''
''' Set the minimum label spacing between two labels on the time axis
'''
''' The minimum label spacing in pixels.
Public Sub setDateLabelSpacing(ByVal labelSpacing As Integer)
If labelSpacing > 0 Then
m_timeLabelSpacing = labelSpacing
Else
m_timeLabelSpacing = 0
End If
End Sub
'''
''' This function is for backward compatibility. It has no purpose.
'''
Public Sub enableToolTips(ByVal b As Boolean, ByVal dateTimeFormat As String)
'do nothing
End Sub
'''
''' Set the tool tip formats for display date/time
'''
''' The tool tip format to use if the data point spacing is one
''' or more months (more than 30 days).
''' The tool tip format to use if the data point spacing is 1 day
''' to less than 30 days.
''' The tool tip format to use if the data point spacing is less
''' than 1 day.
Public Sub setToolTipDateFormat(ByVal monthFormat As String, ByVal dayFormat As String, ByVal hourFormat As String)
If monthFormat IsNot Nothing Then
m_toolTipMonthFormat = monthFormat
End If
If dayFormat IsNot Nothing Then
m_toolTipDayFormat = dayFormat
End If
If hourFormat IsNot Nothing Then
m_toolTipHourFormat = hourFormat
End If
End Sub
'''
''' Get the tool tip format for display date/time
'''
''' The tool tip format string.
Public Function getToolTipDateFormat() As String
If m_timeStamps Is Nothing Then
Return m_toolTipHourFormat
End If
If m_timeStamps.Length <= m_extraPoints Then
Return m_toolTipHourFormat
End If
Dim resolution As Double = (m_timeStamps(m_timeStamps.Length - 1) - m_timeStamps(0)) / (m_timeStamps.Length)
If resolution >= 30 * 86400 Then
Return m_toolTipMonthFormat
ElseIf resolution >= 86400 Then
Return m_toolTipDayFormat
Else
Return m_toolTipHourFormat
End If
End Function
'''
''' Set the number format for use in displaying values in legend keys and tool tips.
'''
''' The default number format.
Public Sub setNumberLabelFormat(ByVal formatString As String)
If formatString IsNot Nothing Then
m_generalFormat = formatString
End If
End Sub
'''
''' A utility function to compute triangular moving averages
'''
''' An array of numbers as input.
''' The moving average period.
''' An array representing the triangular moving average of the input array.
Private Function computeTriMovingAvg(ByVal data As Double(), ByVal period As Integer) As Double()
Dim p As Integer = period / 2 + 1
Return New ArrayMath(data).movAvg(p).movAvg(p).result()
End Function
'''
''' A utility function to compute weighted moving averages
'''
''' An array of numbers as input.
''' The moving average period.
''' An array representing the weighted moving average of the input array.
Private Function computeWeightedMovingAvg(ByVal data As Double(), ByVal period As Integer) As Double()
Dim acc As New ArrayMath(data)
For i As Integer = 2 To period
acc.add(New ArrayMath(data).movAvg(i).mul(i).result())
Next
Return acc.div((1 + period) * period / 2).result()
End Function
'''
''' A utility function to obtain the first visible closing price.
'''
''' The first closing price.
''' are cd.NoValue.
Private Function firstCloseValue() As Double
For i As Integer = m_extraPoints To m_closeData.Length - 1
If (m_closeData(i) <> Chart.NoValue) AndAlso (m_closeData(i) <> 0) Then
Return m_closeData(i)
End If
Next
Return Chart.NoValue
End Function
'''
''' A utility function to obtain the last valid position (that is, position not
''' containing cd.NoValue) of a data series.
'''
''' An array of numbers as input.
''' The last valid position in the input array, or -1 if all positions
''' are cd.NoValue.
Private Function lastIndex(ByVal data As Double()) As Integer
Dim i As Integer = data.Length - 1
While i >= 0
If data(i) <> Chart.NoValue Then
Exit While
End If
i = i - 1
End While
Return i
End Function
'''
''' Set the data used in the chart. If some of the data are not available, some artifical
''' values should be used. For example, if the high and low values are not available, you
''' may use closeData as highData and lowData.
'''
''' An array of dates/times for the time intervals.
''' The high values in the time intervals.
''' The low values in the time intervals.
''' The open values in the time intervals.
''' The close values in the time intervals.
''' The volume values in the time intervals.
''' The number of leading time intervals that are not
''' displayed in the chart. These intervals are typically used for computing
''' indicators that require extra leading data, such as moving averages.
Public Sub setData(ByVal timeStamps As DateTime(), ByVal highData As Double(), ByVal lowData As Double(), ByVal openData As Double(), ByVal closeData As Double(), ByVal volData As Double(), _
ByVal extraPoints As Integer)
setData(Chart.CTime(timeStamps), highData, lowData, openData, closeData, volData, _
extraPoints)
End Sub
'''
''' Set the data used in the chart. If some of the data are not available, some artifical
''' values should be used. For example, if the high and low values are not available, you
''' may use closeData as highData and lowData.
'''
''' An array of dates/times for the time intervals.
''' The high values in the time intervals.
''' The low values in the time intervals.
''' The open values in the time intervals.
''' The close values in the time intervals.
''' The volume values in the time intervals.
''' The number of leading time intervals that are not
''' displayed in the chart. These intervals are typically used for computing
''' indicators that require extra leading data, such as moving averages.
Public Sub setData(ByVal timeStamps As Double(), ByVal highData As Double(), ByVal lowData As Double(), ByVal openData As Double(), ByVal closeData As Double(), ByVal volData As Double(), _
ByVal extraPoints As Integer)
m_timeStamps = timeStamps
m_highData = highData
m_lowData = lowData
m_openData = openData
m_closeData = closeData
If extraPoints > 0 Then
m_extraPoints = extraPoints
Else
m_extraPoints = 0
End If
'''//////////////////////////////////////////////////////////////////////
' Auto-detect volume units
'''//////////////////////////////////////////////////////////////////////
Dim maxVol As Double = New ArrayMath(volData).max()
Dim units As String() = {"", "K", "M", "B"}
Dim unitIndex As Integer = units.Length - 1
While (unitIndex > 0) AndAlso (maxVol < Math.Pow(1000, unitIndex))
unitIndex = unitIndex - 1
End While
m_volData = New ArrayMath(volData).div(Math.Pow(1000, unitIndex)).result()
m_volUnit = units(unitIndex)
End Sub
'''///////////////////////////////////////////////////////////////////////////
' Format x-axis labels
'''///////////////////////////////////////////////////////////////////////////
Private Sub setXLabels(ByVal a As Axis)
a.setLabels2(m_timeStamps)
If m_extraPoints < m_timeStamps.Length Then
Dim tickStep As Integer = CInt(((m_timeStamps.Length - m_extraPoints) * m_timeLabelSpacing / (m_totalWidth - m_leftMargin - m_rightMargin))) + 1
Dim timeRangeInSeconds As Double = m_timeStamps(m_timeStamps.Length - 1) - m_timeStamps(m_extraPoints)
Dim secondsBetweenTicks As Double = timeRangeInSeconds / (m_totalWidth - m_leftMargin - m_rightMargin) * m_timeLabelSpacing
If secondsBetweenTicks * (m_timeStamps.Length - m_extraPoints) <= timeRangeInSeconds Then
tickStep = 1
If m_timeStamps.Length > 1 Then
secondsBetweenTicks = m_timeStamps(m_timeStamps.Length - 1) - m_timeStamps(m_timeStamps.Length - 2)
Else
secondsBetweenTicks = 86400
End If
End If
If (secondsBetweenTicks > 360 * 86400) OrElse ((secondsBetweenTicks > 90 * 86400) AndAlso (timeRangeInSeconds >= 720 * 86400)) Then
'yearly ticks
a.setMultiFormat2(Chart.StartOfYearFilter(), m_yearFormat, tickStep)
ElseIf (secondsBetweenTicks >= 30 * 86400) OrElse ((secondsBetweenTicks > 7 * 86400) AndAlso (timeRangeInSeconds >= 60 * 86400)) Then
'monthly ticks
Dim monthBetweenTicks As Integer = CInt((secondsBetweenTicks / 31 / 86400)) + 1
a.setMultiFormat(Chart.StartOfYearFilter(), m_firstMonthFormat, Chart.StartOfMonthFilter(monthBetweenTicks), m_otherMonthFormat)
a.setMultiFormat2(Chart.StartOfMonthFilter(), "-", 1, False)
ElseIf (secondsBetweenTicks >= 86400) OrElse ((secondsBetweenTicks > 6 * 3600) AndAlso (timeRangeInSeconds >= 86400)) Then
'daily ticks
a.setMultiFormat(Chart.StartOfMonthFilter(), m_firstDayFormat, Chart.StartOfDayFilter(1, 0.5), m_otherDayFormat, tickStep)
Else
'hourly ticks
a.setMultiFormat(Chart.StartOfDayFilter(1, 0.5), m_firstHourFormat, Chart.StartOfHourFilter(1, 0.5), m_otherHourFormat, tickStep)
End If
End If
End Sub
'''///////////////////////////////////////////////////////////////////////////
' Create tool tip format string for showing OHLC data
'''///////////////////////////////////////////////////////////////////////////
Private Function getHLOCToolTipFormat() As String
Return (((("title='" & getToolTipDateFormat() & " Op:{open|") + m_generalFormat & "}, Hi:{high|") + m_generalFormat & "}, Lo:{low|") + m_generalFormat & "}, Cl:{close|") + m_generalFormat & "}'"
End Function
'''
''' Add the main chart - the chart that shows the HLOC data.
'''
''' The height of the main chart in pixels.
''' An XYChart object representing the main chart created.
Public Function addMainChart(ByVal height As Integer) As XYChart
m_mainChart = addIndicator(height)
setMainChart(m_mainChart)
m_mainChart.yAxis().setMargin(2 * m_yAxisMargin)
If m_logScale Then
m_mainChart.yAxis().setLogScale()
Else
m_mainChart.yAxis().setLinearScale()
End If
Return m_mainChart
End Function
'''
''' Add a candlestick layer to the main chart.
'''
''' The candle color for an up day.
''' The candle color for a down day.
''' The CandleStickLayer created.
Public Function addCandleStick(ByVal upColor As Integer, ByVal downColor As Integer) As CandleStickLayer
addOHLCLabel(upColor, downColor, True)
Dim ret As CandleStickLayer = m_mainChart.addCandleStickLayer(m_highData, m_lowData, m_openData, m_closeData, upColor, downColor)
ret.setHTMLImageMap("", "", getHLOCToolTipFormat())
If m_highData.Length - m_extraPoints > 60 Then
ret.setDataGap(0)
End If
If m_highData.Length > m_extraPoints Then
Dim expectedWidth As Integer = (m_totalWidth - m_leftMargin - m_rightMargin) / (m_highData.Length - m_extraPoints)
If expectedWidth <= 5 Then
ret.setDataWidth(expectedWidth + 1 - expectedWidth Mod 2)
End If
End If
Return ret
End Function
'''
''' Add a HLOC layer to the main chart.
'''
''' The color of the HLOC symbol.
''' The HLOCLayer created.
Public Function addHLOC(ByVal color As Integer) As HLOCLayer
Return addHLOC(color, color)
End Function
'''
''' Add a HLOC layer to the main chart.
'''
''' The color of the HLOC symbol for an up day.
''' The color of the HLOC symbol for a down day.
''' The HLOCLayer created.
Public Function addHLOC(ByVal upColor As Integer, ByVal downColor As Integer) As HLOCLayer
addOHLCLabel(upColor, downColor, False)
Dim ret As HLOCLayer = m_mainChart.addHLOCLayer(m_highData, m_lowData, m_openData, m_closeData)
ret.setColorMethod(Chart.HLOCUpDown, upColor, downColor)
ret.setHTMLImageMap("", "", getHLOCToolTipFormat())
ret.setDataGap(0)
Return ret
End Function
Private Sub addOHLCLabel(ByVal upColor As Integer, ByVal downColor As Integer, ByVal candleStickMode As Boolean)
Dim i As Integer = lastIndex(m_closeData)
If i >= 0 Then
Dim openValue As Double = Chart.NoValue
Dim closeValue As Double = Chart.NoValue
Dim highValue As Double = Chart.NoValue
Dim lowValue As Double = Chart.NoValue
If i < m_openData.Length Then
openValue = m_openData(i)
End If
If i < m_closeData.Length Then
closeValue = m_closeData(i)
End If
If i < m_highData.Length Then
highValue = m_highData(i)
End If
If i < m_lowData.Length Then
lowValue = m_lowData(i)
End If
Dim openLabel As String = ""
Dim closeLabel As String = ""
Dim highLabel As String = ""
Dim lowLabel As String = ""
Dim delim As String = ""
If openValue <> Chart.NoValue Then
openLabel = "Op:" & formatValue(openValue, m_generalFormat)
delim = ", "
End If
If highValue <> Chart.NoValue Then
highLabel = (delim & "Hi:") + formatValue(highValue, m_generalFormat)
delim = ", "
End If
If lowValue <> Chart.NoValue Then
lowLabel = (delim & "Lo:") + formatValue(lowValue, m_generalFormat)
delim = ", "
End If
If closeValue <> Chart.NoValue Then
closeLabel = (delim & "Cl:") + formatValue(closeValue, m_generalFormat)
delim = ", "
End If
Dim label As String = openLabel + highLabel + lowLabel + closeLabel
Dim useUpColor As Boolean = (closeValue >= openValue)
If candleStickMode <> True Then
Dim closeChanges As Double() = New ArrayMath(m_closeData).delta().result()
Dim lastChangeIndex As Integer = lastIndex(closeChanges)
useUpColor = (lastChangeIndex < 0)
If useUpColor <> True Then
useUpColor = (closeChanges(lastChangeIndex) >= 0)
End If
End If
Dim udcolor As Integer = downColor
If useUpColor Then
udcolor = upColor
End If
m_mainChart.getLegend().addKey(label, udcolor)
End If
End Sub
'''
''' Add a closing price line on the main chart.
'''
''' The color of the line.
''' The LineLayer object representing the line created.
Public Function addCloseLine(ByVal color As Integer) As LineLayer
Return addLineIndicator2(m_mainChart, m_closeData, color, "Closing Price")
End Function
'''
''' Add a weight close line on the main chart.
'''
''' The color of the line.
''' The LineLayer object representing the line created.
Public Function addWeightedClose(ByVal color As Integer) As LineLayer
Return addLineIndicator2(m_mainChart, New ArrayMath(m_highData).add(m_lowData).add(m_closeData).add(m_closeData).div(4).result(), color, "Weighted Close")
End Function
'''
''' Add a typical price line on the main chart.
'''
''' The color of the line.
''' The LineLayer object representing the line created.
Public Function addTypicalPrice(ByVal color As Integer) As LineLayer
Return addLineIndicator2(m_mainChart, New ArrayMath(m_highData).add(m_lowData).add(m_closeData).div(3).result(), color, "Typical Price")
End Function
'''
''' Add a median price line on the main chart.
'''
''' The color of the line.
''' The LineLayer object representing the line created.
Public Function addMedianPrice(ByVal color As Integer) As LineLayer
Return addLineIndicator2(m_mainChart, New ArrayMath(m_highData).add(m_lowData).div(2).result(), color, "Median Price")
End Function
'''
''' Add a simple moving average line on the main chart.
'''
''' The moving average period
''' The color of the line.
''' The LineLayer object representing the line created.
Public Function addSimpleMovingAvg(ByVal period As Integer, ByVal color As Integer) As LineLayer
Dim label As String = "SMA (" & period & ")"
Return addLineIndicator2(m_mainChart, New ArrayMath(m_closeData).movAvg(period).result(), color, label)
End Function
'''
''' Add an exponential moving average line on the main chart.
'''
''' The moving average period
''' The color of the line.
''' The LineLayer object representing the line created.
Public Function addExpMovingAvg(ByVal period As Integer, ByVal color As Integer) As LineLayer
Dim label As String = "EMA (" & period & ")"
Return addLineIndicator2(m_mainChart, New ArrayMath(m_closeData).expAvg(2.0R / (period + 1)).result(), color, label)
End Function
'''
''' Add a triangular moving average line on the main chart.
'''
''' The moving average period
''' The color of the line.
''' The LineLayer object representing the line created.
Public Function addTriMovingAvg(ByVal period As Integer, ByVal color As Integer) As LineLayer
Dim label As String = "TMA (" & period & ")"
Return addLineIndicator2(m_mainChart, New ArrayMath(computeTriMovingAvg(m_closeData, period)).result(), color, label)
End Function
'''
''' Add a weighted moving average line on the main chart.
'''
''' The moving average period
''' The color of the line.
''' The LineLayer object representing the line created.
Public Function addWeightedMovingAvg(ByVal period As Integer, ByVal color As Integer) As LineLayer
Dim label As String = "WMA (" & period & ")"
Return addLineIndicator2(m_mainChart, New ArrayMath(computeWeightedMovingAvg(m_closeData, period)).result(), color, label)
End Function
'''
''' Add a parabolic SAR indicator to the main chart.
'''
''' Initial acceleration factor
''' Acceleration factor increment
''' Maximum acceleration factor
''' The symbol used to plot the parabolic SAR
''' The symbol size in pixels
''' The fill color of the symbol
''' The edge color of the symbol
''' The LineLayer object representing the layer created.
Public Function addParabolicSAR(ByVal accInitial As Double, ByVal accIncrement As Double, ByVal accMaximum As Double, ByVal symbolType As Integer, ByVal symbolSize As Integer, ByVal fillColor As Integer, _
ByVal edgeColor As Integer) As LineLayer
Dim isLong As Boolean = True
Dim acc As Double = accInitial
Dim extremePoint As Double = 0
Dim psar As Double() = New Double(m_lowData.Length - 1) {}
Dim i_1 As Integer = -1
Dim i_2 As Integer = -1
For i As Integer = 0 To m_lowData.Length - 1
psar(i) = Chart.NoValue
If (m_lowData(i) <> Chart.NoValue) AndAlso (m_highData(i) <> Chart.NoValue) Then
If (i_1 >= 0) AndAlso (i_2 < 0) Then
If m_lowData(i_1) <= m_lowData(i) Then
psar(i) = m_lowData(i_1)
isLong = True
If m_highData(i_1) > m_highData(i) Then
extremePoint = m_highData(i_1)
Else
extremePoint = m_highData(i)
End If
Else
extremePoint = m_lowData(i)
isLong = False
If m_highData(i_1) > m_highData(i) Then
psar(i) = m_highData(i_1)
Else
psar(i) = m_highData(i)
End If
End If
ElseIf (i_1 >= 0) AndAlso (i_2 >= 0) Then
If acc > accMaximum Then
acc = accMaximum
End If
psar(i) = psar(i_1) + acc * (extremePoint - psar(i_1))
If isLong Then
If m_lowData(i) < psar(i) Then
isLong = False
psar(i) = extremePoint
extremePoint = m_lowData(i)
acc = accInitial
Else
If m_highData(i) > extremePoint Then
extremePoint = m_highData(i)
acc = acc + accIncrement
End If
If m_lowData(i_1) < psar(i) Then
psar(i) = m_lowData(i_1)
End If
If m_lowData(i_2) < psar(i) Then
psar(i) = m_lowData(i_2)
End If
End If
Else
If m_highData(i) > psar(i) Then
isLong = True
psar(i) = extremePoint
extremePoint = m_highData(i)
acc = accInitial
Else
If m_lowData(i) < extremePoint Then
extremePoint = m_lowData(i)
acc = acc + accIncrement
End If
If m_highData(i_1) > psar(i) Then
psar(i) = m_highData(i_1)
End If
If m_highData(i_2) > psar(i) Then
psar(i) = m_highData(i_2)
End If
End If
End If
End If
i_2 = i_1
i_1 = i
End If
Next
Dim ret As LineLayer = addLineIndicator2(m_mainChart, psar, fillColor, "Parabolic SAR")
ret.setLineWidth(0)
ret.addDataSet(psar).setDataSymbol(symbolType, symbolSize, fillColor, edgeColor)
Return ret
End Function
'''
''' Add a comparison line to the main price chart.
'''
''' The data series to compare to
''' The color of the comparison line
''' The name of the comparison line
''' The LineLayer object representing the line layer created.
Public Function addComparison(ByVal data As Double(), ByVal color As Integer, ByVal name As String) As LineLayer
Dim firstIndex As Integer = m_extraPoints
While (firstIndex < data.Length) AndAlso (firstIndex < m_closeData.Length)
If (data(firstIndex) <> Chart.NoValue) AndAlso (m_closeData(firstIndex) <> Chart.NoValue) AndAlso (data(firstIndex) <> 0) AndAlso (m_closeData(firstIndex) <> 0) Then
Exit While
End If
firstIndex = firstIndex + 1
End While
If (firstIndex >= data.Length) OrElse (firstIndex >= m_closeData.Length) Then
Return Nothing
End If
Dim scaleFactor As Double = m_closeData(firstIndex) / data(firstIndex)
Dim layer As LineLayer = m_mainChart.addLineLayer(New ArrayMath(data).mul(scaleFactor).result(), Chart.Transparent)
layer.setHTMLImageMap("{disable}")
Dim a As Axis = m_mainChart.addAxis(Chart.Right, 0)
a.setColors(Chart.Transparent, Chart.Transparent)
a.syncAxis(m_mainChart.yAxis(), 1 / scaleFactor, 0)
Dim ret As LineLayer = addLineIndicator2(m_mainChart, data, color, name)
ret.setUseYAxis(a)
Return ret
End Function
'''
''' Display percentage axis scale
'''
''' The Axis object representing the percentage axis.
Public Function setPercentageAxis() As Axis
Dim firstClose As Double = firstCloseValue()
If firstClose = Chart.NoValue Then
Return Nothing
End If
Dim axisAlign As Integer = Chart.Left
If m_axisOnRight Then
axisAlign = Chart.Right
End If
Dim ret As Axis = m_mainChart.addAxis(axisAlign, 0)
configureYAxis(ret, 300)
ret.syncAxis(m_mainChart.yAxis(), 100 / firstClose)
ret.setRounding(False, False)
ret.setLabelFormat("{={value}-100|@}%")
m_mainChart.yAxis().setColors(Chart.Transparent, Chart.Transparent)
m_mainChart.getPlotArea().setGridAxis(Nothing, ret)
Return ret
End Function
'''
''' Add a generic band to the main finance chart. This method is used internally by other methods to add
''' various bands (eg. Bollinger band, Donchian channels, etc).
'''
''' The data series for the upper band line.
''' The data series for the lower band line.
''' The color of the upper and lower band line.
''' The color to fill the region between the upper and lower band lines.
''' The name of the band.
''' An InterLineLayer object representing the filled region.
Public Function addBand(ByVal upperLine As Double(), ByVal lowerLine As Double(), ByVal lineColor As Integer, ByVal fillColor As Integer, ByVal name As String) As InterLineLayer
Dim i As Integer = upperLine.Length - 1
If i >= lowerLine.Length Then
i = lowerLine.Length - 1
End If
While i >= 0
If (upperLine(i) <> Chart.NoValue) AndAlso (lowerLine(i) <> Chart.NoValue) Then
name = ((name & ": ") + formatValue(lowerLine(i), m_generalFormat) & " - ") + formatValue(upperLine(i), m_generalFormat)
Exit While
End If
i = i - 1
End While
Dim uLayer As LineLayer = m_mainChart.addLineLayer(upperLine, lineColor, name)
Dim lLayer As LineLayer = m_mainChart.addLineLayer(lowerLine, lineColor)
Return m_mainChart.addInterLineLayer(uLayer.getLine(), lLayer.getLine(), fillColor)
End Function
'''
''' This method is for backward compatibility.
''' - use addBand(double[], double[], int, int, string) instead.
'''
Public Function addBand(ByVal upperLine As ArrayMath, ByVal lowerLine As ArrayMath, ByVal lineColor As Integer, ByVal fillColor As Integer, ByVal name As String) As InterLineLayer
Return addBand(upperLine.result(), lowerLine.result(), lineColor, fillColor, name)
End Function
'''
''' Add a Bollinger band on the main chart.
'''
''' The period to compute the band.
''' The half-width of the band in terms multiples of standard deviation. Typically 2 is used.
''' The color of the lines defining the upper and lower limits.
''' The color to fill the regional within the band.
''' The InterLineLayer object representing the band created.
Public Function addBollingerBand(ByVal period As Integer, ByVal bandWidth As Double, ByVal lineColor As Integer, ByVal fillColor As Integer) As InterLineLayer
'Bollinger Band is moving avg +/- (width * moving std deviation)
Dim stdDev As Double() = New ArrayMath(m_closeData).movStdDev(period).mul(bandWidth).result()
Dim movAvg As Double() = New ArrayMath(m_closeData).movAvg(period).result()
Dim label As String = "Bollinger (" & period & ", " & bandWidth & ")"
Return addBand(New ArrayMath(movAvg).add(stdDev).result(), New ArrayMath(movAvg).[sub](stdDev).selectGTZ(Nothing, 0).result(), lineColor, fillColor, label)
End Function
'''
''' Add a Donchian channel on the main chart.
'''
''' The period to compute the band.
''' The color of the lines defining the upper and lower limits.
''' The color to fill the regional within the band.
''' The InterLineLayer object representing the band created.
Public Function addDonchianChannel(ByVal period As Integer, ByVal lineColor As Integer, ByVal fillColor As Integer) As InterLineLayer
'Donchian Channel is the zone between the moving max and moving min
Dim label As String = "Donchian (" & period & ")"
Return addBand(New ArrayMath(m_highData).movMax(period).result(), New ArrayMath(m_lowData).movMin(period).result(), lineColor, fillColor, label)
End Function
'''
''' Add a price envelop on the main chart. The price envelop is a defined as a ratio around a
''' moving average. For example, a ratio of 0.2 means 20% above and below the moving average.
'''
''' The period for the moving average.
''' The ratio above and below the moving average.
''' The color of the lines defining the upper and lower limits.
''' The color to fill the regional within the band.
''' The InterLineLayer object representing the band created.
Public Function addEnvelop(ByVal period As Integer, ByVal range As Double, ByVal lineColor As Integer, ByVal fillColor As Integer) As InterLineLayer
'Envelop is moving avg +/- percentage
Dim movAvg As Double() = New ArrayMath(m_closeData).movAvg(period).result()
Dim label As String = "Envelop (SMA " & period & " +/- " & CInt((range * 100)) & "%)"
Return addBand(New ArrayMath(movAvg).mul(1 + range).result(), New ArrayMath(movAvg).mul(1 - range).result(), lineColor, fillColor, label)
End Function
'''
''' Add a volume bar chart layer on the main chart.
'''
''' The height of the bar chart layer in pixels.
''' The color to used on an 'up' day. An 'up' day is a day where
''' the closing price is higher than that of the previous day.
''' The color to used on a 'down' day. A 'down' day is a day
''' where the closing price is lower than that of the previous day.
''' The color to used on a 'flat' day. A 'flat' day is a day
''' where the closing price is the same as that of the previous day.
''' The XYChart object representing the chart created.
Public Function addVolBars(ByVal height As Integer, ByVal upColor As Integer, ByVal downColor As Integer, ByVal flatColor As Integer) As BarLayer
Return addVolBars2(m_mainChart, height, upColor, downColor, flatColor)
End Function
Private Function addVolBars2(ByVal c As XYChart, ByVal height As Integer, ByVal upColor As Integer, ByVal downColor As Integer, ByVal flatColor As Integer) As BarLayer
Dim barLayer As BarLayer = c.addBarLayer2(Chart.Overlay)
barLayer.setBorderColor(Chart.Transparent)
If c Is m_mainChart Then
configureYAxis(c.yAxis2(), height)
Dim topMargin As Integer = c.getDrawArea().getHeight() - m_topMargin - m_bottomMargin - height + m_yAxisMargin
If topMargin < 0 Then
topMargin = 0
End If
c.yAxis2().setTopMargin(topMargin)
barLayer.setUseYAxis2()
End If
Dim a As Axis = c.yAxis2()
If c IsNot m_mainChart Then
a = c.yAxis()
End If
If New ArrayMath(m_volData).max() < 10 Then
a.setLabelFormat("{value|1}" & m_volUnit)
Else
a.setLabelFormat("{value}" & m_volUnit)
End If
Dim closeChange As Double() = New ArrayMath(m_closeData).delta().result()
Dim i As Integer = lastIndex(m_volData)
Dim label As String = "Vol"
If i >= 0 Then
label = (label & ": ") + formatValue(m_volData(i), m_generalFormat) + m_volUnit
closeChange(0) = 0
End If
Dim upDS As DataSet = barLayer.addDataSet(New ArrayMath(m_volData).selectGTZ(closeChange).result(), upColor)
Dim dnDS As DataSet = barLayer.addDataSet(New ArrayMath(m_volData).selectLTZ(closeChange).result(), downColor)
Dim flatDS As DataSet = barLayer.addDataSet(New ArrayMath(m_volData).selectEQZ(closeChange).result(), flatColor)
If (i < 0) OrElse (closeChange(i) = 0) OrElse (closeChange(i) = Chart.NoValue) Then
flatDS.setDataName(label)
ElseIf closeChange(i) > 0 Then
upDS.setDataName(label)
Else
dnDS.setDataName(label)
End If
Return barLayer
End Function
'''
''' Add a blank indicator chart to the finance chart. Used internally to add other indicators.
''' Override to change the default formatting (eg. axis fonts, etc.) of the various indicators.
'''
''' The height of the chart in pixels.
''' The XYChart object representing the chart created.
Public Function addIndicator(ByVal height As Integer) As XYChart
'create a new chart object
Dim ret As New XYChart(m_totalWidth, height + m_topMargin + m_bottomMargin, Chart.Transparent)
ret.setTrimData(m_extraPoints)
If m_currentChart IsNot Nothing Then
'if there is a chart before the newly created chart, disable its x-axis, and copy
'its x-axis labels to the new chart
m_currentChart.xAxis().setColors(Chart.Transparent, Chart.Transparent)
ret.xAxis().copyAxis(m_currentChart.xAxis())
'add chart to MultiChart and update the total height
addChart(0, m_totalHeight + m_plotAreaGap, ret)
m_totalHeight = m_totalHeight + height + 1 + m_plotAreaGap
Else
'no existing chart - create the x-axis labels from scratch
setXLabels(ret.xAxis())
'add chart to MultiChart and update the total height
addChart(0, m_totalHeight, ret)
m_totalHeight = m_totalHeight + height + 1
End If
'the newly created chart becomes the current chart
m_currentChart = ret
'update the size
setSize(m_totalWidth, m_totalHeight + m_topMargin + m_bottomMargin)
'configure the plot area
ret.setPlotArea(m_leftMargin, m_topMargin, m_totalWidth - m_leftMargin - m_rightMargin, height, m_plotAreaBgColor, -1, _
m_plotAreaBorder).setGridColor(m_majorHGridColor, m_majorVGridColor, m_minorHGridColor, m_minorVGridColor)
ret.setAntiAlias(m_antiAlias)
'configure legend box
Dim box As LegendBox = ret.addLegend(m_leftMargin, m_topMargin, False, m_legendFont, m_legendFontSize)
box.setFontColor(m_legendFontColor)
box.setBackground(m_legendBgColor)
box.setMargin2(5, 0, 2, 1)
box.setSize(m_totalWidth - m_leftMargin - m_rightMargin + 1, 0)
'configure x-axis
Dim a As Axis = ret.xAxis()
a.setIndent(True)
a.setTickLength(2, 0)
a.setColors(Chart.Transparent, m_xAxisFontColor, m_xAxisFontColor, m_xAxisFontColor)
a.setLabelStyle(m_xAxisFont, m_xAxisFontSize, m_xAxisFontColor, m_xAxisFontAngle)
'configure y-axis
ret.setYAxisOnRight(m_axisOnRight)
configureYAxis(ret.yAxis(), height)
Return ret
End Function
Private Sub configureYAxis(ByVal a As Axis, ByVal height As Integer)
a.setAutoScale(0, 0.05, 0)
If height < 100 Then
a.setTickDensity(15)
End If
a.setMargin(m_yAxisMargin)
a.setLabelStyle(m_yAxisFont, m_yAxisFontSize, m_yAxisFontColor, 0)
a.setTickLength(-4, -2)
a.setColors(Chart.Transparent, m_yAxisFontColor, m_yAxisFontColor, m_yAxisFontColor)
End Sub
'''
''' Add a generic line indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The data series of the indicator line.
''' The color of the indicator line.
''' The name of the indicator.
''' The XYChart object representing the chart created.
Public Function addLineIndicator(ByVal height As Integer, ByVal data As Double(), ByVal color As Integer, ByVal name As String) As XYChart
Dim c As XYChart = addIndicator(height)
addLineIndicator2(c, data, color, name)
Return c
End Function
'''
''' This method is for backward compatibility.
' - use addLineIndicator(int, double[], int, string) instead.
'''
Public Function addLineIndicator(ByVal height As Integer, ByVal data As ArrayMath, ByVal color As Integer, ByVal name As String) As XYChart
Return addLineIndicator(height, data.result(), color, name)
End Function
'''
''' Add a line to an existing indicator chart.
'''
''' The indicator chart to add the line to.
''' The data series of the indicator line.
''' The color of the indicator line.
''' The name of the indicator.
''' The LineLayer object representing the line created.
Public Function addLineIndicator2(ByVal c As XYChart, ByVal data As Double(), ByVal color As Integer, ByVal name As String) As LineLayer
Return c.addLineLayer(data, color, formatIndicatorLabel(name, data))
End Function
'''
''' This method is for backward compatibility.
' - use addLineIndicator2(XYChart c, double[], int, string) instead.
'''
Public Function addLineIndicator2(ByVal c As XYChart, ByVal data As ArrayMath, ByVal color As Integer, ByVal name As String) As LineLayer
Return addLineIndicator2(c, data.result(), color, name)
End Function
'''
''' Add a generic bar indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The data series of the indicator bars.
''' The color of the indicator bars.
''' The name of the indicator.
''' The XYChart object representing the chart created.
Public Function addBarIndicator(ByVal height As Integer, ByVal data As Double(), ByVal color As Integer, ByVal name As String) As XYChart
Dim c As XYChart = addIndicator(height)
addBarIndicator2(c, data, color, name)
Return c
End Function
'''
''' This method is for backward compatibility.
' - use addBarIndicator(int, double[], int, string) instead.
'''
Public Function addBarIndicator(ByVal height As Integer, ByVal data As ArrayMath, ByVal color As Integer, ByVal name As String) As XYChart
Return addBarIndicator(height, data.result(), color, name)
End Function
'''
''' Add a bar layer to an existing indicator chart.
'''
''' The indicator chart to add the bar layer to.
''' The data series of the indicator bars.
''' The color of the indicator bars.
''' The name of the indicator.
''' The BarLayer object representing the bar layer created.
Public Function addBarIndicator2(ByVal c As XYChart, ByVal data As Double(), ByVal color As Integer, ByVal name As String) As BarLayer
Dim layer As BarLayer = c.addBarLayer(data, color, formatIndicatorLabel(name, data))
layer.setBorderColor(Chart.Transparent)
Return layer
End Function
'''
''' This method is for backward compatibility.
' - use addBarIndicator2(XYChart c, double[], int, string) instead.
'''
Public Function addBarIndicator2(ByVal c As XYChart, ByVal data As ArrayMath, ByVal color As Integer, ByVal name As String) As BarLayer
Return addBarIndicator2(c, data.result(), color, name)
End Function
'''
''' Add an upper/lower threshold range to an existing indicator chart.
'''
''' The indicator chart to add the threshold range to.
''' The line layer that the threshold range applies to.
''' The upper threshold.
''' The color to fill the region of the line that is above the
''' upper threshold.
''' The lower threshold.
''' The color to fill the region of the line that is below
''' the lower threshold.
Public Sub addThreshold(ByVal c As XYChart, ByVal layer As LineLayer, ByVal topRange As Double, ByVal topColor As Integer, ByVal bottomRange As Double, ByVal bottomColor As Integer)
Dim topMark As Mark = c.yAxis().addMark(topRange, topColor, formatValue(topRange, m_generalFormat))
Dim bottomMark As Mark = c.yAxis().addMark(bottomRange, bottomColor, formatValue(bottomRange, m_generalFormat))
c.addInterLineLayer(layer.getLine(), topMark.getLine(), topColor, Chart.Transparent)
c.addInterLineLayer(layer.getLine(), bottomMark.getLine(), Chart.Transparent, bottomColor)
End Sub
Private Function formatIndicatorLabel(ByVal name As String, ByVal data As Double()) As String
Dim i As Integer = lastIndex(data)
If name Is Nothing Then
Return name
End If
If (name = "") OrElse (i < 0) Then
Return name
End If
Dim ret As String = (name & ": ") + formatValue(data(i), m_generalFormat)
Return ret
End Function
'''
''' Add an Accumulation/Distribution indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addAccDist(ByVal height As Integer, ByVal color As Integer) As XYChart
'Close Location Value = ((C - L) - (H - C)) / (H - L)
'Accumulation Distribution Line = Accumulation of CLV * volume
Dim range As Double() = New ArrayMath(m_highData).[sub](m_lowData).result()
Return addLineIndicator(height, New ArrayMath(m_closeData).mul(2).[sub](m_lowData).[sub](m_highData).mul(m_volData).financeDiv(range, 0).acc().result(), color, "Accumulation/Distribution")
End Function
Private Function computeAroonUp(ByVal period As Integer) As Double()
Dim aroonUp As Double() = New Double(m_highData.Length - 1) {}
For i As Integer = 0 To m_highData.Length - 1
Dim highValue As Double = m_highData(i)
If highValue = Chart.NoValue Then
aroonUp(i) = Chart.NoValue
Else
Dim currentIndex As Integer = i
Dim highCount As Integer = period
Dim count As Integer = period
While (count > 0) AndAlso (currentIndex >= count)
currentIndex = currentIndex - 1
Dim currentValue As Double = m_highData(currentIndex)
If currentValue <> Chart.NoValue Then
count = count - 1
If currentValue > highValue Then
highValue = currentValue
highCount = count
End If
End If
End While
If count > 0 Then
aroonUp(i) = Chart.NoValue
Else
aroonUp(i) = highCount * 100.0R / period
End If
End If
Next
Return aroonUp
End Function
Private Function computeAroonDn(ByVal period As Integer) As Double()
Dim aroonDn As Double() = New Double(m_lowData.Length - 1) {}
For i As Integer = 0 To m_lowData.Length - 1
Dim lowValue As Double = m_lowData(i)
If lowValue = Chart.NoValue Then
aroonDn(i) = Chart.NoValue
Else
Dim currentIndex As Integer = i
Dim lowCount As Integer = period
Dim count As Integer = period
While (count > 0) AndAlso (currentIndex >= count)
currentIndex = currentIndex - 1
Dim currentValue As Double = m_lowData(currentIndex)
If currentValue <> Chart.NoValue Then
count = count - 1
If currentValue < lowValue Then
lowValue = currentValue
lowCount = count
End If
End If
End While
If count > 0 Then
aroonDn(i) = Chart.NoValue
Else
aroonDn(i) = lowCount * 100.0R / period
End If
End If
Next
Return aroonDn
End Function
'''
''' Add an Aroon Up/Down indicators chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicators.
''' The color of the Aroon Up indicator line.
''' The color of the Aroon Down indicator line.
''' The XYChart object representing the chart created.
Public Function addAroon(ByVal height As Integer, ByVal period As Integer, ByVal upColor As Integer, ByVal downColor As Integer) As XYChart
Dim c As XYChart = addIndicator(height)
addLineIndicator2(c, computeAroonUp(period), upColor, "Aroon Up")
addLineIndicator2(c, computeAroonDn(period), downColor, "Aroon Down")
c.yAxis().setLinearScale(0, 100)
Return c
End Function
'''
''' Add an Aroon Oscillator indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addAroonOsc(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer) As XYChart
Dim label As String = "Aroon Oscillator (" & period & ")"
Dim c As XYChart = addLineIndicator(height, New ArrayMath(computeAroonUp(period)).[sub](computeAroonDn(period)).result(), color, label)
c.yAxis().setLinearScale(-100, 100)
Return c
End Function
Private Function computeTrueRange() As Double()
Dim previousClose As Double() = New ArrayMath(m_closeData).shift().result()
Dim ret As Double() = New ArrayMath(m_highData).[sub](m_lowData).result()
Dim temp As Double = 0
For i As Integer = 0 To m_highData.Length - 1
If (ret(i) <> Chart.NoValue) AndAlso (previousClose(i) <> Chart.NoValue) Then
temp = Math.Abs(m_highData(i) - previousClose(i))
If temp > ret(i) Then
ret(i) = temp
End If
temp = Math.Abs(previousClose(i) - m_lowData(i))
If temp > ret(i) Then
ret(i) = temp
End If
End If
Next
Return ret
End Function
'''
''' Add an Average Directional Index indicators chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the Positive Directional Index line.
''' The color of the Negatuve Directional Index line.
''' The color of the Average Directional Index line.
''' The XYChart object representing the chart created.
Public Function addADX(ByVal height As Integer, ByVal period As Integer, ByVal posColor As Integer, ByVal negColor As Integer, ByVal color As Integer) As XYChart
'pos/neg directional movement
Dim pos As ArrayMath = New ArrayMath(m_highData).delta().selectGTZ()
Dim neg As ArrayMath = New ArrayMath(m_lowData).delta().mul(-1).selectGTZ()
Dim delta As Double() = New ArrayMath(pos.result()).[sub](neg.result()).result()
pos.selectGTZ(delta)
neg.selectLTZ(delta)
'pos/neg directional index
Dim tr As Double() = computeTrueRange()
pos.financeDiv(tr, 0.25).mul(100).expAvg(2.0R / (period + 1))
neg.financeDiv(tr, 0.25).mul(100).expAvg(2.0R / (period + 1))
'directional movement index ??? what happen if division by zero???
Dim totalDM As Double() = New ArrayMath(pos.result()).add(neg.result()).result()
Dim dx As ArrayMath = New ArrayMath(pos.result()).[sub](neg.result()).abs().financeDiv(totalDM, 0).mul(100).expAvg(2.0R / (period + 1))
Dim c As XYChart = addIndicator(height)
Dim label1 As String = "+DI (" & period & ")"
Dim label2 As String = "-DI (" & period & ")"
Dim label3 As String = "ADX (" & period & ")"
addLineIndicator2(c, pos.result(), posColor, label1)
addLineIndicator2(c, neg.result(), negColor, label2)
addLineIndicator2(c, dx.result(), color, label3)
Return c
End Function
'''
''' Add an Average True Range indicators chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the True Range line.
''' The color of the Average True Range line.
''' The XYChart object representing the chart created.
Public Function addATR(ByVal height As Integer, ByVal period As Integer, ByVal color1 As Integer, ByVal color2 As Integer) As XYChart
Dim trueRange As Double() = computeTrueRange()
Dim c As XYChart = addLineIndicator(height, trueRange, color1, "True Range")
Dim label As String = "Average True Range (" & period & ")"
addLineIndicator2(c, New ArrayMath(trueRange).expAvg(2.0R / (period + 1)).result(), color2, label)
Return c
End Function
'''
''' Add a Bollinger Band Width indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The band width to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addBollingerWidth(ByVal height As Integer, ByVal period As Integer, ByVal width As Double, ByVal color As Integer) As XYChart
Dim label As String = ("Bollinger Width (" & period & ", ") + width & ")"
Return addLineIndicator(height, New ArrayMath(m_closeData).movStdDev(period).mul(width * 2).result(), color, label)
End Function
'''
''' Add a Community Channel Index indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The distance beween the middle line and the upper and lower threshold lines.
''' The fill color when the indicator exceeds the upper threshold line.
''' The fill color when the indicator falls below the lower threshold line.
''' The XYChart object representing the chart created.
Public Function addCCI(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer, ByVal range As Double, ByVal upColor As Integer, ByVal downColor As Integer) As XYChart
'typical price
Dim tp As Double() = New ArrayMath(m_highData).add(m_lowData).add(m_closeData).div(3).result()
'simple moving average of typical price
Dim smvtp As Double() = New ArrayMath(tp).movAvg(period).result()
'compute mean deviation
Dim movMeanDev As Double() = New Double(smvtp.Length - 1) {}
For i As Integer = 0 To smvtp.Length - 1
Dim avg As Double = smvtp(i)
If avg = Chart.NoValue Then
movMeanDev(i) = Chart.NoValue
Else
Dim currentIndex As Integer = i
Dim count As Integer = period - 1
Dim acc As Double = 0
While (count > 0) AndAlso (currentIndex >= count)
currentIndex = currentIndex - 1
Dim currentValue As Double = tp(currentIndex)
If currentValue <> Chart.NoValue Then
count = count - 1
acc = acc + Math.Abs(avg - currentValue)
End If
End While
If count > 0 Then
movMeanDev(i) = Chart.NoValue
Else
movMeanDev(i) = acc / period
End If
End If
Next
Dim c As XYChart = addIndicator(height)
Dim label As String = "CCI (" & period & ")"
Dim layer As LineLayer = addLineIndicator2(c, New ArrayMath(tp).[sub](smvtp).financeDiv(movMeanDev, 0).div(0.015).result(), color, label)
addThreshold(c, layer, range, upColor, -range, downColor)
Return c
End Function
'''
''' Add a Chaikin Money Flow indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addChaikinMoneyFlow(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer) As XYChart
Dim range As Double() = New ArrayMath(m_highData).[sub](m_lowData).result()
Dim volAvg As Double() = New ArrayMath(m_volData).movAvg(period).result()
Dim label As String = "Chaikin Money Flow (" & period & ")"
Return addBarIndicator(height, New ArrayMath(m_closeData).mul(2).[sub](m_lowData).[sub](m_highData).mul(m_volData).financeDiv(range, 0).movAvg(period).financeDiv(volAvg, 0).result(), color, label)
End Function
'''
''' Add a Chaikin Oscillator indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addChaikinOscillator(ByVal height As Integer, ByVal color As Integer) As XYChart
'first compute acc/dist line
Dim range As Double() = New ArrayMath(m_highData).[sub](m_lowData).result()
Dim accdist As Double() = New ArrayMath(m_closeData).mul(2).[sub](m_lowData).[sub](m_highData).mul(m_volData).financeDiv(range, 0).acc().result()
'chaikin osc = exp3(accdist) - exp10(accdist)
Dim expAvg10 As Double() = New ArrayMath(accdist).expAvg(2.0R / (10 + 1)).result()
Return addLineIndicator(height, New ArrayMath(accdist).expAvg(2.0R / (3 + 1)).[sub](expAvg10).result(), color, "Chaikin Oscillator")
End Function
'''
''' Add a Chaikin Volatility indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to smooth the range.
''' The period to compute the rate of change of the smoothed range.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addChaikinVolatility(ByVal height As Integer, ByVal period1 As Integer, ByVal period2 As Integer, ByVal color As Integer) As XYChart
Dim label As String = ("Chaikin Volatility (" & period1 & ", ") + period2 & ")"
Return addLineIndicator(height, New ArrayMath(m_highData).[sub](m_lowData).expAvg(2.0R / (period1 + 1)).rate(period2).[sub](1).mul(100).result(), color, label)
End Function
'''
''' Add a Close Location Value indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addCLV(ByVal height As Integer, ByVal color As Integer) As XYChart
'Close Location Value = ((C - L) - (H - C)) / (H - L)
Dim range As Double() = New ArrayMath(m_highData).[sub](m_lowData).result()
Return addLineIndicator(height, New ArrayMath(m_closeData).mul(2).[sub](m_lowData).[sub](m_highData).financeDiv(range, 0).result(), color, "Close Location Value")
End Function
'''
''' Add a Detrended Price Oscillator indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addDPO(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer) As XYChart
Dim label As String = "DPO (" & period & ")"
Return addLineIndicator(height, New ArrayMath(m_closeData).movAvg(period).shift(period / 2 + 1).[sub](m_closeData).mul(-1).result(), color, label)
End Function
'''
''' Add a Donchian Channel Width indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addDonchianWidth(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer) As XYChart
Dim label As String = "Donchian Width (" & period & ")"
Return addLineIndicator(height, New ArrayMath(m_highData).movMax(period).[sub](New ArrayMath(m_lowData).movMin(period).result()).result(), color, label)
End Function
'''
''' Add a Ease of Movement indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to smooth the indicator.
''' The color of the indicator line.
''' The color of the smoothed indicator line.
''' The XYChart object representing the chart created.
Public Function addEaseOfMovement(ByVal height As Integer, ByVal period As Integer, ByVal color1 As Integer, ByVal color2 As Integer) As XYChart
Dim boxRatioInverted As Double() = New ArrayMath(m_highData).[sub](m_lowData).financeDiv(m_volData, 0).result()
Dim result As Double() = New ArrayMath(m_highData).add(m_lowData).div(2).delta().mul(boxRatioInverted).result()
Dim c As XYChart = addLineIndicator(height, result, color1, "EMV")
Dim label As String = "EMV EMA (" & period & ")"
addLineIndicator2(c, New ArrayMath(result).movAvg(period).result(), color2, label)
Return c
End Function
'''
''' Add a Fast Stochastic indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the %K line.
''' The period to compute the %D line.
''' The color of the %K line.
''' The color of the %D line.
''' The XYChart object representing the chart created.
Public Function addFastStochastic(ByVal height As Integer, ByVal period1 As Integer, ByVal period2 As Integer, ByVal color1 As Integer, ByVal color2 As Integer) As XYChart
Dim movLow As Double() = New ArrayMath(m_lowData).movMin(period1).result()
Dim movRange As Double() = New ArrayMath(m_highData).movMax(period1).[sub](movLow).result()
Dim stochastic As Double() = New ArrayMath(m_closeData).[sub](movLow).financeDiv(movRange, 0.5).mul(100).result()
Dim label1 As String = "Fast Stochastic %K (" & period1 & ")"
Dim c As XYChart = addLineIndicator(height, stochastic, color1, label1)
Dim label2 As String = "%D (" & period2 & ")"
addLineIndicator2(c, New ArrayMath(stochastic).movAvg(period2).result(), color2, label2)
c.yAxis().setLinearScale(0, 100)
Return c
End Function
'''
''' Add a MACD indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The first moving average period to compute the indicator.
''' The second moving average period to compute the indicator.
''' The moving average period of the signal line.
''' The color of the indicator line.
''' The color of the signal line.
''' The color of the divergent bars.
''' The XYChart object representing the chart created.
Public Function addMACD(ByVal height As Integer, ByVal period1 As Integer, ByVal period2 As Integer, ByVal period3 As Integer, ByVal color As Integer, ByVal signalColor As Integer, _
ByVal divColor As Integer) As XYChart
Dim c As XYChart = addIndicator(height)
'MACD is defined as the difference between two exponential averages (typically 12/26 days)
Dim expAvg1 As Double() = New ArrayMath(m_closeData).expAvg(2.0R / (period1 + 1)).result()
Dim macd As Double() = New ArrayMath(m_closeData).expAvg(2.0R / (period2 + 1)).[sub](expAvg1).result()
'Add the MACD line
Dim label1 As String = ("MACD (" & period1 & ", ") + period2 & ")"
addLineIndicator2(c, macd, color, label1)
'MACD signal line
Dim macdSignal As Double() = New ArrayMath(macd).expAvg(2.0R / (period3 + 1)).result()
Dim label2 As String = "EXP (" & period3 & ")"
addLineIndicator2(c, macdSignal, signalColor, label2)
'Divergence
addBarIndicator2(c, New ArrayMath(macd).[sub](macdSignal).result(), divColor, "Divergence")
Return c
End Function
'''
''' Add a Mass Index indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The color of the indicator line.
''' The fill color when the indicator exceeds the upper threshold line.
''' The fill color when the indicator falls below the lower threshold line.
''' The XYChart object representing the chart created.
Public Function addMassIndex(ByVal height As Integer, ByVal color As Integer, ByVal upColor As Integer, ByVal downColor As Integer) As XYChart
'Mass Index
Dim f As Double = 2.0R / (10)
Dim exp9 As Double() = New ArrayMath(m_highData).[sub](m_lowData).expAvg(f).result()
Dim exp99 As Double() = New ArrayMath(exp9).expAvg(f).result()
Dim c As XYChart = addLineIndicator(height, New ArrayMath(exp9).financeDiv(exp99, 1).movAvg(25).mul(25).result(), color, "Mass Index")
c.yAxis().addMark(27, upColor)
c.yAxis().addMark(26.5, downColor)
Return c
End Function
'''
''' Add a Money Flow Index indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The distance beween the middle line and the upper and lower threshold lines.
''' The fill color when the indicator exceeds the upper threshold line.
''' The fill color when the indicator falls below the lower threshold line.
''' The XYChart object representing the chart created.
Public Function addMFI(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer, ByVal range As Double, ByVal upColor As Integer, ByVal downColor As Integer) As XYChart
'Money Flow Index
Dim typicalPrice As Double() = New ArrayMath(m_highData).add(m_lowData).add(m_closeData).div(3).result()
Dim moneyFlow As Double() = New ArrayMath(typicalPrice).mul(m_volData).result()
Dim selector As Double() = New ArrayMath(typicalPrice).delta().result()
Dim posMoneyFlow As Double() = New ArrayMath(moneyFlow).selectGTZ(selector).movAvg(period).result()
Dim posNegMoneyFlow As Double() = New ArrayMath(moneyFlow).selectLTZ(selector).movAvg(period).add(posMoneyFlow).result()
Dim c As XYChart = addIndicator(height)
Dim label As String = "Money Flow Index (" & period & ")"
Dim layer As LineLayer = addLineIndicator2(c, New ArrayMath(posMoneyFlow).financeDiv(posNegMoneyFlow, 0.5).mul(100).result(), color, label)
addThreshold(c, layer, 50 + range, upColor, 50 - range, downColor)
c.yAxis().setLinearScale(0, 100)
Return c
End Function
'''
''' Add a Momentum indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addMomentum(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer) As XYChart
Dim label As String = "Momentum (" & period & ")"
Return addLineIndicator(height, New ArrayMath(m_closeData).delta(period).result(), color, label)
End Function
'''
''' Add a Negative Volume Index indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the signal line.
''' The color of the indicator line.
''' The color of the signal line.
''' The XYChart object representing the chart created.
Public Function addNVI(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer, ByVal signalColor As Integer) As XYChart
Dim nvi As Double() = New Double(m_volData.Length - 1) {}
Dim previousNVI As Double = 100
Dim previousVol As Double = Chart.NoValue
Dim previousClose As Double = Chart.NoValue
For i As Integer = 0 To m_volData.Length - 1
If m_volData(i) = Chart.NoValue Then
nvi(i) = Chart.NoValue
Else
If (previousVol <> Chart.NoValue) AndAlso (m_volData(i) < previousVol) AndAlso (previousClose <> Chart.NoValue) AndAlso (m_closeData(i) <> Chart.NoValue) Then
nvi(i) = previousNVI + previousNVI * (m_closeData(i) - previousClose) / previousClose
Else
nvi(i) = previousNVI
End If
previousNVI = nvi(i)
previousVol = m_volData(i)
previousClose = m_closeData(i)
End If
Next
Dim c As XYChart = addLineIndicator(height, nvi, color, "NVI")
If nvi.Length > period Then
Dim label As String = "NVI SMA (" & period & ")"
addLineIndicator2(c, New ArrayMath(nvi).movAvg(period).result(), signalColor, label)
End If
Return c
End Function
'''
''' Add an On Balance Volume indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addOBV(ByVal height As Integer, ByVal color As Integer) As XYChart
Dim closeChange As Double() = New ArrayMath(m_closeData).delta().result()
Dim upVolume As Double() = New ArrayMath(m_volData).selectGTZ(closeChange).result()
Dim downVolume As Double() = New ArrayMath(m_volData).selectLTZ(closeChange).result()
Return addLineIndicator(height, New ArrayMath(upVolume).[sub](downVolume).acc().result(), color, "OBV")
End Function
'''
''' Add a Performance indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addPerformance(ByVal height As Integer, ByVal color As Integer) As XYChart
Dim closeValue As Double = firstCloseValue()
If closeValue <> Chart.NoValue Then
Return addLineIndicator(height, New ArrayMath(m_closeData).mul(100 / closeValue).[sub](100).result(), color, "Performance")
Else
'chart is empty !!!
Return addIndicator(height)
End If
End Function
'''
''' Add a Percentage Price Oscillator indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The first moving average period to compute the indicator.
''' The second moving average period to compute the indicator.
''' The moving average period of the signal line.
''' The color of the indicator line.
''' The color of the signal line.
''' The color of the divergent bars.
''' The XYChart object representing the chart created.
Public Function addPPO(ByVal height As Integer, ByVal period1 As Integer, ByVal period2 As Integer, ByVal period3 As Integer, ByVal color As Integer, ByVal signalColor As Integer, _
ByVal divColor As Integer) As XYChart
Dim expAvg1 As Double() = New ArrayMath(m_closeData).expAvg(2.0R / (period1 + 1)).result()
Dim expAvg2 As Double() = New ArrayMath(m_closeData).expAvg(2.0R / (period2 + 1)).result()
Dim ppo As ArrayMath = New ArrayMath(expAvg2).[sub](expAvg1).financeDiv(expAvg2, 0).mul(100)
Dim ppoSignal As Double() = New ArrayMath(ppo.result()).expAvg(2.0R / (period3 + 1)).result()
Dim label1 As String = ("PPO (" & period1 & ", ") + period2 & ")"
Dim label2 As String = "EMA (" & period3 & ")"
Dim c As XYChart = addLineIndicator(height, ppo.result(), color, label1)
addLineIndicator2(c, ppoSignal, signalColor, label2)
addBarIndicator2(c, ppo.[sub](ppoSignal).result(), divColor, "Divergence")
Return c
End Function
'''
''' Add a Positive Volume Index indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the signal line.
''' The color of the indicator line.
''' The color of the signal line.
''' The XYChart object representing the chart created.
Public Function addPVI(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer, ByVal signalColor As Integer) As XYChart
'Positive Volume Index
Dim pvi As Double() = New Double(m_volData.Length - 1) {}
Dim previousPVI As Double = 100
Dim previousVol As Double = Chart.NoValue
Dim previousClose As Double = Chart.NoValue
For i As Integer = 0 To m_volData.Length - 1
If m_volData(i) = Chart.NoValue Then
pvi(i) = Chart.NoValue
Else
If (previousVol <> Chart.NoValue) AndAlso (m_volData(i) > previousVol) AndAlso (previousClose <> Chart.NoValue) AndAlso (m_closeData(i) <> Chart.NoValue) Then
pvi(i) = previousPVI + previousPVI * (m_closeData(i) - previousClose) / previousClose
Else
pvi(i) = previousPVI
End If
previousPVI = pvi(i)
previousVol = m_volData(i)
previousClose = m_closeData(i)
End If
Next
Dim c As XYChart = addLineIndicator(height, pvi, color, "PVI")
If pvi.Length > period Then
Dim label As String = "PVI SMA (" & period & ")"
addLineIndicator2(c, New ArrayMath(pvi).movAvg(period).result(), signalColor, label)
End If
Return c
End Function
'''
''' Add a Percentage Volume Oscillator indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The first moving average period to compute the indicator.
''' The second moving average period to compute the indicator.
''' The moving average period of the signal line.
''' The color of the indicator line.
''' The color of the signal line.
''' The color of the divergent bars.
''' The XYChart object representing the chart created.
Public Function addPVO(ByVal height As Integer, ByVal period1 As Integer, ByVal period2 As Integer, ByVal period3 As Integer, ByVal color As Integer, ByVal signalColor As Integer, _
ByVal divColor As Integer) As XYChart
Dim expAvg1 As Double() = New ArrayMath(m_volData).expAvg(2.0R / (period1 + 1)).result()
Dim expAvg2 As Double() = New ArrayMath(m_volData).expAvg(2.0R / (period2 + 1)).result()
Dim pvo As ArrayMath = New ArrayMath(expAvg2).[sub](expAvg1).financeDiv(expAvg2, 0).mul(100)
Dim pvoSignal As Double() = New ArrayMath(pvo.result()).expAvg(2.0R / (period3 + 1)).result()
Dim label1 As String = ("PVO (" & period1 & ", ") + period2 & ")"
Dim label2 As String = "EMA (" & period3 & ")"
Dim c As XYChart = addLineIndicator(height, pvo.result(), color, label1)
addLineIndicator2(c, pvoSignal, signalColor, label2)
addBarIndicator2(c, pvo.[sub](pvoSignal).result(), divColor, "Divergence")
Return c
End Function
'''
''' Add a Price Volumne Trend indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addPVT(ByVal height As Integer, ByVal color As Integer) As XYChart
Return addLineIndicator(height, New ArrayMath(m_closeData).rate().[sub](1).mul(m_volData).acc().result(), color, "PVT")
End Function
'''
''' Add a Rate of Change indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addROC(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer) As XYChart
Dim label As String = "ROC (" & period & ")"
Return addLineIndicator(height, New ArrayMath(m_closeData).rate(period).[sub](1).mul(100).result(), color, label)
End Function
Private Function RSIMovAvg(ByVal data As Double(), ByVal period As Integer) As Double()
'The "moving average" in classical RSI is based on a formula that mixes simple
'and exponential moving averages.
If period <= 0 Then
period = 1
End If
Dim count As Integer = 0
Dim acc As Double = 0
For i As Integer = 0 To data.Length - 1
If Math.Abs(data(i) / Chart.NoValue - 1) > 0.00001R Then
count = count + 1
acc = acc + data(i)
If count < period Then
data(i) = Chart.NoValue
Else
data(i) = acc / period
acc = data(i) * (period - 1)
End If
End If
Next
Return data
End Function
Private Function computeRSI(ByVal period As Integer) As Double()
'RSI is defined as the average up changes for the last 14 days, divided by the
'average absolute changes for the last 14 days, expressed as a percentage.
Dim absChange As Double() = RSIMovAvg(New ArrayMath(m_closeData).delta().abs().result(), period)
Dim absUpChange As Double() = RSIMovAvg(New ArrayMath(m_closeData).delta().selectGTZ().result(), period)
Return New ArrayMath(absUpChange).financeDiv(absChange, 0.5).mul(100).result()
End Function
'''
''' Add a Relative Strength Index indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The distance beween the middle line and the upper and lower threshold lines.
''' The fill color when the indicator exceeds the upper threshold line.
''' The fill color when the indicator falls below the lower threshold line.
''' The XYChart object representing the chart created.
Public Function addRSI(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer, ByVal range As Double, ByVal upColor As Integer, ByVal downColor As Integer) As XYChart
Dim c As XYChart = addIndicator(height)
Dim label As String = "RSI (" & period & ")"
Dim layer As LineLayer = addLineIndicator2(c, computeRSI(period), color, label)
'Add range if given
If (range > 0) AndAlso (range < 50) Then
addThreshold(c, layer, 50 + range, upColor, 50 - range, downColor)
End If
c.yAxis().setLinearScale(0, 100)
Return c
End Function
'''
''' Add a Slow Stochastic indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the %K line.
''' The period to compute the %D line.
''' The color of the %K line.
''' The color of the %D line.
''' The XYChart object representing the chart created.
Public Function addSlowStochastic(ByVal height As Integer, ByVal period1 As Integer, ByVal period2 As Integer, ByVal color1 As Integer, ByVal color2 As Integer) As XYChart
Dim movLow As Double() = New ArrayMath(m_lowData).movMin(period1).result()
Dim movRange As Double() = New ArrayMath(m_highData).movMax(period1).[sub](movLow).result()
Dim stochastic As ArrayMath = New ArrayMath(m_closeData).[sub](movLow).financeDiv(movRange, 0.5).mul(100).movAvg(3)
Dim label1 As String = "Slow Stochastic %K (" & period1 & ")"
Dim label2 As String = "%D (" & period2 & ")"
Dim c As XYChart = addLineIndicator(height, stochastic.result(), color1, label1)
addLineIndicator2(c, stochastic.movAvg(period2).result(), color2, label2)
c.yAxis().setLinearScale(0, 100)
Return c
End Function
'''
''' Add a Moving Standard Deviation indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addStdDev(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer) As XYChart
Dim label As String = "Moving StdDev (" & period & ")"
Return addLineIndicator(height, New ArrayMath(m_closeData).movStdDev(period).result(), color, label)
End Function
'''
''' Add a Stochastic RSI indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The distance beween the middle line and the upper and lower threshold lines.
''' The fill color when the indicator exceeds the upper threshold line.
''' The fill color when the indicator falls below the lower threshold line.
''' The XYChart object representing the chart created.
Public Function addStochRSI(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer, ByVal range As Double, ByVal upColor As Integer, ByVal downColor As Integer) As XYChart
Dim rsi As Double() = computeRSI(period)
Dim movLow As Double() = New ArrayMath(rsi).movMin(period).result()
Dim movRange As Double() = New ArrayMath(rsi).movMax(period).[sub](movLow).result()
Dim c As XYChart = addIndicator(height)
Dim label As String = "StochRSI (" & period & ")"
Dim layer As LineLayer = addLineIndicator2(c, New ArrayMath(rsi).[sub](movLow).financeDiv(movRange, 0.5).mul(100).result(), color, label)
'Add range if given
If (range > 0) AndAlso (range < 50) Then
addThreshold(c, layer, 50 + range, upColor, 50 - range, downColor)
End If
c.yAxis().setLinearScale(0, 100)
Return c
End Function
'''
''' Add a TRIX indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The XYChart object representing the chart created.
Public Function addTRIX(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer) As XYChart
Dim f As Double = 2.0R / (period + 1)
Dim label As String = "TRIX (" & period & ")"
Return addLineIndicator(height, New ArrayMath(m_closeData).expAvg(f).expAvg(f).expAvg(f).rate().[sub](1).mul(100).result(), color, label)
End Function
Private Function computeTrueLow() As Double()
'the lower of today's low or yesterday's close.
Dim previousClose As Double() = New ArrayMath(m_closeData).shift().result()
Dim ret As Double() = New Double(m_lowData.Length - 1) {}
For i As Integer = 0 To m_lowData.Length - 1
If (m_lowData(i) <> Chart.NoValue) AndAlso (previousClose(i) <> Chart.NoValue) Then
If m_lowData(i) < previousClose(i) Then
ret(i) = m_lowData(i)
Else
ret(i) = previousClose(i)
End If
Else
ret(i) = Chart.NoValue
End If
Next
Return ret
End Function
'''
''' Add an Ultimate Oscillator indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The first moving average period to compute the indicator.
''' The second moving average period to compute the indicator.
''' The third moving average period to compute the indicator.
''' The color of the indicator line.
''' The distance beween the middle line and the upper and lower threshold lines.
''' The fill color when the indicator exceeds the upper threshold line.
''' The fill color when the indicator falls below the lower threshold line.
''' The XYChart object representing the chart created.
Public Function addUltimateOscillator(ByVal height As Integer, ByVal period1 As Integer, ByVal period2 As Integer, ByVal period3 As Integer, ByVal color As Integer, ByVal range As Double, _
ByVal upColor As Integer, ByVal downColor As Integer) As XYChart
Dim trueLow As Double() = computeTrueLow()
Dim buyingPressure As Double() = New ArrayMath(m_closeData).[sub](trueLow).result()
Dim trueRange As Double() = computeTrueRange()
Dim rawUO1 As Double() = New ArrayMath(buyingPressure).movAvg(period1).financeDiv(New ArrayMath(trueRange).movAvg(period1).result(), 0.5).mul(4).result()
Dim rawUO2 As Double() = New ArrayMath(buyingPressure).movAvg(period2).financeDiv(New ArrayMath(trueRange).movAvg(period2).result(), 0.5).mul(2).result()
Dim rawUO3 As Double() = New ArrayMath(buyingPressure).movAvg(period3).financeDiv(New ArrayMath(trueRange).movAvg(period3).result(), 0.5).mul(1).result()
Dim c As XYChart = addIndicator(height)
Dim label As String = (("Ultimate Oscillator (" & period1 & ", ") + period2 & ", ") + period3 & ")"
Dim layer As LineLayer = addLineIndicator2(c, New ArrayMath(rawUO1).add(rawUO2).add(rawUO3).mul(100.0R / 7).result(), color, label)
addThreshold(c, layer, 50 + range, upColor, 50 - range, downColor)
c.yAxis().setLinearScale(0, 100)
Return c
End Function
'''
''' Add a Volume indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The color to used on an 'up' day. An 'up' day is a day where
''' the closing price is higher than that of the previous day.
''' The color to used on a 'down' day. A 'down' day is a day
''' where the closing price is lower than that of the previous day.
''' The color to used on a 'flat' day. A 'flat' day is a day
''' where the closing price is the same as that of the previous day.
''' The XYChart object representing the chart created.
Public Function addVolIndicator(ByVal height As Integer, ByVal upColor As Integer, ByVal downColor As Integer, ByVal flatColor As Integer) As XYChart
Dim c As XYChart = addIndicator(height)
addVolBars2(c, height, upColor, downColor, flatColor)
Return c
End Function
'''
''' Add a William %R indicator chart.
'''
''' The height of the indicator chart in pixels.
''' The period to compute the indicator.
''' The color of the indicator line.
''' The distance beween the middle line and the upper and lower threshold lines.
''' The fill color when the indicator exceeds the upper threshold line.
''' The fill color when the indicator falls below the lower threshold line.
''' The XYChart object representing the chart created.
Public Function addWilliamR(ByVal height As Integer, ByVal period As Integer, ByVal color As Integer, ByVal range As Double, ByVal upColor As Integer, ByVal downColor As Integer) As XYChart
Dim movLow As Double() = New ArrayMath(m_lowData).movMin(period).result()
Dim movHigh As Double() = New ArrayMath(m_highData).movMax(period).result()
Dim movRange As Double() = New ArrayMath(movHigh).[sub](movLow).result()
Dim c As XYChart = addIndicator(height)
Dim layer As LineLayer = addLineIndicator2(c, New ArrayMath(movHigh).[sub](m_closeData).financeDiv(movRange, 0.5).mul(-100).result(), color, "William %R")
addThreshold(c, layer, -50 + range, upColor, -50 - range, downColor)
c.yAxis().setLinearScale(-100, 0)
Return c
End Function
'''
''' This method is for backward compatibility. It has no purpose.
'''
Public Sub layoutChart()
'do nothing
End Sub
End Class
End Namespace