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:

screenshot-Tutorial03-equityCurve.png