Click or drag to resize

How To develop an Indicator

An indicator is a calculation of different market matrix and is plotted on a chart (or in a separate panel) as a series to analyze the market. ArthaChitra natively comes with a set of popular indicators. However if the user wishes to develop his/her own logic into an indicator then ArthaChitra provides a platform to do so.

The main calculations are stored in the Values property which is an array of ISeriesT, where T is a double. The Values property is created by calling the AddPlot(Plot) method. The AddPlot method can be called only when the indicator State is in Initialize State.

If the user chooses to draw a horizontal line then he/she can do so by calling the AddLine(Line) method. Like the AddPlot method, the AddLine method can be only called when the indicator is in initialized state.

Understanding State

During the life cycle of an indicator it goes through different stages or processes. In each stage the indicator performs a set of jobs. In which state the indicator s in is defined by the State property. Whenever the state gets changed it raises the method OnStateChange. The different states which an indicator goes through are:

  • Initialize - Here the plot values and line values are defined.
  • StartUp - User should initialize indicators, series here.
  • Historical - When historical data is being processed.
  • RealTime - When incoming ticks are being processed in realtime.
  • Finalize - When the indicator encounters an error or is being finalized (disposed off). All clear up codes should be placed here.
Abstract methods

OnStateChange

The OnStateChanged method is called whenever the State of the indicator is changed. Please refer here to know more about the State enumeration

protected override void OnStateChange()
{
    //your code here
}

OnBarUpdate

The main business logic, pertaining to how the series plot values are computed, generally resides in the OnBarUpdate method. The OnBarUpdate method is called whenever a new tick comes in. How this method is called depends on the Calculate property. If calculate property is set to OnEachTick then this method is called on each incoming tick. If the Calculate property is set as OnPriceChange then OnBarUpdate will be called when the previous price differs from the last price.

protected override void OnBarUpdate()
{
    //your code here
}
Add reference Indicators

To add reference to another indicator please use the AddIndicator method.

//reference the SMA
SMA sma = AddIndicator<SMA>(new object[] { 14 });
//If you want to call reference the Avg series of the MacD indicator
ISeries<double> macdAvg = AddIndicator<MacD>(new object[]{ 14, 26, 9 }).Avg;

Note: new object[]{} is the parameter values of the specific indicators constructor.

The above method however pose a challange as in absense of intellisense defining the exact constractor parameter can often result in ambiguity. To avoid this developer might consider defining a custom methods in the AddOrGetIndicator class to access the indicator. Users then can easily call those methods to access that indicator. The below codes demonstrates how to define the SMA indicator and how it is accessed by other indicators.

public static partial class AddOrGetIndicator
{
    public static SMA SMA(SharpScriptBase owner, int period)
    {
        return SMA(owner, owner.Input, period);
    }

    public static SMA SMA(SharpScriptBase owner, ISeries<double> input, int period)
    {
        return CacheIndicator<SMA>(owner, input, new object[]{ period });
    }

}

Users can access the same as:

SMA sma = AddOrGetIndicator.SMA(this, 14);    //calls a 14 peirod simple moving average

A referenced indicator must be initialized when the scripts is in Initialize or StartUp state.

    //declare a private field
    private SMA sma;

protected override void OnStateChange()
{
    switch (base.State)
    {
            case State.Finalize:
                break;
            case State.Initialize:
                break;
            case State.StartUp:
                //initialize the indicator
                this.sma = AddOrGetIndicator.SMA(this, 14);
                break;
            default:
                break;
    }
}

protected override void OnBarUpdate()
{
    //now refer it as below
    double smaValue = sma[0];

    //other codes
}
How to add an Drawing/Chart object

You can add a drawing object via a SharpScript code. For example the below code will add a line from the current running bar to the last 10 (ten) bars back at closing price.

if (CurrentBar > 10)
{
    Draw.Line(this, "line", Time[0], Close[0], Time[10], Close[10]);
}

If the same tag name is used then the existing chartObject will be replaced. If you wish to create multiple chartObjects then please use unique tag names for the chartObjects

While drawing any draw object user must check for thread safety as shown below.

if (base.CrossAbove(Close, 100.0d, 1))
{
    if (this.Dispatcher.CheckAccess())
    {
        Draw.Diamond(this, "diamond", Time[0], Low[0]);
    }
    else 
    {
        this.Dispatcher.InvokeAsync(() =>
        {
            Draw.Diamond(this, "diamond", Time[0], Low[0]);
        });
    }    
}

Version 1.0.0.30 introduces a method InvokeAsync and it is recommended to invoke any thread sensitive codes via it. For example to add a draw object user can simply use the below code:

if (base.CrossAbove(Close, 100.0d, 1))
{
    InvokeAsync(() => Draw.Diamond(this, "diamond", Time[0], Low[0])));
}
Alert in Market Scanner

If user applies an indicator in the Market Scanner View and want to define how the alerts will trigger then he/she can override the AlertScanner(TimeSpan) method. The below code as found in the RSI indicator further illustrates it.

#region Scanner Alert

    protected override void AlertScanner(TimeSpan rearm)
    {
        if (CrossAbove(Values[0], 70.0d, 1))
        {
            Alert("RSIUp", string.Format("RSI above 70 @{0}", Math.Round(Values[0][0], 2)), rearm);
        }
        else if (CrossBelow(Values[0], 30.0d, 1))
        {
            Alert("RSIDown", string.Format("RSI below 30 @{0}", Math.Round(Values[0][0], 2)), rearm);
        }
    }

    #endregion
Xaml
See Also