在 GitHub 上查看
网格模板策略
概述
该策略是 MetaTrader 4 专家顾问 Grid_Template 的 StockSharp 移植版本。它会在当前买卖价附近构建一组对称的挂单网格,可直接作为突破模板使用,也可以在此基础上叠加自定义入场过滤条件。当所有挂单成交或被取消后,策略会立即重建新网格。实现保留了原始 EA 的资金管理公式,以及在设定小时数后自动撤销未触发挂单的机制。
交易逻辑
- 订阅 Level1 报价,持续跟踪最佳买价与卖价,无需蜡烛或指标。
- 当账户没有持仓且策略没有任何活动订单时,分别在卖价上方和买价下方各放置
GridOrders 张止损挂单。
- 第一层挂单距离当前价格
PriceDistancePips 个点,之后每一层额外再增加 GridStepPips 个点的间距。
- 所有挂单共用同一笔固定手数(或资金管理计算得到的手数),并使用相同的止盈、止损点数。
- 挂单被触发成交后,策略立即登记对应的止损 (
SellStop/BuyStop) 与止盈 (SellLimit/BuyLimit) 保护单,注释与入场单保持一致,便于识别。
- 如果在到期时间前没有任何挂单被触发,则撤销所有尚未执行的挂单并重新布置网格。
资金管理
- 当
UseMoneyManagement 关闭时,所有订单都使用固定参数 StaticVolume 所定义的手数。
- 当开启资金管理时,手数按照原始模板公式
freeMargin * RiskPercent / 100000 计算,再根据交易所的 VolumeStep、VolumeMin、VolumeMax 进行四舍五入与裁剪。策略使用投资组合的当前价值近似 MT4 的可用保证金。
- 计算出的手数会再次根据合约最小手数规范化,如果低于允许范围则返回 0,以避免提交无效订单。
订单与风险控制
- 买入止损挂单价格 =
ask + PriceDistancePips + GridStepPips * level,卖出方向采用对称计算。
- 保护单在挂单成交后才会注册,从而模拟 MT4 中止盈/止损直接附着在同一交易单上的行为。
PendingExpirationHours 控制挂单的存活时间,为 0 时表示挂单会一直保留直至成交或手动取消。
- 净持仓回到 0 后,策略会自动撤销任何仍处于活动状态的保护单,确保环境干净。
参数
| 参数 |
说明 |
OrderComment |
为所有网格订单写入的注释,与原 EA 保持一致。 |
StaticVolume |
在禁用资金管理时使用的固定手数。 |
UseMoneyManagement |
是否启用资金管理公式。 |
RiskPercent |
资金管理公式使用的风险百分比。 |
TakeProfitPips |
每笔网格订单的止盈距离。 |
StopLossPips |
每笔网格订单的止损距离。 |
PriceDistancePips |
当前价格到第一层挂单的点数间距。 |
GridStepPips |
相邻网格层之间额外增加的点数。 |
GridOrders |
每个方向创建的挂单数量。 |
PendingExpirationHours |
挂单网格的有效时长(小时)。 |
备注
- 策略本身不包含任何指标条件,可通过继承并重写
TryPlaceGrid 等方法添加自定义过滤逻辑。
- 由于止盈与止损通过独立订单实现,若出现部分成交,实际执行方式可能与 MT4 的票据式保护价存在细微差异。
- 运行前请确认交易所的
PriceStep 与 Decimals 设置对应正确的点值,以避免在实盘中出现偏差。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Grid Template strategy: places trades at regular grid intervals.
/// Buys when price drops by grid step, sells when price rises by grid step.
/// </summary>
public class GridTemplateStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _gridStepPercent;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal GridStepPercent
{
get => _gridStepPercent.Value;
set => _gridStepPercent.Value = value;
}
public GridTemplateStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_gridStepPercent = Param(nameof(GridStepPercent), 3.0m)
.SetGreaterThanZero()
.SetDisplay("Grid Step %", "Price change percentage for grid level", "Grid");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = 10 };
decimal? lastTradePrice = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, (candle, smaVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (!lastTradePrice.HasValue)
{
lastTradePrice = close;
return;
}
var step = lastTradePrice.Value * GridStepPercent / 100m;
if (close <= lastTradePrice.Value - step)
{
BuyMarket();
lastTradePrice = close;
}
else if (close >= lastTradePrice.Value + step)
{
SellMarket();
lastTradePrice = close;
}
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class grid_template_strategy(Strategy):
def __init__(self):
super(grid_template_strategy, self).__init__()
self._grid_step_percent = self.Param("GridStepPercent", 3.0) \
.SetDisplay("Grid Step %", "Price change percentage for grid level", "Grid")
self._sma = None
self._last_trade_price = None
@property
def grid_step_percent(self):
return self._grid_step_percent.Value
def OnReseted(self):
super(grid_template_strategy, self).OnReseted()
self._sma = None
self._last_trade_price = None
def OnStarted2(self, time):
super(grid_template_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = 10
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.Bind(self._sma, self._process_candle)
subscription.Start()
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
if not self._sma.IsFormed:
return
close = float(candle.ClosePrice)
if self._last_trade_price is None:
self._last_trade_price = close
return
step = self._last_trade_price * self.grid_step_percent / 100.0
if close <= self._last_trade_price - step:
self.BuyMarket()
self._last_trade_price = close
elif close >= self._last_trade_price + step:
self.SellMarket()
self._last_trade_price = close
def CreateClone(self):
return grid_template_strategy()