Indicator with multiple inputs


Indicators always have at least one input (main input). Main input's instrument, period and offer side are chart's instrument, period and offer side. Even if the main input wasn't defined in the indicator, it still exists. Indicator's calculate method receives indexes for this main input. (See IIndicator interface section for more details) When custom inputs are used in the indicator, the following rules apply:

  1. Chart data is loaded from the disk or internet and is ready to be displayed on the chart. Data for indicator's custom inputs may not be fully loaded yet.
  2. As soon as main input's data is fully loaded it is passed to the indicator.
  3. When data for other inputs is fully loaded it is passed to the indicator which then gets recalculated.

The following example demonstrates how multiple inputs may be defined and used in a single indicator.


public class MultiInputIndicator implements IIndicator {
    private IndicatorInfo indicatorInfo;
    private InputParameterInfo[] inputParameterInfos;
    private OptInputParameterInfo[] optInputParameterInfos;
    private OutputParameterInfo[] outputParameterInfos;
    //define three inputs
    private IBar[][] inputs = new IBar[3][];    
    //and one output
    private double[][] outputs = new double[1][];   

    public void onStart(IIndicatorContext context) {        
        indicatorInfo = new IndicatorInfo("EXAMPIND", "Sums previous values", "My indicators",
                                          false, false, false,
                                          3, 0, 1);
        inputParameterInfos = new InputParameterInfo[] {
                new InputParameterInfo("Main", InputParameterInfo.Type.BAR){{                   
                new InputParameterInfo("ASK", InputParameterInfo.Type.BAR){{
                new InputParameterInfo("BID", InputParameterInfo.Type.BAR){{
        optInputParameterInfos = new OptInputParameterInfo[] {
                new OptInputParameterInfo("Time period", OptInputParameterInfo.Type.OTHER,
                    new IntegerRangeDescription(2, 2, 100, 1))};
        outputParameterInfos = new OutputParameterInfo[] {
                new OutputParameterInfo("out", OutputParameterInfo.Type.DOUBLE,

Method Calculate

    public IndicatorResult calculate(int startIndex, int endIndex) {
        if (startIndex - getLookback() < 0) {
            startIndex -= startIndex - getLookback();
        int i, j;
        for (i = startIndex, j = 0; i <= endIndex; i++, j++) {
            int timeIndex = getTimeIndex(inputs[0][i].getTime(), inputs[1]);            
            int timeIndex2= getTimeIndex(inputs[0][i].getTime(), inputs[2]);
            outputs[0][j] = (timeIndex == -1 || timeIndex2 == -1)? 
                    Double.NaN : ((IBar)inputs[2][timeIndex2]).getHigh() + ((IBar)inputs[1][timeIndex]).getHigh();
        return new IndicatorResult(startIndex, j);

Method getTimeIndex

Method getTimeIndex looks for custom input candle with the same time as the main input's candle and returns -1 when unsuccessful

private int getTimeIndex(long time, IBar[] target) {
        if (target == null) {
                return -1;

        int first = 0;
        int upto = target.length;

        while (first < upto) {
            int mid = (first + upto) / 2;

            IBar data = target[mid];

            if (data.getTime() == time) {
                return mid;
            else if (time < data.getTime()) {
                upto = mid;
            else if (time > data.getTime()) {
                first = mid + 1;
        return -1;


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