Tutorial: Moving Average
In this tutorial, we will look at an example implementation of the MovingAverage
indicator.
To get acquainted with the implementation example, download the archive with the indicator example and open it in IDE of your choice, we suggest you use Cursor IDE.
You can download the archive of Moving Average from here
Indicator structure
A custom indicator is built by extending the IndicatorImplementation
class, provided by the forex-tester-custom-indicator-api
library.
You can find this implementation in the source code of the Moving Average example described in the Setup and Installation section.
import { IndicatorImplementation } from "forex-tester-custom-indicator-api";
export default class MovingAverage extends IndicatorImplementation {
// indicator logic
}
Indicator parameters
These parameters can be of different types, they are determined by the TOptValue class, and they will be displayed in the indicator addition/editing window
export default class MovingAverage extends IndicatorImplementation {
// Declaring class-level fields
public Period!: TOptValue_number;
public Shift!: TOptValue_number;
public MAtype!: TOptValue_number;
public ApplyToPrice!: TOptValue_number;
public VShift!: TOptValue_number;
Init(): void {
// Create parameters using factory method
this.Period = this.api.createTOptValue_number(8);
this.Shift = this.api.createTOptValue_number(0);
this.MAtype = this.api.createTOptValue_number(E_MAType.SMA);
this.ApplyToPrice = this.api.createTOptValue_number(TPriceType.CLOSE);
this.VShift = this.api.createTOptValue_number(0);
...existing code...
}

For this, they need to be registered in the Init function.
public Init(): void {
...existing code...
// Register parameter this.Period so it's shown in the indicator settings
this.api.RegOption(
'Period',
TOptionType.INTEGER,
this.Period
);
// Setting the maximum avalable range that can be used for Period value
this.api.SetOptionRange(
'Period',
1,
Number.MAX_SAFE_INTEGER
);
// Register parameter this.Shift so it's shown in the indicator settings
this.api.RegOption(
'Shift',
TOptionType.INTEGER,
this.Shift
);
// Register parameter this.VShift so it's its shown in the indicator settings
this.api.RegOption(
'VShift',
TOptionType.INTEGER,
this.VShift
);
// Register the MA type so it has a drowdown in the indicator settings
this.api.RegMATypeOption(
this.MAtype,
'MAtype'
);
// Register the price type so it has a dropdown in the indicator settings.
this.api.RegApplyToPriceOption(
this.ApplyToPrice,
'ApplyToPrice'
);
...existing code...
}
You can see the methods for registering parameters in the external parameters definition
Buffers setup
Buffers are used to store and display indicator values on the chart.
public SSMA!: TIndexBuffer
private SMA!: TIndexBuffer
They need to be declared with all class fields and initialized in the Init function
this.SMA = this.api.CreateIndexBuffer();
this.SSMA = this.api.CreateIndexBuffer();
After their creation, you need to tell how many buffers will be displayed on the chart and bind them by index, starting from 0 (the indices must be unique) In this case, there is one buffer
this.api.IndicatorBuffers(1);
this.api.SetIndexBuffer(0, this.SSMA);
Each registered buffer can be configured
this.api.SetIndexLabel(0, "MA");
this.api.SetIndexStyle(0, TDrawStyle.LINE, TPenStyle.SOLID, 1, "#FF0000");
this.api.SetIndexDrawBegin(0, this.Period.value - 1 + this.Shift.value);
Other settings
Also, other methods for configuring the indicator are used in the Init function. To ensure the indicator recalculates on each tick, use the function RecalculateMeAlways. If this setting is not used, each buffer index will be calculated only once to save resources, but some indicators may be calculated inaccurately. If the calculations do not heavily load the processor, we recommend always using it.
Set the indicator name, which will be displayed in the indicator settings window and in the context menu: IndicatorShortName.
We want the Moving Average indicator to be displayed on the main chart, so we use SetOutputWindow.
Using this call, specify that values equal to 0 will not be drawn on the chart: SetEmptyValue.
Indicator's main function
The main function of the indicator is the Calculate function, where the indicator values are calculated on each tick.
public Calculate(index: number): void {
// check if the index is in the valid range
if (index + this.Period.value >= this.api.Bars()) {
return
}
// calculate the SMA value
const calculatedSMA = this.api.GetMA(
index,
0,
this.Period.value,
this.MAtype.value,
this.ApplyToPrice.value,
// here we get the value of the previous bar
this.SMA.getValue(index + 1)
)
this.SMA.setValue(index, calculatedSMA)
// set the value which is going to be displayed on the chart
this.SSMA.setValue(index, calculatedSMA + this.VShift.value * this.api.Point())
}
Changing parameters
To add custom logic after changing the indicator parameters, we use the OnParamsChange method. In Moving average, it is applied to the horizontal shift of the indicator. This method is often used to work with custom objects or to shift the indicator.
public OnParamsChange(): void {
this.api.SetBufferShift(0, this.Shift.value)
}
Last updated