Profile Picture

Another indicator sample

Posted By billb 12 Years Ago
Posted Friday December 08 2006
At the request of another member, I've decided to post the source code to another indicator to serve as an example.  This is our On Balance Volume code, slightly modified for clarity.

using System;
using System.Collections.Generic;
using System.Text;
using RightEdge.Common;

namespace MyIndicators
        Name =
        Description =
"My On Balance Volume implementation",
        Id =
"Generate a unique ID here",
        DefaultDrawingPane =
        HelpText =
"Put some detailed text here if desired.")]
public class MyOBV : IndicatorBase
private double previousValue = 0.0;
private List<BarData> pBars = null;

        /// Constructs an On Balance Volume indicator instance.
        public MyOBV()
pBars = new List<BarData>();

        /// Calculates the values for an entire series.
public override List<double> CalcSeriesValues(IList<BarData> bars)
            pBars =
new List<BarData>();
return base.CalcSeriesValues(bars);

        /// Calculates the next value in the indicator series.
        public override double CalcNextValue(BarData bar)
            int pBarIndex = pBars.Count - 1;

if (pBarIndex == 0)
// This is our first non-empty bar
previousValue = double.NaN;
return double.NaN;
double todaysClose = bar.Close;
double yesterdaysClose = pBars[pBarIndex - 1].Close;
double todaysVolume = bar.Volume;
if (double.IsNaN(previousValue))
                    previousValue = todaysVolume;
return todaysVolume;
double yesterdaysOBV = previousValue;
if (todaysClose > yesterdaysClose)
                        previousValue = yesterdaysOBV + todaysVolume;
return yesterdaysOBV + todaysVolume;
                        previousValue = yesterdaysOBV - todaysVolume;
return yesterdaysOBV - todaysVolume;

The meat is really in CalcNextValue().  This is called each time a new bar comes in, so we perform the calculation and return the value.

CalcSeriesValues() is used for optimizing an entire series of calculations.  In other words, many indicators can be calculated independently, while some rely on previously calculated values.  If you can store those previously calc'ed values instead of having to recalculate them everytime, you save a lot of cycles.  That's really what this function is for.  We don't need it in this case.

The attributes at the top of the class are used for the user interface and persistence.  We're specifying a default color of Crimson, we're asking RightEdge to place this in the "Volume" tree node in the indicator pane, it will show up as "OBV" in the indicator list.  Now the ID is important because this is what RightEdge uses to identify this indicator so that it can save and load it from a saved chart for example.  We typically generate a GUID as the ID.  You can generate this ID in Visual Studio if you're familiar with that tool, or use the one at  DefaultDrawingPane proposes a default pane name to the user when they drag and drop this indicator.  HelpText is the text that is displayed when a user selects Indicator Information from the indicator pane.

Posted Sunday December 10 2006
Would it be possible to get an example in VB?
Posted Monday December 11 2006
Sure.  Here's the code (I think my VB conversion is pretty close).  I've also attached the project file for your convenience.

Imports System
Imports System.Collections.Generic
Imports System.Text
Imports RightEdge.Common
Imports RightEdge.Indicators

Namespace MyIndicators

<YYEIndicatorAttribute(System.Drawing.KnownColor.Crimson, YYEIndicatorAttribute.EIndicatorGroup.Volatility, Author:="You", CompanyName:="", DefaultDrawingPane:="OBVPane", Description:="Your Indicator Description here", GroupName:="", HelpText:="Add help text that will be displayed in the RightEdge user interface here.", Id:="<Generate a unique ID here>", Name:="Your Indicator Name Here", Version:="0.1")> _

<Serializable()> _

<SeriesInputAttribute(Name:="Input", Order:=1, Value:=BarElement.Close)> _

Public Class MyRightEdgeIndicator
Inherits SeriesCalculatorBaseWithValues

Private periods As Integer = 20
Private shift As Double = 5
Private sma As SMA = Nothing
Private validCount As Integer = 0

Public Sub New(ByVal periods As Integer, ByVal shift As Double)
Me.periods = periods
Me.shift = shift
       sma =
New SMA(periods)
End Sub

    Protected Overloads Overrides Function CalcNewValue(ByVal index As Integer) As Double
System.Math.Min(System.Threading.Interlocked.Increment(validCount), validCount - 1)
If validCount < periods Then
Return Double.NaN
End If

        Dim price As Double = inputs(0)(index)
Return price - (sma(index) * (shift / 100))
    End Function

Protected Overloads Overrides Sub Reset()
        validCount = 0
End Sub

Public Overloads Overrides Sub NewBar()
End Sub

Public Overloads Overrides Sub NewSeries(ByVal count As Integer)
End Sub

    Public Overloads Overrides Sub SetInputs(ByVal ParamArray newInputs As ISeries())
    End Sub
End Class

Attachments (391 views, 218.00 KB)
Posted Monday December 11 2006
Thanks for that.

Just one question for now. How would I add a signal line to that moving average? Can I do it through code, or do I have to manually add two SMA's to the chart?

Posted Monday December 11 2006
There's only one series per indicator.  So you'd have to build another indicator that calculates the signal line, or you can do it through the user interface.  In other words, you would be your indicator and for the series line, "chain" this indicator to an SMA.  There's a good example of this using the MACD and a MACD signal line here:

BigBerner (12/11/2006)
Thanks for that.

Just one question for now. How would I add a signal line to that moving average? Can I do it through code, or do I have to manually add two SMA's to the chart?

Posted Monday December 11 2006
OK thanks.

One more question. I don't really understand the line of code Dim price as Double = inputs(0)(index). Can you explain the inputs(0)(index) part - I can't see where they are coming from.

Posted Monday December 11 2006
Sure, the inputs come from the SeriesCalculatorBase class.  This is the core of what allows indicators to be chained.  Since this indicator is derived from SeriesCalculatorBaseWithValues, which is derived from SeriesCalculatorBase, the inputs variable is populated for you.  This gives you the input parameter.  In cases where you can have multiple inputs, this array would hold those values.

Here's some of the relevant documentation.

Note, if you want a really simple indicator that takes one input and doesn't need to support chaining with another indicator (in other words, will always take a piece of bar data), derive from IndicatorBase.

Keep firing off the questions as they come up.  I know getting your head around the various classes is a bit of a task at first.  Hopefully you'll find that they make life easy.

Similar Topics

Reading This Topic

2005-2018 © RightEdge Systems