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.
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:
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 }
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 }
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 }
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]))); }
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