Use Custom Indicator in Strategies

Custom indicators can be used from a strategy just like any other standard JForex platform indicator. In order to use these custom indicators in calculations they must be registered first. There are two ways how one can register a custom indicator within a strategy:

  • register the indicator from a compiled .jfx file;
  • register the indicator from a class which is located within the strategy's java file.

For the former apporach one can also elect to package the indicators in the compiled strategy file, see below the first example for this. As an example of application of both approaches see onStart method of Simple custom indicator.

The following examples demonstrate ways how one can register custom indicators, handle their outputs and plot them on chart.

Package custom indicator in strategy jfx file

In order to package the indicators inside the compiled strategy one has to use the CustomIndicators annotation. This approach allows the user to run the strategy on other workstations without having to copy along the indicator jfx files.

Note: CustomIndicators available with JForex-API IIndicators.getIndicatorByPath available with JForex-API 2.9.1.

package jforex;

import java.util.Arrays;

import com.dukascopy.api.*;
import static com.dukascopy.api.IIndicators.AppliedPrice.*;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.feed.IFeedDescriptor;
import com.dukascopy.api.feed.util.TimePeriodAggregationFeedDescriptor;
import com.dukascopy.api.indicators.IIndicator;

//pass file name if the indicator is located in context.getFilesDir, otherwise - full file path
public class CutomIndicatorPackageInJfx implements IStrategy {

    private IConsole console;
    private IIndicators indicators;
    private IHistory history;

    public void onStart(IContext context) throws JFException {
        console = context.getConsole();
        indicators = context.getIndicators();
        history = context.getHistory();
        String indPath = getClass().getAnnotation(CustomIndicators.class).value(); 
            //for multiple indicators split the path with File.pathSeparator 
        IIndicator indicator = indicators.getIndicatorByPath(indPath);
        if(indicator == null){
            console.getErr().println("Indicator by path "+indPath+" not registered!");
        String indName = indicator.getIndicatorInfo().getName();
        //add indicator to the last active chart if there is one
        IChart chart = context.getLastActiveChart();
        if(chart != null){
        //take feed from chart if there is one
        IFeedDescriptor feedDescriptor = chart!= null 
                ? chart.getFeedDescriptor()
                : new TimePeriodAggregationFeedDescriptor(
                        Instrument.EURUSD, Period.TEN_SECS, OfferSide.BID, Filter.NO_FILTER);

        //use default value - just like the chart does
        int timePeriod = (Integer) indicator

        //calculate with shift 1
        Object[] resultByShift =  indicators.calculateIndicator(
                feedDescriptor, new OfferSide[] {OfferSide.BID}, indName, new AppliedPrice[]{CLOSE}, 
                new Object[]{timePeriod}, 1);
        double prevValue = (Double) resultByShift[0];

       //calculate last 5
        long lastTime = history.getFeedData(feedDescriptor, 0).getTime();
        Object[] resultByCandleInterval =  indicators.calculateIndicator(
                feedDescriptor, new OfferSide[] {OfferSide.BID}, indName, new AppliedPrice[]{CLOSE}, 
                new Object[]{timePeriod}, 5, lastTime, 0);
        double[] values = (double[]) resultByCandleInterval[0];

        console.getOut().format("previous=%.7f last 5=%s on feed=%s", 
            prevValue, Arrays.toString(values), feedDescriptor).println();

    public void onTick(Instrument instrument, ITick tick) throws JFException {}
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {}
    public void onMessage(IMessage message) throws JFException {}
    public void onAccount(IAccount account) throws JFException {}
    public void onStop() throws JFException {}

Simple custom indicator

The following strategy registers two identical indicators, calculates them and prints out the result. See Indicators section for more details on IIndicators.calculateIndicator method.

    public void onStart(IContext context) throws JFException {
        this.console = context.getConsole();
        this.indicators = context.getIndicators();

        //register custom indicator located in ...\JForex\Strategies\files folder
                new File(context.getFilesDir() + System.getProperty("file.separator") + "Indicator.jfx"));
        //register custom indicator defined inside a strategy

        Object[] firstIndicatorValues =  indicators.calculateIndicator(
                Instrument.EURUSD, Period.ONE_HOUR, new  OfferSide[] {OfferSide.BID}, "EXAMPIND",
                new AppliedPrice[]{AppliedPrice.CLOSE}, new Object[]{4}, 0);
        console.getOut().println("first indicator value: " + ((Object[])firstIndicatorValues)[0]);

        Object[] secondIndicatorValues =  indicators.calculateIndicator(
                Instrument.EURUSD, Period.ONE_HOUR, new  OfferSide[] {OfferSide.BID}, "INNERINDICATOR",
                new AppliedPrice[]{AppliedPrice.CLOSE}, new Object[]{4}, 0);
        console.getOut().println("second indicator value: " + ((Object[])secondIndicatorValues)[0]);
    public static class Indicator implements IIndicator {
        private IndicatorInfo indicatorInfo = 
            new IndicatorInfo("INNERINDICATOR", "Sums previous values", "My indicators",
                              false, false, false,
                              1, 1, 1);
        private InputParameterInfo[] inputParameterInfos = 
            new InputParameterInfo[] { new InputParameterInfo("Input data", InputParameterInfo.Type.DOUBLE)};
        private OptInputParameterInfo[] optInputParameterInfos = 
            new OptInputParameterInfo[] {
                new OptInputParameterInfo("Time period", OptInputParameterInfo.Type.OTHER,
                      new IntegerRangeDescription(2, 2, 100, 1))};
        private OutputParameterInfo[] outputParameterInfos = new OutputParameterInfo[] {
                new OutputParameterInfo("out",
        private double[][] inputs = new double[1][];
        private int timePeriod = 2;
        private double[][] outputs = new double[1][];

        public void onStart(IIndicatorContext context) { }

        public IndicatorResult calculate(int startIndex, int endIndex) {
            //calculating startIndex taking into account lookback value
            if (startIndex - getLookback() < 0) {
                startIndex -= startIndex - getLookback();
            int i, j;
            for (i = startIndex, j = 0; i <= endIndex; i++, j++) {
                double value = 0;
                for (int k = timePeriod; k > 0; k--) {
                    value += inputs[0][i - k];
                outputs[0][j] = value;
            return new IndicatorResult(startIndex, j);

        public IndicatorInfo getIndicatorInfo() {
            return indicatorInfo;

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

        public int getLookback() {
            return timePeriod;

        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) {
            inputs[index] = (double[]) array;

        public void setOptInputParameter(int index, Object value) {
            timePeriod = (Integer) value;

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

Handle multiple outputs and plot on chart

Consider a strategy which works with a custom, multi-output indicator and shows how to go about:

  • parsing and logging custom indicator's outputs,
  • plotting custom indicator on the chart.

There are two calculation variants used:

  • by shift (single value),
  • by candle interval (value array).

Before launching the strategy, compile the AwesomeOscillator indicator and then in parameters dialog select the compiled .jfx file's location.

Trade with TradersDynamicIndex

The strategy trades according to TradersDynamicIndex custom indicator. It makes:

  • a BUY order if green ("RSI Price Line") crosses above red ("Trade Signal Line")
  • a SELL order - if green crosses below red.

On its start the strategy plots the indicator on the chart. The strategy works only in determined market hours. The strategy applies take profit and stop loss prices and on new order creation, it closes the previous one.

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