Auf GitHub ansehen

Donchian Scalper

Overview

Donchian Scalper is a StockSharp port of the MetaTrader 4 expert advisor DonchianScalperEA. The strategy monitors Donchian channel boundaries and the exponential moving average (EMA) of the same length. A pending stop order is armed only after price pulls back through the EMA, signalling that momentum has reset before a potential breakout. Entries are executed with stop orders placed at the current Donchian extremes and protected by the opposite band. Profits are managed either by a fixed take-profit distance or by adaptive trailing stops that track the chosen market structure.

Strategy logic

Entry preparation

  • Pullback validation – the strategy waits until one of the two previously closed candles crosses below the EMA (for longs) or above the EMA (for shorts). The crossing level is offset by the configurable Cross Anchor distance to ensure the pullback is meaningful.
  • Breakout arming – once the pullback condition is satisfied and the cooldown timer has expired, a stop order is submitted at the most recent Donchian boundary (upper band for longs, lower band for shorts). The opposite band defines the initial protective stop. Existing pending orders are automatically realigned when the Donchian levels flatten for at least two candles.

Trade management

  • Initial protection – when a breakout order fills, the strategy places a protective stop order using the precomputed stop price. The stop level equals the opposite Donchian band and can be shifted inward by the Stop Loss (points) setting.
  • Profit control – two management modes are available:
    • Close At Profit – closes the position once the net movement from the average entry price exceeds the configured take-profit distance.
    • Trailing – keeps the trade open and periodically tightens the protective stop. The trailing engine can follow the Donchian boundary, the EMA, or an ATR-based volatility band.
  • Cooldown – after all positions are closed, the strategy waits for the specified number of finished candles before arming new breakout orders. This reproduces the MetaTrader logic that requires at least three bars between trades.

Parameters

  • Volume – order volume used for stop entries and market exits.
  • Channel Period – Donchian channel length, also used for the EMA filter.
  • Cross Anchor – additional distance (in points) that the pullback must exceed before the breakout order is armed.
  • Stop Loss (points) – distance added to the opposite Donchian band for the initial protective stop; set to 0 to place the stop directly on the band.
  • Take Profit (points) – profit target used by the Close At Profit mode. Ignored when the trailing mode is active.
  • Candle Type – timeframe driving indicator calculations.
  • Profit Mode – selects between the fixed take-profit exit and adaptive trailing stops.
  • Trailing Mode – trailing engine used in the Trailing profit mode. Choices are Donchian boundary, EMA, or ATR-based trailing.
  • Cooldown Bars – minimum number of finished candles that must pass after the position becomes flat before new orders may be placed.
  • ATR Period / ATR Multiplier – parameters for the ATR trailing engine. The multiplier defines how many ATRs are subtracted (long) or added (short) to compute the trailing stop.

Additional notes

  • The strategy aligns every stop and entry price to the instrument’s price step to ensure exchange compliance.
  • When both long and short stop orders are active, filling one side will automatically cancel the opposite pending order to avoid hedging.
  • If Take Profit (points) is set to zero while the profit mode remains Close At Profit, the strategy will keep positions open until the protective stop is hit.
  • The conversion focuses on the high-level StockSharp API: indicator binding, candle subscriptions, and helper methods (BuyStop, SellStop, SellMarket, etc.). Python implementation is not included in this package.
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Donchian Channel scalper with EMA filter.
/// Buys on upper channel breakout above EMA, sells on lower breakout below EMA.
/// Exits at middle band.
/// </summary>
public class DonchianScalperStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<DataType> _candleType;

	public int ChannelPeriod
	{
		get => _channelPeriod.Value;
		set => _channelPeriod.Value = value;
	}

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

	public DonchianScalperStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Channel Period", "Donchian channel lookback", "Indicators");

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

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

		var donchian = new DonchianChannels { Length = ChannelPeriod };
		var ema = new ExponentialMovingAverage { Length = ChannelPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(donchian, ema, (candle, donchianVal, emaVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (donchianVal is not DonchianChannelsValue dcValue)
					return;

				if (dcValue.UpperBand is not decimal upper ||
					dcValue.LowerBand is not decimal lower ||
					dcValue.Middle is not decimal middle)
					return;

				if (emaVal.IsEmpty)
					return;

				var emaValue = emaVal.GetValue<decimal>();
				var close = candle.ClosePrice;

				// Long: close breaks above upper Donchian and is above EMA
				if (Position == 0 && close >= upper && close > emaValue)
					BuyMarket();
				// Short: close breaks below lower Donchian and is below EMA
				else if (Position == 0 && close <= lower && close < emaValue)
					SellMarket();
			})
			.Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);

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