Tutorial 3: writing a counter-trend strategy
We use the previous tutorial to build a more complex strategy.
What will you learn?
We define here a counter trend strategy, based on a channel like in the previous sample. You will learn how to use new indicators like the Simple Moving Average or the Average True Range, which is a measure of volatility and market noise.
What's the strategy logic?
We will enter in a long (short) position once we have broken a lower (upper) band of our channel. We define the channel as the moving average plus or minus a multiple of the Average True Range over the past n bars.
We only comment here the new trading behavior.
What's the code?
This sample illustrates more and more exit strategies. In particular, we exhibited how to avoid being exposed before the week-ends and we added another exit condition based on the crossing of a predefined moving average.
The parameters of this strategy are as follows :
int channelLag = 9;
double maxDurationBar = 8;
double atrMultiplier = 1;
double stopLossPct = 0.10;
double targetProfitPct = 0.10;
double qtyPct = 0.5;
boolean exitAtMAComeBack = false;
@Override
public void onStrategyStart() {
barCount = 0;
lowestLowSeries = newIndicator().timeSeries("lowest").draw(Color.BLUE).get();
highestHighSeries = newIndicator().timeSeries("highest").draw(Color.BLUE).get();
maSeries = newIndicator().sma().withLength(channelLag).draw(Color.ORANGE).get();
atr = newIndicator().atr().withLength(channelLag).get();
}
In the onOpen() callback, we try to be as modular as possible, adding calls to external functions like closePositionOnFriday(), closePositionIfMaximumDurationIsReached() and closePositionIfSMACrossed().
@Override
public void onOpen(OpenPrice openPrice) {
if (getBars().getCount() >= channelLag) {
if (!hasPosition()) {
if (highLevel < openPrice.getPrice()) {
sell(calculateQty(), "Entry");
} else if (lowLevel > openPrice.getPrice()) {
buy(calculateQty(), "Entry");
}
} else {
barCount++;
closePositionOnFriday(openPrice.getDate());
closePositionIfMaximumDurationIsReached();
closePositionIfSMACrossed(maSeries, getBars().ago(0));
}
double movingAverage = maSeries.getValue();
double atrValue = atr.getValue();
highLevel = movingAverage + (atrMultiplier * atrValue);
lowLevel = movingAverage - (atrMultiplier * atrValue);
highestHighSeries.add(openPrice.getDate(), highLevel);
lowestLowSeries.add(openPrice.getDate(), lowLevel);
}
}
We write our externals functions:
// Close position if we exceeded the trade's maximum duration
private void closePositionIfMaximumDurationIsReached() {
if (hasPosition() && (maxDurationBar > 0) && (barCount > maxDurationBar)) {
closePosition("Max Duration");
}
}
// Close position if we crossed back the moving average
private void closePositionIfSMACrossed(SimpleMovingAverage sma, Bar bar) {
if (exitAtMAComeBack) {
if (getPosition().isLong() && sma.crossesBelow(sma, bar)) {
closePosition("Exit SMA cross back");
} else if (getPosition().isShort() && sma.crossesAbove(sma, bar)) {
closePosition("Exit SMA cross back");
}
}
}
// Close position on friday
private void closePositionOnFriday(DateTime barDate) {
if (barDate.getDayOfWeek() == DateTimeConstants.FRIDAY) {
closePosition("Close before Weekend");
}
}
What does the result look like?
We execute this strategy on 2 futures, CAC_40 and CRUDE_OIL:
