在 GitHub 上查看

Daydream Channel Breakout

Daydream Channel Breakout 是 MetaTrader 平台上 Daydream EA 的 StockSharp 高级 API 版本。策略属于逆势思路:当价格跌破前一周期的唐奇安通道下轨时买入,突破上轨时做空,并且通过以点数为单位的“虚拟”止盈来平仓,因此不会在交易所保留挂单。

策略原理

  • 使用已经收盘的 ChannelPeriod 根 K 线构建唐奇安通道,当前 K 线不参与计算(完全复刻 MT5 的 shift=1 逻辑)。
  • 做多:收盘价低于上一根通道下轨。下单数量为 OrderVolume + Math.Max(0, -Position),因此会自动平掉现有的空头再开多单。
  • 做空:收盘价高于上一根通道上轨。通过 OrderVolume + Math.Max(0, Position) 先平多再开空。
  • 每根 K 线最多触发一次信号。下单后必须等待下一根 K 线才会再次检测。
  • 对持仓实时计算虚拟止盈。当浮动盈利超过 TakeProfitPips(按品种的点值换算成价格距离)时,以市价单平仓。

参数说明

参数 说明 默认值 备注
OrderVolume 每次新进场使用的手数。若存在反向仓位会自动加上其绝对值。 0.1 与 MT5 设置一致。
TakeProfitPips 虚拟止盈点数。 50 点值通过 Security.PriceStep 计算,3/5 位报价会额外乘以 10。
ChannelPeriod 唐奇安通道回溯长度。 25 完全继承原始 EA。
CandleType 计算所用 K 线类型。 TimeSpan.FromHours(1).TimeFrame() 可替换为任意 StockSharp 蜡烛类型。

信号流程

  1. 订阅数据:按照 CandleType 创建 K 线订阅,并使用 BindEx 绑定 DonchianChannels 指标。
  2. 止盈检查:每根完成的蜡烛首先检查浮动收益是否达到虚拟止盈,若满足则平仓并终止本根 K 线的后续判断。
  3. 通道缓存:保存上一根蜡烛的上下轨,保证信号始终基于完成周期的通道值。
  4. 进场规则
    • 收盘价低于前一周期下轨 → 反向加仓买入。
    • 收盘价高于前一周期上轨 → 反向加仓卖出。
  5. 日志与图表:所有进出场都会记录详细日志;在 Designer、Shell 等工具中还会绘制蜡烛、通道以及成交轨迹。

风险控制

  • 只有虚拟止盈,没有内置止损或追踪保护,建议结合账户层面的风控或外部保护机制。
  • 由于下单会把现有仓位的绝对值加到订单中,如果连续多根 K 线给出同向信号,策略可能继续在同一方向加仓。
  • 点值计算中,如果品种的最小价位变动对应 3 或 5 位报价,会乘以 10 模拟 MT5 中 Point() 到 pip 的转换。对于其他品种,可调整参数来获得合适的距离。

使用建议

  • 更适合震荡或均值回归型市场环境,在趋势行情中可能需要额外的过滤条件。
  • 回测或实盘时请考虑真实的点差和手续费,策略使用市价单入场。
  • 可以结合交易时段过滤、波动率阈值或组合级风险管理一起使用。
  • 代码完全基于 StockSharp 高级 API 实现,能够直接在 Designer、Shell、Runner 等工具中运行。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Daydream Channel Breakout strategy. Uses Highest/Lowest channel breakout.
/// </summary>
public class DaydreamChannelBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _channelPeriod;
	private decimal? _prevHigh;
	private decimal? _prevLow;

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

	public DaydreamChannelBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_channelPeriod = Param(nameof(ChannelPeriod), 25).SetGreaterThanZero().SetDisplay("Channel Period", "Donchian lookback", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh = null;
		_prevLow = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevHigh = null; _prevLow = null;
		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevHigh = high; _prevLow = low; return; }
		if (_prevHigh == null || _prevLow == null) { _prevHigh = high; _prevLow = low; return; }
		var close = candle.ClosePrice;
		if (close > _prevHigh.Value && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (close < _prevLow.Value && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevHigh = high; _prevLow = low;
	}
}