Diese Strategie repliziert den MetaTrader-Experten „Gridtrading_at_volatile_market.mq4“ unter Verwendung der StockSharp-Hochebene API. Es handelt um Donchian Kanalgrenzen, die in einem längeren Zeitrahmen erkannt wurden, und bestätigt gleichzeitig Einträge mit verschlingenden Mustern im Handelszeitrahmen. Sobald ein Raster aktiv ist, fügt die Strategie durchschnittliche Aufträge hinzu, wenn der Preis um ein Vielfaches des höheren Zeitrahmens ATR steigt, und endet, wenn die Portfoliogewinn- oder Drawdown-Ziele erreicht sind.
Wie es funktioniert
Es werden zwei Candle-Streams verwendet: der vom Benutzer ausgewählte Handelszeitrahmen und ein automatisch daraus abgeleiteter höherer Zeitrahmen (M1→M5→M15→M30→H1→H4→D1).
Auf dem höheren Zeitrahmen berechnet die Strategie:
ATR(20) zur Größe des Rasterabstands.
SMA(SlowMaLength), um den Trend zusammen mit RSI zu filtern.
DonchianChannels(20) für Unterstützungs- und Widerstandsniveaus.
Im Handelszeitraum werden die letzten beiden abgeschlossenen Kerzen verfolgt, um bullische oder bärische Engulfing-Muster zu erkennen.
Ein langes Raster beginnt, wenn die vorherige Kerze das untere Band von Donchian berührt, ein bullisches Engulfing-Muster bildet und RSI überverkaufte Bedingungen bestätigt (RSI < 35, während der Preis über dem höheren Zeitrahmen SMA liegt). Ein kurzes Gitter spiegelt diese Regeln im oberen Band mit RSI > 65 wider.
Nach der ersten Marktorder behält die Strategie den Anfangspreis als Anker bei. Wenn sich der Preis im aktuellen Rasterschritt um 2 * ATR gegenüber der Position bewegt, wird eine weitere Order mit einem Volumen multipliziert mit GridMultiplier hinzugefügt.
Das Raster wird geschlossen und alle Bestellungen werden storniert, wenn:
Der kombinierte (realisierte + nicht realisierte) PnL übersteigt TakeProfitFactor * total grid volume.
Der Drawdown fällt unter -MaxDrawdownFraction * initial portfolio value.
Parameter
TakeProfitFactor – Gewinnmultiplikator des gesamten Netzvolumens, das zum Schließen des Netzes erforderlich ist (Standard 0.1).
SlowMaLength – Zeitraum des höheren Zeitrahmens SMA, der zum Filtern verwendet wird (Standard 50).
GridMultiplier – geometrischer Faktor, der auf jede zusätzliche Mittelungsreihenfolge angewendet wird (Standard 1.5).
BaseOrderVolume – Volumen der ersten Ordnung im Raster (Standard 0.1).
MaxDrawdownFraction – maximaler Verlust im Verhältnis zum anfänglichen Portfoliowert, bevor das Raster zwangsweise geschlossen wird (Standard 0.8).
CandleType – Handelszeitraum. Der höhere Zeitrahmen wird automatisch abgeleitet.
Notizen
Es werden nur geschlossene Kerzen verarbeitet, um ein Nachlackieren zu vermeiden.
Die Strategie stützt sich auf verfügbare Geld-/Briefkurse, um den offenen PnL zu bewerten; Wenn nur die letzten Handelspreise angegeben werden, ist die Näherung möglicherweise weniger genau.
Wenn die Portfolioinformationen nicht verfügbar sind, wird der Drawdown-Schutz übersprungen, sodass das Raster weiterläuft, bis das Gewinnziel erreicht ist oder die Position manuell geschlossen wird.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "Grid Trading at Volatile Market" MetaTrader expert.
/// Uses RSI + SMA trend filter for initial entry then manages a simple
/// grid of averaging orders based on ATR distance.
/// </summary>
public class GridTradingAtVolatileMarketStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _maxGridLevels;
private RelativeStrengthIndex _rsi;
private readonly Queue<decimal> _smaQueue = new();
private decimal _smaSum;
private Sides? _gridDirection;
private int _gridLevel;
private decimal _lastEntryPrice;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
public int MaxGridLevels
{
get => _maxGridLevels.Value;
set => _maxGridLevels.Value = value;
}
public GridTradingAtVolatileMarketStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for signal detection", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period for entry signals", "Indicators");
_smaPeriod = Param(nameof(SmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "SMA period for trend filter", "Indicators");
_maxGridLevels = Param(nameof(MaxGridLevels), 3)
.SetGreaterThanZero()
.SetDisplay("Max Grid Levels", "Maximum averaging levels", "Grid");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_smaQueue.Clear();
_smaSum = 0;
_gridDirection = null;
_gridLevel = 0;
_lastEntryPrice = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
// Manual SMA
_smaQueue.Enqueue(close);
_smaSum += close;
while (_smaQueue.Count > SmaPeriod)
_smaSum -= _smaQueue.Dequeue();
if (!_rsi.IsFormed || _smaQueue.Count < SmaPeriod)
return;
var smaValue = _smaSum / _smaQueue.Count;
var volume = Volume;
if (volume <= 0)
volume = 1;
// If no grid active, look for entry signals
if (_gridDirection is null)
{
// Buy: RSI oversold + price below SMA (mean reversion)
if (rsiValue < 35 && close < smaValue)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(volume);
_gridDirection = Sides.Buy;
_gridLevel = 1;
_lastEntryPrice = close;
}
// Sell: RSI overbought + price above SMA
else if (rsiValue > 65 && close > smaValue)
{
if (Position > 0)
SellMarket(Position);
SellMarket(volume);
_gridDirection = Sides.Sell;
_gridLevel = 1;
_lastEntryPrice = close;
}
}
else
{
// Grid management - add levels on adverse moves
var distanceThreshold = _lastEntryPrice * 0.005m; // 0.5% grid step
if (_gridDirection == Sides.Buy)
{
// Price moved further down - add to grid
if (_gridLevel < MaxGridLevels && close < _lastEntryPrice - distanceThreshold)
{
BuyMarket(volume);
_gridLevel++;
_lastEntryPrice = close;
}
// Take profit - price recovered above SMA
else if (close > smaValue && rsiValue > 50)
{
if (Position > 0)
SellMarket(Position);
_gridDirection = null;
_gridLevel = 0;
}
}
else if (_gridDirection == Sides.Sell)
{
// Price moved further up - add to grid
if (_gridLevel < MaxGridLevels && close > _lastEntryPrice + distanceThreshold)
{
SellMarket(volume);
_gridLevel++;
_lastEntryPrice = close;
}
// Take profit - price fell below SMA
else if (close < smaValue && rsiValue < 50)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
_gridDirection = null;
_gridLevel = 0;
}
}
}
}
/// <inheritdoc />
protected override void OnReseted()
{
_rsi = null;
_smaQueue.Clear();
_smaSum = 0;
_gridDirection = null;
_gridLevel = 0;
_lastEntryPrice = 0;
base.OnReseted();
}
}
import clr
from collections import deque
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class grid_trading_at_volatile_market_strategy(Strategy):
"""
Grid Trading at Volatile Market: RSI + SMA trend filter for initial entry
then manages a grid of averaging orders based on distance threshold.
"""
def __init__(self):
super(grid_trading_at_volatile_market_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period for entry signals", "Indicators")
self._sma_period = self.Param("SmaPeriod", 50) \
.SetDisplay("SMA Period", "SMA period for trend filter", "Indicators")
self._max_grid_levels = self.Param("MaxGridLevels", 3) \
.SetDisplay("Max Grid Levels", "Maximum averaging levels", "Grid")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe for signal detection", "General")
self._sma_queue = deque()
self._sma_sum = 0.0
self._grid_direction = None # 'buy' or 'sell'
self._grid_level = 0
self._last_entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(grid_trading_at_volatile_market_strategy, self).OnReseted()
self._sma_queue = deque()
self._sma_sum = 0.0
self._grid_direction = None
self._grid_level = 0
self._last_entry_price = 0.0
def OnStarted2(self, time):
super(grid_trading_at_volatile_market_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def _process_candle(self, candle, rsi_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
rsi = float(rsi_val)
self._sma_queue.append(close)
self._sma_sum += close
sma_period = self._sma_period.Value
while len(self._sma_queue) > sma_period:
self._sma_sum -= self._sma_queue.popleft()
if len(self._sma_queue) < sma_period:
return
sma_value = self._sma_sum / len(self._sma_queue)
if self._grid_direction is None:
if rsi < 35 and close < sma_value:
self.BuyMarket()
self._grid_direction = 'buy'
self._grid_level = 1
self._last_entry_price = close
elif rsi > 65 and close > sma_value:
self.SellMarket()
self._grid_direction = 'sell'
self._grid_level = 1
self._last_entry_price = close
else:
distance_threshold = self._last_entry_price * 0.005
if self._grid_direction == 'buy':
if self._grid_level < self._max_grid_levels.Value and close < self._last_entry_price - distance_threshold:
self.BuyMarket()
self._grid_level += 1
self._last_entry_price = close
elif close > sma_value and rsi > 50:
if self.Position > 0:
self.SellMarket()
self._grid_direction = None
self._grid_level = 0
elif self._grid_direction == 'sell':
if self._grid_level < self._max_grid_levels.Value and close > self._last_entry_price + distance_threshold:
self.SellMarket()
self._grid_level += 1
self._last_entry_price = close
elif close < sma_value and rsi < 50:
if self.Position < 0:
self.BuyMarket()
self._grid_direction = None
self._grid_level = 0
def CreateClone(self):
return grid_trading_at_volatile_market_strategy()