Ver no GitHub

Divergence Trader Basket Strategy

Overview

This strategy is a StockSharp port of the "Divergence Trader" MetaTrader expert advisor. It compares two simple moving averages calculated on configurable price sources and measures their difference (divergence). When the distance between the fast and slow averages falls inside a neutral corridor, the algorithm assumes that momentum is about to resume and opens a position in the direction of the prevailing bias. The implementation uses only completed candles from a selected timeframe and relies on the high-level API with indicator bindings.

Parameters

  • Lot Size – trading volume submitted with each new position. The value is aligned with the instrument volume step.
  • Fast SMA Period / Price – length and price source for the fast moving average.
  • Slow SMA Period / Price – length and price source for the slow moving average.
  • Buy Threshold – minimal positive divergence required before opening a long position.
  • Stay-Out Threshold – maximal divergence allowed for new entries; values outside this range disable trading.
  • Take Profit (pips) – profit target expressed in pips. Disabled when set to zero.
  • Stop Loss (pips) – loss tolerance in pips. Disabled when set to zero.
  • Trailing Stop (pips) – trailing distance activated after the trade becomes profitable. Disabled when zero.
  • Break-Even Trigger / Buffer (pips) – pip gain required before protecting the position at break-even and optional buffer to offset the break-even stop from the entry price.
  • Basket Profit / Basket Loss – account-equity based thresholds that flatten all positions when reached. Loss control is disabled by default.
  • Start Hour / Stop Hour – trading window in local time. When both values are equal the strategy operates all day.
  • Candle Type – timeframe used for both signal generation and risk management.

Trading Logic

  1. Subscribe to the configured candle series and calculate the fast and slow simple moving averages.
  2. Work only with finished candles to avoid intrabar noise and to stay close to the original EA behaviour.
  3. Track the divergence (fast minus slow) computed on the previously finished candle:
    • If the divergence is positive and remains between the Buy Threshold and Stay-Out Threshold, submit a market buy order.
    • If the divergence is negative and its absolute value stays inside the corridor, submit a market sell order.
  4. Trades are ignored outside the allowed hours or when the strategy already has an open position.

Position Management

  • Break-even control – when the floating profit reaches the trigger, the strategy stores a break-even stop level (optionally shifted by the buffer). A candle that touches this level closes the position.
  • Trailing stop – once profit exceeds the trailing distance, the stop level follows the most favourable price while always staying behind it by the configured number of pips.
  • Take profit / stop loss – fixed exits calculated from the entry price in pip units.
  • Basket protection – portfolio equity is compared against the configured profit and loss limits. Hitting either boundary closes the current position and cancels active orders, emulating the "CloseEverything" routine from the MQL version.

Usage Notes

  • The divergence corridor is symmetric: widening the Stay-Out Threshold allows trades to stay open longer, while narrowing it increases the frequency of signals.
  • Price source options correspond to StockSharp CandlePrice values, making it possible to use open, close, median or typical prices just like in MetaTrader.
  • The strategy plots candles, both moving averages and filled orders on a chart area for monitoring and debugging.
  • Money-management features depend on portfolio data. When running in a sandbox without portfolio statistics, basket controls are ignored automatically.
using System;
using System.Linq;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Divergence-based strategy converted from the Divergence Trader MQL expert advisor.
/// Trades based on the divergence between fast and slow moving averages.
/// </summary>
public class DivergenceTraderBasketStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<decimal> _buyThreshold;
	private readonly StrategyParam<decimal> _stayOutThreshold;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _previousDifference;
	private decimal _entryPrice;

	public DivergenceTraderBasketStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 7)
			.SetDisplay("Fast SMA Period", "Length of the fast simple moving average.", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 88)
			.SetDisplay("Slow SMA Period", "Length of the slow simple moving average.", "Indicators");

		_buyThreshold = Param(nameof(BuyThreshold), 0.0001m)
			.SetDisplay("Buy Threshold", "Minimum divergence value required before buying.", "Signals");

		_stayOutThreshold = Param(nameof(StayOutThreshold), 1000m)
			.SetDisplay("Stay-Out Threshold", "Upper divergence limit that disables new entries.", "Signals");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe used for calculations.", "General");
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public decimal BuyThreshold
	{
		get => _buyThreshold.Value;
		set => _buyThreshold.Value = value;
	}

	public decimal StayOutThreshold
	{
		get => _stayOutThreshold.Value;
		set => _stayOutThreshold.Value = value;
	}

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

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_previousDifference = null;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_previousDifference = null;
		_entryPrice = 0;

		var fastMa = new SimpleMovingAverage { Length = FastPeriod };
		var slowMa = new SimpleMovingAverage { Length = SlowPeriod };

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

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

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

		var currentDiff = fastValue - slowValue;

		if (_previousDifference == null)
		{
			_previousDifference = currentDiff;
			return;
		}

		var prevDiff = _previousDifference.Value;
		_previousDifference = currentDiff;

		// Manage open position
		if (Position != 0)
		{
			// Exit on divergence sign change
			if (Position > 0 && currentDiff < 0)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (Position < 0 && currentDiff > 0)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			return;
		}

		// Entry logic: divergence crosses zero line
		if (currentDiff > 0 && prevDiff <= 0)
		{
			// Bullish divergence crossover
			BuyMarket();
			_entryPrice = candle.ClosePrice;
		}
		else if (currentDiff < 0 && prevDiff >= 0)
		{
			// Bearish divergence crossover
			SellMarket();
			_entryPrice = candle.ClosePrice;
		}
	}
}