Dissecting a JForex strategy -- MA_Play.java

Having studied the anatomy of an empty JForex strategy (Part 1 and Part 2), it's time to dissect a working one. MA_Play is the strategy that is included with every JForex API download as a demonstration. You can find the complete source code of this strategy in /src/singlejartest/ in the JForex API zipped package. Recall that the first Interface method which runs at the start of the strategy is onStart. The onStart method of MA_Play is reproduced below.

public void onStart(IContext context) throws JFException { 
    engine = context.getEngine(); 
    indicators = context.getIndicators();
    this.console = context.getConsole();
    console.getOut().println("Started"); 
}

The variables engine, indicators, and console are fields of the MA_Play class. They are global variables within the class. What lines 42--44 do is to save the IEngine, IIndicators, and IConsole objects for later use.

The last line of onStart, line 45, is merely to print a message on your JForex program console to notify the user that the strategy has started.

Once onStart is finished processing, the server is likely to call onTick if a market tick arrives. If it's not during market hours, then there's no tick and some other event might happen instead of onTick. Think of the methods as events rather than a linear process. You program your JForex strategy according to what you want to do with each of the six IStrategy Interface event.

For this particular strategy, the programmer decides to implement their strategy at the tick level. As such, much of the trading algorithm resides in onTick for MA_Play. Note that this is a design choice, you can use onBar if you want your strategy to process at the bar level (or you can use both onTick and onBar).

Here's the source code for onTick in MA_Play.

public void onTick(Instrument instrument, ITick tick) throws JFException {
    if (ma1[instrument.ordinal()] == -1) {
        ma1[instrument.ordinal()] = indicators.ema(instrument, 
            Period.TEN_SECS, 
            OfferSide.BID, 
            IIndicators.AppliedPrice.MEDIAN_PRICE, 
            14, 1);
    }
    double ma0 = indicators.ema(instrument, Period.TEN_SECS, OfferSide.BID, IIndicators.AppliedPrice.MEDIAN_PRICE, 14, 0);
    if (ma0 == 0 || ma1[instrument.ordinal()] == 0) {
        ma1[instrument.ordinal()] = ma0;
        return;
    }

    double diff = (ma1[instrument.ordinal()] - ma0) / (instrument.getPipValue());

    if (positionsTotal(instrument) == 0) {
        if (diff > 1) {
            engine.submitOrder(getLabel(instrument), instrument, IEngine.OrderCommand.SELL, 0.001, 0, 0, tick.getAsk()
                    + instrument.getPipValue() * 10, tick.getAsk() - instrument.getPipValue() * 15);
        }
        if (diff < -1) {
            engine.submitOrder(getLabel(instrument), instrument, IEngine.OrderCommand.BUY, 0.001, 0, 0, tick.getBid()
                    - instrument.getPipValue() * 10, tick.getBid() + instrument.getPipValue() * 15);
        }
    }
    ma1[instrument.ordinal()] = ma0;
}

At a glance, you may notice that the variables ma0 and ma1 play a key role in determining the setup. Hint: To reverse engineer a strategy, it may be easier to work backward from when the order is placed, which is done by engine.submitOrder in this case.

ma0 and ma1 hold results from exponential moving averages (EMA). ma0 is the current value. ma1 is the previous bar's value. Lines 56--63 check using IF tests (lines 56 and 60) to see if either of the variables hold invalid data. If the data is invalid, the indicator is calculated and the rest of the onTick is skipped with the return statement on line 62.

Note: Indicator values can sometimes be invalid (zero, negative, or Double.NaN, depending on the particular indicator implementation) if there is insufficient data to calculate it or an error occurred, for examples.

The EMAs are fetched in lines 57 and 59 using the IIndicators object (which was initialized in onStart). The JForex Wiki provides an explanation of its use.

Notice that ma1 is an array, which was declared in line 38 with a size equivalent to the number of all available JForex instruments. In particular, it is used with a special index value as in ma1[instrument.ordinal()]. In other words, it is asking for the current instrument's slot in the ma1 array. The current instrument is the one that is passed into the method in line 55.

Moving down the code, another point of interest is line 65, showing the use of instrument.getPipValue(). Line 67 checks if the current total number of position is zero. If it is, meaning no opened position, then the strategy proceeds to check the entry signal to enter a trade (lines 68--76).

positionsTotal() is a custom method defined in lines 84--92. It uses a FOR loop to cycle through all the orders obtained from [engine.getOrders(instrument)][]

Once either of the long or short condition, lines 68 and 72, respectively, is met, the strategy submits an order in lines 69 for a short and line 73 for a long. The particulars of submitting market orders is described in the JForex Wiki.

When you stop this strategy, onStop (lines 48--53) is called. For this strategy, the programmer loops through all the orders again using engine.getOrders() and closes each of the position with an [order.close()][] command in line 50.

That is it for this trivial strategy. If there is one point that you should remember. Note my use of the many links to the JForex javadoc and JForex Wiki throughout this post. You are likely to find many of your answers from those two sources. If not, there's always the JForex Support Board. Now that you've had an idea of how MA_Play.java works, it's time to test it. In the next post in January, we will discuss the JForex Historical Tester and what to watch for when running a strategy live.