在 GitHub 上查看
Gaps 策略
该策略基于价格缺口的反转思路:当新 K 线的开盘价突破上一根 K 线的最高价或最低价一定的点数后,立即在
反方向建立仓位,并通过固定止损、固定止盈以及可选的分级跟踪止损来管理风险。
工作流程
- 在选定的时间框架上监控单个标的的收盘 K 线数据。
- 每当新 K 线生成时,对比最近两根 K 线的开盘价与上一根 K 线的高低点:
- 如果新 K 线开盘价低于上一根最低价并超过
GapPips 点,判定出现向下跳空,买入做多,期待价格回补缺口;
- 如果新 K 线开盘价高于上一根最高价并超过
GapPips 点,判定出现向上跳空,卖出做空,期待价格回落。
- 入场后策略自动进行持仓管理:
- 止损价设在距离入场价
StopLossPips 点的位置(多单在下方,空单在上方);
- 止盈价设在距离入场价
TakeProfitPips 点的位置(沿持仓方向);
- 若开启跟踪止损,当价格累计向有利方向移动
TrailingStopPips + TrailingStepPips 点时,
将止损价锁定在距离最新极值 TrailingStopPips 点的位置,且只有当价格再次前进至少 TrailingStepPips 点时才会继续移动。
- 每根已完成 K 线都会根据最高价与最低价检查是否触发止损或止盈,确保盘中触价能够在下一根 K 线立即执行退出。
- 入场前会取消所有挂单,并在需要反手时通过一笔市价单同时平掉旧仓位并建立新方向仓位。
参数说明
OrderVolume = 0.1 —— 每次建仓的交易量(手数)。
StopLossPips = 50 —— 入场价与止损价之间的点数,设为 0 表示不使用止损。
TakeProfitPips = 50 —— 入场价与止盈价之间的点数,设为 0 表示不使用止盈。
TrailingStopPips = 5 —— 跟踪止损与最新极值之间的点数,设为 0 表示关闭跟踪功能。
TrailingStepPips = 5 —— 每次移动跟踪止损前所需的最小价格改变量(点)。
GapPips = 1 —— 触发交易所需的最小跳空幅度(点)。
CandleType = 1 小时时间框架 —— 用于监控缺口并执行风控的 K 线类型。
实现细节
- 所有以点数表示的参数会根据标的的最小报价单位转换成绝对价格;对外汇市场的三位或五位小数报价会自动调
整以获得真实的“pip”尺寸。
- 当
TrailingStopPips 大于 0 时,TrailingStepPips 必须为正值,否则策略会在启动时抛出异常,避免与原始 MQL
版本逻辑不一致。
- 策略仅在完整收盘的 K 线上进行信号与风控判断,符合 StockSharp 高阶 API 的使用规范。
- 止损与止盈通过市价离场实现,不会在盘口中挂出额外的保护性订单。
- 默认参数针对外汇品种设计;在波动率不同的市场中使用时,请根据需要调整点数与时间框架。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Gap trading strategy. Detects price gaps between candles and trades the gap fill.
/// </summary>
public class GapsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _gapPercent;
private decimal? _prevClose;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal GapPercent
{
get => _gapPercent.Value;
set => _gapPercent.Value = value;
}
public GapsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_gapPercent = Param(nameof(GapPercent), 0.05m)
.SetDisplay("Gap Percent", "Minimum gap size as percentage", "Trading");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevClose == null)
{
_prevClose = candle.ClosePrice;
return;
}
var prevClose = _prevClose.Value;
var open = candle.OpenPrice;
var close = candle.ClosePrice;
_prevClose = close;
if (prevClose == 0)
return;
var gapPct = (open - prevClose) / prevClose * 100;
// Gap up detected - sell expecting gap fill
if (gapPct > GapPercent && Position == 0)
{
SellMarket();
}
// Gap down detected - buy expecting gap fill
else if (gapPct < -GapPercent && Position == 0)
{
BuyMarket();
}
}
}
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.Strategies import Strategy
class gaps_strategy(Strategy):
def __init__(self):
super(gaps_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._gap_percent = self.Param("GapPercent", 0.05) \
.SetDisplay("Gap Percent", "Minimum gap size as percentage", "Trading")
self._prev_close = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def GapPercent(self):
return self._gap_percent.Value
def OnReseted(self):
super(gaps_strategy, self).OnReseted()
self._prev_close = None
def OnStarted2(self, time):
super(gaps_strategy, self).OnStarted2(time)
self._prev_close = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
if self._prev_close is None:
self._prev_close = close
return
prev_close = self._prev_close
self._prev_close = close
if prev_close == 0:
return
gap_pct = (open_price - prev_close) / prev_close * 100.0
gp = float(self.GapPercent)
# Gap up detected - sell expecting gap fill
if gap_pct > gp and self.Position == 0:
self.SellMarket()
# Gap down detected - buy expecting gap fill
elif gap_pct < -gp and self.Position == 0:
self.BuyMarket()
def CreateClone(self):
return gaps_strategy()