在 GitHub 上查看
GBP 9 AM Breakout 策略
概述
GBP 9 AM Breakout Strategy 是经典 MetaTrader "GBP9AM" 专家的 StockSharp 版本。策略在伦敦市场开盘(本地时间 9:00)附近同时放置买入止损和卖出止损订单,以捕捉开盘后的方向性突破。所有距离均以点(pip)为单位,并根据交易品种的最小报价单位自动换算。
交易逻辑
- 策略订阅可配置的 K 线类型(默认 1 分钟),仅在 K 线收盘后进行计算,保证时间戳精确。
- 每个交易日仅准备一次双向挂单,新交易日会自动重置状态。
- 当 K 线时间达到参数
LookHour 与 LookMinute 时:
- 取消仍在市场中的挂单,并关闭可能存在的持仓,防止信号冲突。
- 基于最新收盘价计算买入止损、卖出止损及其对应的止损/止盈价位,自动处理 3 位或 5 位小数报价的点值。
- 同时挂出 Buy Stop 与 Sell Stop 订单,形成开盘突破的“跨式”结构。
- 任一方向成交后,另一侧挂单立即撤销。策略随后持续监控价格,满足止损或止盈条件时平仓。
- 可选的
UseCloseHour 参数允许在设定时间(默认 18:00)强制平仓并撤单,避免隔夜持仓。
参数说明
| 参数 |
说明 |
Volume |
双向挂单的交易量。 |
LookHour |
触发布单的小时(0–23),需与经纪商时区下的伦敦 9:00 对应。 |
LookMinute |
触发布单的分钟。 |
CloseHour |
日内强制平仓与撤单的小时。 |
UseCloseHour |
是否启用日内强制平仓功能。 |
TakeProfitPips |
距离入场价的止盈点数,适用于多空双方。 |
BuyDistancePips |
Buy Stop 距离最新价的点数。 |
SellDistancePips |
Sell Stop 距离最新价的点数。 |
BuyStopLossPips |
多头仓位的止损点数。 |
SellStopLossPips |
空头仓位的止损点数。 |
CandleType |
用于计时与风控的 K 线类型(默认 1 分钟)。 |
风险控制
- 每笔交易在挂单阶段即设定对应的止损与止盈,实现固定的风险回报结构。
- 日终平仓机制可避免隔夜风险,除非手动关闭该功能。
- 策略每天仅建立一次突破结构,能够减少横盘行情中的过度交易。
使用建议
- 根据行情服务器的时区调整
LookHour,确保 9:00 伦敦时间被正确触发。
- 结合当前波动性调整各类点距参数,使止损与目标更加贴合市场。
- 建议在具有可靠
PriceStep 信息的外汇品种上使用,以保证点值转换准确。
- 注意经纪商的保证金要求,较大的
Volume 设置需要更高的资金占用,与原始 MQL 版本相同。
文件结构
CS/Gbp9AmBreakoutStrategy.cs:基于 StockSharp 高级 API 的 C# 实现。
README.md:英文说明。
README_ru.md:俄文说明。
README_zh.md:中文说明(当前文件)。
根据项目约束,本策略暂不提供 Python 版本。
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>
/// Session breakout strategy using Highest/Lowest channel.
/// Trades on breakouts above/below the previous channel levels.
/// </summary>
public class Gbp9AmBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal? _prevHigh;
private decimal? _prevLow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
public Gbp9AmBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_period = Param(nameof(Period), 12)
.SetGreaterThanZero()
.SetDisplay("Period", "Channel lookback period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(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 = Period };
var lowest = new Lowest { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevHigh = high;
_prevLow = low;
return;
}
var close = candle.ClosePrice;
if (_prevHigh == null || _prevLow == null)
{
_prevHigh = high;
_prevLow = low;
return;
}
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;
}
}
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 gbp9_am_breakout_strategy(Strategy):
def __init__(self):
super(gbp9_am_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._period = self.Param("Period", 12) \
.SetDisplay("Period", "Channel lookback period", "Indicators")
self._prev_high = None
self._prev_low = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Period(self):
return self._period.Value
def OnReseted(self):
super(gbp9_am_breakout_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
def OnStarted2(self, time):
super(gbp9_am_breakout_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
highest = Highest()
highest.Length = self.Period
lowest = Lowest()
lowest.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _on_process(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
hv = float(high_value)
lv = float(low_value)
close = float(candle.ClosePrice)
if self._prev_high is None or self._prev_low is None:
self._prev_high = hv
self._prev_low = lv
return
if close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = hv
self._prev_low = lv
def CreateClone(self):
return gbp9_am_breakout_strategy()