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

转换说明

  • ✅ 保留了每周调度、区间计算、保护价位与马丁格尔补偿等核心逻辑。
  • ✅ 将 MetaTrader 特有函数(iATR、CopyBuffer、OrderSend 等)替换为 StockSharp 的 SubscribeCandles、AverageTrueRange、BuyMarket/SellMarket 等高级接口。
  • ✅ 代码中的注释均为英文,并提供了详尽的多语文档说明。
  • 同时提供 C# 和 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;
	}
}