Indicator calls other indicator

3 EMAs

Sometimes it's needed to use another indicator results to implement a new indicator. Here we create an example indicator step by step, that outputs 3 EMA outputs in one shot.

  1. Create new indicator from JForex. New editor will open with example indicator in it.

  2. We will need IIndicatorsProvider from IIndicatorContext to get EMA indicator. onStart method is the good place to get it:

    IIndicatorsProvider indicatorsProvider = context.getIndicatorsProvider();
    ema = indicatorsProvider.getIndicator("EMA");
  3. Now change info objects. Indicator will have one input, three parameters and three outputs:

        indicatorInfo = new IndicatorInfo("THREEEMA", "Shows three different EMA indicators", "My indicators",
                true, false, true, 1, 3, 3);
        inputParameterInfos = new InputParameterInfo[] {
                new InputParameterInfo("Input data", InputParameterInfo.Type.DOUBLE)
        optInputParameterInfos = new OptInputParameterInfo[] {
                new OptInputParameterInfo("Time period EMA1", OptInputParameterInfo.Type.OTHER,
                                          new IntegerRangeDescription(5, 2, 1000, 1)),
                new OptInputParameterInfo("Time period EMA2", OptInputParameterInfo.Type.OTHER,
                                          new IntegerRangeDescription(10, 2, 1000, 1)),
                new OptInputParameterInfo("Time period EMA3", OptInputParameterInfo.Type.OTHER,
                                          new IntegerRangeDescription(20, 2, 1000, 1))};
        outputParameterInfos = new OutputParameterInfo[] {
                new OutputParameterInfo("EMA1", OutputParameterInfo.Type.DOUBLE,
                new OutputParameterInfo("EMA2", OutputParameterInfo.Type.DOUBLE,
                new OutputParameterInfo("EMA3", OutputParameterInfo.Type.DOUBLE,

    Also change holder fields for parameters and outputs. Add field to save ema indicator:

    private int[] timePeriod = new int[3];
    private double[][] outputs = new double[3][];
    private IIndicator ema;
  4. Lookback of the indicator should be biggest lookback of all three emas.

    public int getLookback() {
    ema.setOptInputParameter(0, timePeriod[0]);
    int ema1Lookback = ema.getLookback();
    ema.setOptInputParameter(0, timePeriod[1]);
    int ema2Lookback = ema.getLookback();
    ema.setOptInputParameter(0, timePeriod[2]);
    int ema3Lookback = ema.getLookback();
    return Math.max(ema1Lookback, Math.max(ema2Lookback, ema3Lookback));

    Also change method that saves time period parameters:

    timePeriod[index] = (Integer) value;
  5. And finally write the calclulate method: First get the correct startIndex taking into account lookback

    //calculating startIndex taking into account lookback value
    if (startIndex - getLookback() < 0) {
    startIndex -= startIndex - getLookback();

    Set parameters to ema and calculate it's values:

    ema.setInputParameter(0, inputs[0]);
    //calculate first ema
    ema.setOptInputParameter(0, timePeriod[0]);
    ema.setOutputParameter(0, outputs[0]);
    ema.calculate(startIndex, endIndex);
    //calculate second ema
    ema.setOptInputParameter(0, timePeriod[1]);
    ema.setOutputParameter(0, outputs[1]);
    ema.calculate(startIndex, endIndex);
    //calculate third ema
    ema.setOptInputParameter(0, timePeriod[2]);
    ema.setOutputParameter(0, outputs[2]);
    return ema.calculate(startIndex, endIndex);

IndicatorResult returned from ema is also correct for main indicator, so we just return it after the last calculation.

Indicator is ready!

Signal arrows over RSI

Consider an indicator which draws signal arrows:

  • Red, down arrow when RSI < 30.
  • Green, up arrow when RSI < 70.
 * Copyright 2018 Dukascopy® (Suisse) SA. All rights reserved.
 * DUKASCOPY PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
package jforex;

import java.awt.Color;
import com.dukascopy.api.indicators.IIndicator;
import com.dukascopy.api.indicators.IIndicatorContext;
import com.dukascopy.api.indicators.IIndicatorsProvider;
import com.dukascopy.api.indicators.IndicatorInfo;
import com.dukascopy.api.indicators.IndicatorResult;
import com.dukascopy.api.indicators.InputParameterInfo;
import com.dukascopy.api.indicators.IntegerRangeDescription;
import com.dukascopy.api.indicators.OptInputParameterInfo;
import com.dukascopy.api.indicators.OutputParameterInfo;

import static com.dukascopy.api.indicators.OutputParameterInfo.DrawingStyle.*;
import static com.dukascopy.api.indicators.OutputParameterInfo.Type.*;

public class RSISinglaArrows implements IIndicator {
    private IndicatorInfo indicatorInfo;
    private InputParameterInfo[] inputParameterInfos;
    private OptInputParameterInfo[] optInputParameterInfos;
    private OutputParameterInfo[] outputParameterInfos;
    //Price includes 5 arrays: open, close, high, low, volume
    private double[][][] inputsPriceArr = new double[1][][]; 
    //price array depending on AppliedPrice
    private double[][] inputsDouble = new double[1][]; 
    private double[][] outputs = new double[2][];

    IIndicator rsiIndicator;
    int rsiTimePeriod = 14;

    //output indices
    private static final int DOWN = 0;
    private static final int UP = 1;
    //input indices
    private static final int HIGH = 2;
    private static final int LOW = 3;

    public void onStart(IIndicatorContext context) {
        indicatorInfo = new IndicatorInfo("RSI_Signals", "RSI signals",
            "Custom indicators", true, false, false,
            2, 1, 2);
        inputParameterInfos = new InputParameterInfo[] { 
                new InputParameterInfo("Price arrays", InputParameterInfo.Type.PRICE),
                new InputParameterInfo("Price double", InputParameterInfo.Type.DOUBLE)
        optInputParameterInfos = new OptInputParameterInfo[] { new OptInputParameterInfo("Rsi time period",
                OptInputParameterInfo.Type.OTHER, new IntegerRangeDescription(rsiTimePeriod, 1, 200, 1))
        outputParameterInfos = new OutputParameterInfo[] {
                new OutputParameterInfo("Maximums", DOUBLE, ARROW_SYMBOL_DOWN) {{ setColor(Color.RED); }},
                new OutputParameterInfo("Minimums", DOUBLE, ARROW_SYMBOL_UP) {{ setColor(Color.GREEN); }} };

        IIndicatorsProvider indicatorsProvider = context.getIndicatorsProvider();
        rsiIndicator = indicatorsProvider.getIndicator("RSI");

    public IndicatorResult calculate(int startIndex, int endIndex) {
        if (startIndex - getLookback() < 0) {
            startIndex -= startIndex - getLookback();
        // calculating rsi
        double[] rsiOutput = new double[endIndex - startIndex + 1];
        rsiIndicator.setInputParameter(0, inputsDouble[0]);
        rsiIndicator.setOutputParameter(0, rsiOutput); 
        rsiIndicator.calculate(startIndex, endIndex);

        int i, j;
        for (i = startIndex, j = 0; i <= endIndex; i++, j++) {
            //place down signal on the high price of the corresponding bar
            outputs[DOWN][j] = rsiOutput[j] < 30 ? inputsPriceArr[0][HIGH][i] : Double.NaN;
            //place up signal on the low price of the corresponding bar
            outputs[UP][j] = rsiOutput[j] > 70 ? inputsPriceArr[0][LOW][i] : Double.NaN;

        return new IndicatorResult(startIndex, endIndex-startIndex + 1); 

    public IndicatorInfo getIndicatorInfo() {
        return indicatorInfo;

    public InputParameterInfo getInputParameterInfo(int index) {
        if (index <= inputParameterInfos.length) {
            return inputParameterInfos[index];
        return null;

    public int getLookback() {
        return rsiTimePeriod;

    public int getLookforward() {
        return 0;

    public OptInputParameterInfo getOptInputParameterInfo(int index) {
        if (index <= optInputParameterInfos.length) {
            return optInputParameterInfos[index];
        return null;

    public OutputParameterInfo getOutputParameterInfo(int index) {
        if (index <= outputParameterInfos.length) {
            return outputParameterInfos[index];
        return null;

    public void setInputParameter(int index, Object array) {
        if(index == 0)
            inputsPriceArr[0] = (double[][]) array;
        else if(index == 1)
            inputsDouble[0] = (double[]) array;

    public void setOptInputParameter(int index, Object value) {
        if (index == 0) {
            //set rsi time period
            rsiTimePeriod = (Integer) value;
            rsiIndicator.setOptInputParameter(0, (Integer) value);

    public void setOutputParameter(int index, Object array) {
        outputs[index] = (double[]) array;

The information on this web site is provided only as general information, which may be incomplete or outdated. Click here for full disclaimer.