在 GitHub 上查看

Donchian Scalper

概述

Donchian Scalper 是 MetaTrader 4 专家顾问 DonchianScalperEA 的 StockSharp 版本。策略跟踪与指数移动平均线(EMA)具有相同期数的 Donchian 通道边界。只有当价格穿越 EMA 形成回调后,才会准备突破方向的挂单。挂单放置在当前 Donchian 极值位置,并以对侧通道作为初始保护。盈利可以通过固定止盈距离或自适应的追踪止损来管理。

策略逻辑

入场准备

  • 回调确认 – 仅当最近两根已完成 K 线中的任意一根向下穿越 EMA(做多)或向上穿越 EMA(做空)时才会满足条件。穿越水平会根据可配置的“回调锚点”距离进行偏移,以确保回调足够明显。
  • 突破挂单 – 当回调条件满足且冷却计时结束后,在最近的 Donchian 边界提交止损单:上轨用于多头,下轨用于空头。对侧通道定义初始保护止损。当 Donchian 水平在至少两根 K 线上保持不变时,现有挂单会自动重新对齐。

交易管理

  • 初始保护 – 当突破挂单成交时,策略会根据预先计算的价格放置保护性止损单。止损位等于对侧 Donchian 边界,可通过“止损(点数)”参数向通道内部偏移。
  • 盈利控制 – 提供两种管理模式:
    • 固定止盈 – 当价格相对平均开仓价的净移动超过设定的止盈距离时平仓。
    • 追踪止损 – 保持持仓并定期收紧保护性止损。追踪引擎可以跟随 Donchian 边界、EMA 或基于 ATR 的波动带。
  • 冷却时间 – 当仓位全部关闭后,策略会等待设定数量的已完成 K 线后才会再次挂出突破单,复现原始 EA 至少等待三根 K 线的逻辑。

参数

  • Volume – 用于挂单和市价平仓的交易量。
  • Channel Period – Donchian 通道长度,同时也用于 EMA 过滤。
  • Cross Anchor – 回调所需的额外距离(以点数计)。
  • Stop Loss (points) – 在计算初始保护止损时添加到对侧 Donchian 通道上的距离,设为 0 表示止损直接放在通道上。
  • Take Profit (points) – 固定止盈模式下使用的目标距离,启用追踪模式时忽略该值。
  • Candle Type – 指标计算所使用的时间框架。
  • Profit Mode – 选择固定止盈或追踪止损的盈利管理方式。
  • Trailing Mode – 在追踪模式下使用的追踪引擎,可选 Donchian 边界、EMA 或 ATR 波动追踪。
  • Cooldown Bars – 平仓后需要等待的已完成 K 线数量,之后才能重新挂出突破单。
  • ATR Period / ATR Multiplier – ATR 追踪引擎的周期和乘数,乘数决定追踪止损与当前价格相差多少个 ATR。

其他说明

  • 所有挂单和止损价格都会对齐到交易所最小价格跳动,以满足撮合要求。
  • 当多空突破单同时存在时,一方成交会自动取消另一方挂单,避免形成对冲。
  • 如果在固定止盈模式下将 Take Profit (points) 设为零,仓位将一直持有,直到保护性止损被触发。
  • 本移植依赖 StockSharp 的高级 API:指标绑定、蜡烛订阅以及 BuyStopSellStopSellMarket 等辅助方法。本包未包含 Python 实现。
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);
		}
	}
}