在 GitHub 上查看
每周区间突破策略(ID 3412)
每周区间突破策略 是 MetaTrader 5 专家顾问 RangeBreakout.mq5 的 StockSharp 高级 API 复刻版本。策略会在每周指定的交易日和小时收盘后计算突破区间,当后续价格向上或向下突破该区间时开仓,仅维持一笔仓位。马丁格尔加仓与亏损补偿机制完整保留,同时借助 StockSharp 的蜡烛、Level1 行情与指标绑定接口实现。
交易流程
- 每周准备阶段。 当指定交易日的目标小时蜡烛收盘时,将收盘价保存为参考价,状态由「待机」切换为「准备」。
- 区间计算。
- 主要区间来自 20 日平均真实波幅(ATR),ATR 乘以
ATR Percentage 后再根据最小跳动位进行归一化。
- 如果 ATR 数据暂不可用,则改用当前卖价乘以
Price Percentage 作为替代。
- 保护价位。
- 上下突破触发价分别位于参考价上方与下方一个区间的位置。
- 止盈、止损距离按区间百分比计算。发生亏损时会启动补偿缓冲区,将止盈距离替换为累计补偿值,并按相同幅度扩大止损距离,与原脚本逻辑一致。
- 下单执行。
- 在「准备」阶段监听 Level1 行情。价格上破上沿即做多,下破下沿即做空,所有委托均为市价单,同时对价格进行跳动单位修正。
- 进入「交易」阶段后持续监控 Level1 行情,触发止盈或止损即以市价平仓。
- 马丁格尔恢复。
- 止损离场后,下一次下单量翻倍,并将本次区间亏损距离加入补偿缓冲区,使下一次止盈目标覆盖累计亏损。
- 止盈离场则重置加仓倍数与补偿缓冲区。
- 日终重置。 平仓后状态回到「待机」,等待下一个符合条件的交易日与小时重新生成区间。
参数说明
| 参数 |
默认值 |
说明 |
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 以价格距离记录累计亏损;启用时替换止盈距离并扩大止损,等效于原脚本利用成交盈亏反算价格差。
- 日志提示。 当用户选择周六或周日时,会输出信息日志并自动改为周一,复刻原脚本的警告。
使用建议
- 将
Trading Day 与 Start Hour 对齐到目标市场的低波动时段(例如亚洲盘)或重要开盘时段(例如伦敦开盘)。
- 联合调整
ATR Percentage、Take Profit Percentage 与 Stop Loss Percentage。更大的 ATR 倍数会放宽触发区间,改变交易频率;调整止盈止损百分比可修改收益风险比。
- 通过优化器扫描
Start Hour、Base Volume 以及各类百分比参数,重现原脚本的参数搜索流程。
- 关注马丁格尔倍数带来的风险放大。在高杠杆账户上运行时,可适当降低
Base Volume。
- 策略针对单一标的设计,可复制多份并配置不同品种或时段以扩充覆盖面。
转换说明
- ✅ 保留了每周调度、区间计算、保护价位与马丁格尔补偿等核心逻辑。
- ✅ 将 MetaTrader 特有函数(
iATR、CopyBuffer、OrderSend 等)替换为 StockSharp 的 SubscribeCandles、AverageTrueRange、BuyMarket/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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class range_breakout_weekly_strategy(Strategy):
def __init__(self):
super(range_breakout_weekly_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._channel_period = self.Param("ChannelPeriod", 20)
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def ChannelPeriod(self):
return self._channel_period.Value
@ChannelPeriod.setter
def ChannelPeriod(self, value):
self._channel_period.Value = value
def OnReseted(self):
super(range_breakout_weekly_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(range_breakout_weekly_strategy, self).OnStarted2(time)
self._has_prev = False
highest = Highest()
highest.Length = self.ChannelPeriod
lowest = Lowest()
lowest.Length = self.ChannelPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._process_candle).Start()
def _process_candle(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
if self._has_prev:
if close > self._prev_high and self.Position <= 0:
self.BuyMarket()
elif close < self._prev_low and self.Position >= 0:
self.SellMarket()
self._prev_high = float(high_value)
self._prev_low = float(low_value)
self._has_prev = True
def CreateClone(self):
return range_breakout_weekly_strategy()