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