在 GitHub 上查看
VR Smart Grid Lite 策略
VR Smart Grid Lite 策略 重现了同名 MetaTrader 智能交易系统的运行方式。策略通过市价单构建马丁格尔式网格,在价格逆势移动达到设定步长时加仓。网格支持两种出场方式:在加权平均价一次性平掉最旧与最新订单,或部分减仓以保留网格结构。
参数
- Take Profit (pips) – 当仅有一笔仓位时使用的止盈距离。
- Start Volume – 每个方向首笔订单的初始手数。
- Maximal Volume – 单笔订单允许的最大手数。
- Close Mode –
Average 在权重目标价同时平掉最旧和最新订单;PartClose 仅减掉最新订单的起始手数并完全平掉最旧订单。
- Order Step (pips) – 价格逆势移动达到该步长后才会加仓。
- Minimal Profit (pips) – 在加权平均价基础上追加的额外利润缓冲。
- Slippage (pips) – 保留自原始 EA 的滑点输入,用于文档完整性。
- Candle Type – 用于驱动逻辑的 K 线周期,上一根已完成 K 线决定当前偏向。
算法
- 每当收到新的完结 K 线时,读取上一根 K 线的方向。
- 如果上一根 K 线收阳,且当前没有多单或价格较最低买入价下跌超过设定步长,则以市价买入新仓。
- 如果上一根 K 线收阴,且当前没有空单或价格较最高卖出价上涨超过设定步长,则以市价卖出新仓。
- 新仓手数来源于当前方向最低(或最高)价格订单的手数,并在每次加仓时加倍,同时遵守最大手数和交易所的最小手数/步长限制。
- 当某方向只剩一笔仓位时,使用固定止盈距离退出。
- 当存在多笔仓位时,根据最旧与最新订单计算加权平均价并决定出场:
- Average 模式:价格触及权重目标价加上利润缓冲时,同时平掉最旧和最新订单。
- PartClose 模式:价格触及目标时,按起始手数减仓最新订单,并完全平掉最旧订单,其余网格继续保持。
- 策略跟踪所有开仓与平仓,保持内部网格状态与真实持仓同步。
注意事项
- 策略使用市价单,实际成交价与滑点取决于经纪商与市场流动性。
- 请确认交易品种的最小手数与步长与设定的起始手数兼容。
- 网格/马丁策略在强趋势行情中风险迅速累积,请务必做好资金管理。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// VR Smart Grid Lite: grid trading based on price levels with SMA filter.
/// </summary>
public class VrSmartGridLiteStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _gridPercent;
private readonly StrategyParam<int> _smaPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal GridPercent
{
get => _gridPercent.Value;
set => _gridPercent.Value = value;
}
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
public VrSmartGridLiteStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_gridPercent = Param(nameof(GridPercent), 3.0m)
.SetGreaterThanZero()
.SetDisplay("Grid %", "Grid step percentage", "Grid");
_smaPeriod = Param(nameof(SmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "SMA period for trend", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = SmaPeriod };
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 * GridPercent / 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 vr_smart_grid_lite_strategy(Strategy):
def __init__(self):
super(vr_smart_grid_lite_strategy, self).__init__()
self._grid_percent = self.Param("GridPercent", 3.0) \
.SetDisplay("Grid %", "Grid step percentage", "Grid")
self._sma_period = self.Param("SmaPeriod", 20) \
.SetDisplay("SMA Period", "SMA period for trend", "Indicators")
self._sma = None
self._last_trade_price = None
@property
def grid_percent(self):
return self._grid_percent.Value
@property
def sma_period(self):
return self._sma_period.Value
def OnReseted(self):
super(vr_smart_grid_lite_strategy, self).OnReseted()
self._sma = None
self._last_trade_price = None
def OnStarted2(self, time):
super(vr_smart_grid_lite_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = self.sma_period
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_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 vr_smart_grid_lite_strategy()