在 GitHub 上查看

每周区间突破策略(ID 3412)

每周区间突破策略 是 MetaTrader 5 专家顾问 RangeBreakout.mq5 的 StockSharp 高级 API 复刻版本。策略会在每周指定的交易日和小时收盘后计算突破区间,当后续价格向上或向下突破该区间时开仓,仅维持一笔仓位。马丁格尔加仓与亏损补偿机制完整保留,同时借助 StockSharp 的蜡烛、Level1 行情与指标绑定接口实现。

交易流程

  1. 每周准备阶段。 当指定交易日的目标小时蜡烛收盘时,将收盘价保存为参考价,状态由「待机」切换为「准备」。
  2. 区间计算。
    • 主要区间来自 20 日平均真实波幅(ATR),ATR 乘以 ATR Percentage 后再根据最小跳动位进行归一化。
    • 如果 ATR 数据暂不可用,则改用当前卖价乘以 Price Percentage 作为替代。
  3. 保护价位。
    • 上下突破触发价分别位于参考价上方与下方一个区间的位置。
    • 止盈、止损距离按区间百分比计算。发生亏损时会启动补偿缓冲区,将止盈距离替换为累计补偿值,并按相同幅度扩大止损距离,与原脚本逻辑一致。
  4. 下单执行。
    • 在「准备」阶段监听 Level1 行情。价格上破上沿即做多,下破下沿即做空,所有委托均为市价单,同时对价格进行跳动单位修正。
    • 进入「交易」阶段后持续监控 Level1 行情,触发止盈或止损即以市价平仓。
  5. 马丁格尔恢复。
    • 止损离场后,下一次下单量翻倍,并将本次区间亏损距离加入补偿缓冲区,使下一次止盈目标覆盖累计亏损。
    • 止盈离场则重置加仓倍数与补偿缓冲区。
  6. 日终重置。 平仓后状态回到「待机」,等待下一个符合条件的交易日与小时重新生成区间。

参数说明

参数 默认值 说明
Trading Day Monday 用于测量参考蜡烛的交易日。若选择周六或周日,会自动切换到周一并输出提示。
Start Hour 0 参考蜡烛的小时(0-23),可用于回测不同的交易时段。
Price Percentage 1.0 ATR 不可用时,以卖价乘以该百分比生成区间。
ATR Percentage 100 ATR 倍数,用于计算突破区间宽度。
Take Profit Percentage 100 区间的百分比,决定止盈距入场的距离。发生亏损时会被补偿距离覆盖。
Stop Loss Percentage 100 区间的百分比,决定止损距入场的距离。补偿缓冲区同样会扩大该距离。
Base Volume 0.1 初始下单量,之后根据马丁格尔倍数调整。会自动按照 VolumeStep 四舍五入并限制在 VolumeMinVolumeMax 范围内。
ATR Period 20 ATR 参考的日线蜡烛数量。
Hour Candle Type 1 小时 用于检测准备窗口的蜡烛订阅。
ATR Candle Type 1 天 向 ATR 指标提供数据的蜡烛订阅。

实现细节

  • 数据订阅。 同时订阅 1 小时蜡烛(准备时间)、1 天蜡烛(ATR)与 Level1 行情(买卖价),利用高阶 Bind 接口直接获取指标值,无需手动复制缓存。
  • 价格归一化。 所有价格均通过 Security.ShrinkPrice 处理,确保满足最小跳动单位,与 MetaTrader 中的 NormalizeDouble 行为一致。
  • 下单量处理。 依据合约的最小/最大手数与步长自动修正交易量,完全对齐原脚本的 SetVolume 检查。
  • 状态机。 三个阶段(待机/准备/交易)对应原脚本的枚举,确保每个准备窗口只会触发一次交易。平仓后重新进入待机状态。
  • 补偿缓冲区。 compensationOffset 以价格距离记录累计亏损;启用时替换止盈距离并扩大止损,等效于原脚本利用成交盈亏反算价格差。
  • 日志提示。 当用户选择周六或周日时,会输出信息日志并自动改为周一,复刻原脚本的警告。

使用建议

  1. Trading DayStart Hour 对齐到目标市场的低波动时段(例如亚洲盘)或重要开盘时段(例如伦敦开盘)。
  2. 联合调整 ATR PercentageTake Profit PercentageStop Loss Percentage。更大的 ATR 倍数会放宽触发区间,改变交易频率;调整止盈止损百分比可修改收益风险比。
  3. 通过优化器扫描 Start HourBase Volume 以及各类百分比参数,重现原脚本的参数搜索流程。
  4. 关注马丁格尔倍数带来的风险放大。在高杠杆账户上运行时,可适当降低 Base Volume
  5. 策略针对单一标的设计,可复制多份并配置不同品种或时段以扩充覆盖面。

转换说明

  • ✅ 保留了每周调度、区间计算、保护价位与马丁格尔补偿等核心逻辑。
  • ✅ 将 MetaTrader 特有函数(iATRCopyBufferOrderSend 等)替换为 StockSharp 的 SubscribeCandlesAverageTrueRangeBuyMarket/SellMarket 等高级接口。
  • ✅ 代码中的注释均为英文,并提供了详尽的多语文档说明。
  • ✅ 未创建 Python 版本,也未修改测试项目,符合任务要求。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Range Breakout Weekly strategy: periodic range breakout using highest/lowest channels.
/// Buys on breakout above recent high, sells on breakout below recent low.
/// </summary>
public class RangeBreakoutWeeklyStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _channelPeriod;

	private decimal _prevHigh;
	private decimal _prevLow;
	private bool _hasPrev;

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

	public RangeBreakoutWeeklyStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_channelPeriod = Param(nameof(ChannelPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Channel Period", "Highest/Lowest period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh = 0m;
		_prevLow = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ProcessCandle).Start();
	}

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

		var close = candle.ClosePrice;

		if (_hasPrev)
		{
			if (close > _prevHigh && Position <= 0)
				BuyMarket();
			else if (close < _prevLow && Position >= 0)
				SellMarket();
		}

		_prevHigh = highValue;
		_prevLow = lowValue;
		_hasPrev = true;
	}
}