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.