View on GitHub

Explosion Strategy

The strategy reproduces the logic of the MetaTrader "Explosion" expert. It watches the range of each finished candle and enters the market when the latest bar "explodes" – its height more than doubles the range of the previous bar. The direction is decided by the candle body: a bullish body opens a long position, while a bearish body opens a short one.

Trading rules

  1. Process only finished candles coming from the configured CandleType subscription.
  2. Compute the current range as High - Low and compare it with the range of the previous candle.
  3. A long entry is opened when currentRange > previousRange * 2 and the close is above the open.
  4. A short entry is opened when currentRange > previousRange * 2 and the close is below the open.
  5. When OnlyOnePositionPerBar is enabled, at most one trade per direction is allowed for a candle open time. Attempts on the same bar are ignored.
  6. The strategy keeps a single net position, therefore an opposite trade automatically closes any existing exposure before establishing the new direction.
  7. Protective mechanics:
    • StopLossPips and TakeProfitPips place virtual exit levels measured in pips from the entry price.
    • TrailingStopPips and TrailingStepPips move the stop once price travels in favour of the position by at least the trailing distance and every additional step.
    • The optional pip multiplier emulates the MQL auto-digits helper by multiplying the pip size by 10 on 3- and 5-digit instruments.

Parameters

Parameter Default Description
TradeVolume 0.01 Market order volume used on entries.
StopLossPips 20 Stop-loss distance in pips. Zero disables the stop.
TakeProfitPips 10 Take-profit distance in pips. Zero disables the take.
TrailingStopPips 25 Activation distance for the trailing stop in pips. Zero disables trailing.
TrailingStepPips 5 Additional move in pips required before the trailing stop advances. Must stay positive when trailing is enabled.
UseAutoPipMultiplier true Multiply the pip size by 10 on instruments with 3 or 5 decimal places, matching the MQL auto-digits helper.
OnlyOnePositionPerBar true Forbid more than one entry per bar open time.
CandleType TimeSpan.FromMinutes(1).TimeFrame() Candle series used for calculations.

Notes on the conversion

  • StockSharp works with a net position, so hedging multiple simultaneous orders of the original Expert Advisor is not supported. The behaviour is equivalent to enabling OnlyOneOpenedPos in the MQL version.
  • Trailing stop updates are performed on candle closes instead of every tick. The logic matches the original thresholds while remaining compatible with the high-level API.
  • The pip multiplier reproduces the automatic digit detection that scales distances by 10 on 5-digit forex symbols.

Suggested usage

  1. Choose the instrument and timeframe that match the original expert (e.g., the recommended M15/M30 charts for forex pairs).
  2. Adjust the pip-based risk parameters to the instrument's volatility.
  3. Enable logging to monitor when the trailing stop advances and how protective levels are recalculated.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class ExplosionStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ExplosionStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}