View on GitHub

Billy Expert Strategy

Overview

  • Converted from the original MetaTrader 4 expert "Billy_expert.mq4".
  • Long-only momentum strategy that waits for four consecutive descending highs and opens before entering.
  • Uses two stochastic oscillators (fast on the trading timeframe, slow on a higher timeframe) to confirm that momentum is shifting upward.
  • Designed for spot FX pairs but can be applied to any instrument that provides minute-based candles.

Signal logic

Price action filter

  1. Evaluate finished candles on the primary timeframe.
  2. Require four consecutive candles where both the high and the open decrease. This recreates the MT4 High[0] < High[1] < High[2] < High[3] and Open[0] < Open[1] < Open[2] < Open[3] checks.
  3. The pattern suggests an exhausted bearish move and prepares the strategy for a reversal trade.

Oscillator confirmation

  1. Calculate a fast stochastic oscillator on the trading timeframe and a slow stochastic on the confirmation timeframe.
  2. For each oscillator, demand that the %K line be above the %D line on both the current and previous completed candle (%K(0) > %D(0) and %K(1) > %D(1)).
  3. The trade is triggered only when both oscillators simultaneously confirm bullish momentum.

Order management

  • Entries: market buys sized by the strategy Volume parameter (if a short position exists it is closed and reversed automatically).
  • Stop loss: fixed distance below the fill price using the Stop Loss (pts) parameter. A value of 0 disables the stop.
  • Take profit: fixed distance above the fill price using the Take Profit (pts) parameter. A value of 0 disables the target.
  • Position cap: Max Orders limits how many long entries can be active at the same time. Because StockSharp keeps a net position, the strategy approximates the MT4 behaviour by counting how many Volume blocks are currently open.
  • Trailing stop: the original EA declared a trailing stop input but did not implement it. The converted version also omits trailing logic for parity.

Parameters

Name Description Default
Trading Candle Primary timeframe for price pattern and fast stochastic. 1 minute
Slow Stochastic Candle Higher timeframe used for the confirmation stochastic. 5 minutes
Stochastic Length Lookback window for %K. 5
%K Smoothing Smoothing applied to the %K line. 3
%D Period Smoothing applied to the %D line. 3
Slowing Additional smoothing factor for %K. 3
Stop Loss (pts) Stop loss distance in price steps. 0
Take Profit (pts) Take profit distance in price steps. 12
Max Orders Maximum simultaneous long entries. 1

Usage notes

  • Set the Volume property before starting the strategy; StockSharp defaults to 0, which would block order placement.
  • The price step is read from Security.PriceStep (falls back to Security.Step or 1). Ensure your instrument metadata is configured correctly to get precise stop/target levels.
  • When the confirmation timeframe differs from the trading timeframe, the most recent completed slow candle is reused until a new one appears, matching the behaviour of the original MT4 script.
  • The EA did not manage exits beyond broker-side stop loss and take profit. The conversion mirrors this behaviour by sending protective market orders when the levels are touched.
  • Because StockSharp aggregates positions, Max Orders > 1 works best when each entry uses the same Volume size.

Differences from the MT4 version

  • Safety check for missing price step information with a log warning instead of silently using Point.
  • Added guard clauses to ensure the strategy trades only when all required data (price history and both stochastic oscillators) is available.
  • The strategy runs on finished candles only, while MT4 processed ticks but throttled by bar time. This change avoids duplicate evaluations and keeps the logic deterministic.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Long-only reversal strategy that looks for consecutive descending highs
/// and enters when RSI confirms oversold conditions with momentum turning up.
/// </summary>
public class BillyExpertReversalStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;

	private decimal _prevHigh1, _prevHigh2, _prevHigh3;
	private int _barCount;
	private decimal _prevRsi;
	private bool _hasPrevRsi;
	private decimal _entryPrice;

	public BillyExpertReversalStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "Length for RSI indicator.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh1 = 0;
		_prevHigh2 = 0;
		_prevHigh3 = 0;
		_barCount = 0;
		_prevRsi = 50;
		_hasPrevRsi = false;
		_entryPrice = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevHigh1 = 0;
		_prevHigh2 = 0;
		_prevHigh3 = 0;
		_barCount = 0;
		_prevRsi = 50;
		_hasPrevRsi = false;
		_entryPrice = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, rsi);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_barCount++;

		var high = candle.HighPrice;
		var close = candle.ClosePrice;

		// Check descending highs pattern (3 consecutive lower highs)
		var descendingHighs = _barCount >= 4 &&
			high < _prevHigh1 &&
			_prevHigh1 < _prevHigh2 &&
			_prevHigh2 < _prevHigh3;

		// RSI turning up from oversold
		var rsiBullish = _hasPrevRsi && _prevRsi < 40 && rsiValue > _prevRsi;

		// Manage long position
		if (Position > 0)
		{
			// Exit on take-profit, stop-loss, or RSI overbought
			if (_entryPrice > 0 && close >= _entryPrice * 1.015m)
			{
				SellMarket();
			}
			else if (_entryPrice > 0 && close <= _entryPrice * 0.985m)
			{
				SellMarket();
			}
			else if (rsiValue > 75)
			{
				SellMarket();
			}
		}

		// Manage short position (exit only, this is mostly long-only)
		if (Position < 0)
		{
			if (rsiValue < 30)
			{
				BuyMarket();
			}
		}

		// Entry: descending highs (selling exhaustion) + RSI confirms reversal
		if (Position == 0)
		{
			if (descendingHighs && rsiBullish)
			{
				_entryPrice = close;
				BuyMarket();
			}
			// Also allow short on ascending lows pattern with overbought RSI
			else if (_barCount >= 4 && rsiValue > 70 && _prevRsi > 70)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		// Update history
		_prevHigh3 = _prevHigh2;
		_prevHigh2 = _prevHigh1;
		_prevHigh1 = high;
		_prevRsi = rsiValue;
		_hasPrevRsi = true;
	}
}