OnTick

What is it??

The OnTick method is one of the core functions used in a custom strategy. It’s responsible for calculating the logic of your strategy for each bar (candle) on the chart.

This function runs automatically on each price update (tick) and recalculates values for the given bar.


Working with bars

In the OnTick() method, you work with bars using specific indices to access their data.

Index 0 always represents the current bar, which can be used for non-critical indicator calculations.

Index 1 represents the previous bar, ideal for confirmed signals.

For example:

this.api.Close(0) gets the close price of the current bar.

this.api.Close(1) gets the close price of the previous bar.

this.api.High(2) gets the high price from two bars ago.

Choose the index based on whether you need confirmed data or are working with flexible conditions.


Example Explained

public OnTick() {
    if (this.MAFastPeriod.value >= this.MASlowPeriod.value) {
      console.error("Fast MA Period must be less than Slow MA Period");
      return;
    }

    // process logic only once per bar opening
    const currentTime = this.api.Time(0);
    if (currentTime.valueOf() === this.lastBarTime.valueOf()) {
      return;
    }
    this.lastBarTime = currentTime;

    // get current and previous MA values
    const currSlowMA = this.getMAValue(this.MASlowPeriod.value, 1);
    const prevSlowMA = this.getMAValue(this.MASlowPeriod.value, 2);

    const currFastMA = this.getMAValue(this.MAFastPeriod.value, 1);
    const prevFastMA = this.getMAValue(this.MAFastPeriod.value, 2);

    // check for buy signal (bullish crossover)
    if (currFastMA > currSlowMA && prevFastMA <= prevSlowMA) {
      this.closePosition(TTradePositionType.SELL, this.MagicNumber.value);
      this.placePosition(TTradePositionType.BUY, this.MagicNumber.value);
    }

    // check for bearish crossover
    if (currFastMA < currSlowMA && prevFastMA >= prevSlowMA) {
      this.closePosition(TTradePositionType.BUY, this.MagicNumber.value);
      this.placePosition(TTradePositionType.SELL, this.MagicNumber.value);
    }
  }

  private closePosition(
    positionType: TTradePositionType,
    magicNumber: number = 0
  ) {
    for (let i = this.api.getActiveOrderCount() - 1; i >= 0; i--) {
      if (
        this.api.selectOrder(i, 0, 0) &&
        this.api.getOrderSymbol() === this.api.Symbol() &&
        this.api.getOrderType() === positionType &&
        (magicNumber === 0 || this.api.getOrderMagicNumber() === magicNumber)
      ) {
        const ticket = this.api.getOrderTicket();
        this.api.closeOrder(ticket);
      }
    }
  }

  private placePosition(
    positionType: TTradePositionType,
    magicNumber: number = 0
  ) {
    const ticket = this.api.placeOrder(
      this.api.Symbol(),
      positionType,
      0,
      this.LotSize.value,
      0,
      0,
      "MACrossOverStrategy",
      magicNumber
    );
    if (ticket === null) {
      console.error(
        `Position ${
          TTradePositionType[positionType]
        } failed with error #${this.api.GetLastError()}`
      );
    }
  }

  private getMAValue(period: number, index: number) {
    return this.api.iMA(
      this.api.Symbol(),
      this.api.Timeframe(),
      period,
      0,
      this.MAMethod.value,
      TPriceType.CLOSE,
      index
    );
  }

In Short

  • This method is where you put your main logic.

  • It runs automatically for each bar.

  • You should use it to calculate and store strategy values.

Last updated