Advanced Adaptive Grid Strategy
Advanced Adaptive Grid Strategy uses multiple technical indicators to evaluate trend direction and builds a dynamic grid of entry levels. The grid size adapts to volatility via ATR and orders are placed when price touches grid levels in the trend direction. Risk controls include fixed stop-loss, take-profit, trailing stop, time-based exit and daily loss limit.
Details
- Entry Criteria:
- In trending markets price reaching calculated grid levels with RSI confirmation.
- In sideways markets overbought/oversold RSI triggers grid entries.
- Long/Short: Both sides.
- Exit Criteria:
- Stop-loss, take-profit, trailing stop, trend reversal or time-based exit.
- Stops: Fixed and trailing.
- Default Values:
BaseGridSize= 1MaxPositions= 5UseVolatilityGrid= TrueAtrLength= 14AtrMultiplier= 1.5RsiLength= 14RsiOverbought= 70RsiOversold= 30ShortMaLength= 20LongMaLength= 50SuperLongMaLength= 200MacdFastLength= 12MacdSlowLength= 26MacdSignalLength= 9StopLossPercent= 2TakeProfitPercent= 3UseTrailingStop= TrueTrailingStopPercent= 1MaxLossPerDay= 5TimeBasedExit= TrueMaxHoldingPeriod= 48
- Filters:
- Category: Grid / Trend
- Direction: Both
- Indicators: ATR, SMA, MACD, RSI, Momentum
- Stops: Yes
- Complexity: High
- Timeframe: Any
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: High
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Advanced Adaptive Grid Trading Strategy.
/// Uses RSI extremes and MA trend to enter, with percentage-based stop/TP.
/// </summary>
public class AdvancedAdaptiveGridStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _rsiOverbought;
private readonly StrategyParam<int> _rsiOversold;
private readonly StrategyParam<int> _shortMaLength;
private readonly StrategyParam<int> _longMaLength;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<int> _cooldownBars;
private decimal _entryPrice;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int RsiOverbought { get => _rsiOverbought.Value; set => _rsiOverbought.Value = value; }
public int RsiOversold { get => _rsiOversold.Value; set => _rsiOversold.Value = value; }
public int ShortMaLength { get => _shortMaLength.Value; set => _shortMaLength.Value = value; }
public int LongMaLength { get => _longMaLength.Value; set => _longMaLength.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AdvancedAdaptiveGridStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_rsiOverbought = Param(nameof(RsiOverbought), 70)
.SetDisplay("RSI Overbought", "Overbought level", "Indicators");
_rsiOversold = Param(nameof(RsiOversold), 30)
.SetDisplay("RSI Oversold", "Oversold level", "Indicators");
_shortMaLength = Param(nameof(ShortMaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Short MA", "Short moving average length", "Trend");
_longMaLength = Param(nameof(LongMaLength), 50)
.SetGreaterThanZero()
.SetDisplay("Long MA", "Long moving average length", "Trend");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 3m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var shortMa = new SimpleMovingAverage { Length = ShortMaLength };
var longMa = new SimpleMovingAverage { Length = LongMaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, shortMa, longMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, shortMa);
DrawIndicator(area, longMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal shortMaValue, decimal longMaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var currentPrice = candle.ClosePrice;
var bullish = shortMaValue > longMaValue;
var bearish = shortMaValue < longMaValue;
// Check stop/TP for existing positions
if (Position > 0 && _entryPrice > 0)
{
var stopPrice = _entryPrice * (1 - StopLossPercent / 100m);
var tpPrice = _entryPrice * (1 + TakeProfitPercent / 100m);
if (currentPrice <= stopPrice || currentPrice >= tpPrice)
{
SellMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
var stopPrice = _entryPrice * (1 + StopLossPercent / 100m);
var tpPrice = _entryPrice * (1 - TakeProfitPercent / 100m);
if (currentPrice >= stopPrice || currentPrice <= tpPrice)
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
return;
}
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Entry logic
if (bullish && rsiValue < RsiOversold && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = currentPrice;
_cooldownRemaining = CooldownBars;
}
else if (bearish && rsiValue > RsiOverbought && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = currentPrice;
_cooldownRemaining = CooldownBars;
}
// Trend reversal exit
else if (Position > 0 && bearish && rsiValue > RsiOverbought)
{
SellMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
}
else if (Position < 0 && bullish && rsiValue < RsiOversold)
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class advanced_adaptive_grid_strategy(Strategy):
def __init__(self):
super(advanced_adaptive_grid_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._rsi_overbought = self.Param("RsiOverbought", 70) \
.SetDisplay("RSI Overbought", "Overbought level", "Indicators")
self._rsi_oversold = self.Param("RsiOversold", 30) \
.SetDisplay("RSI Oversold", "Oversold level", "Indicators")
self._short_ma_length = self.Param("ShortMaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Short MA", "Short moving average length", "Trend")
self._long_ma_length = self.Param("LongMaLength", 50) \
.SetGreaterThanZero() \
.SetDisplay("Long MA", "Long moving average length", "Trend")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", 3.0) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._entry_price = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(advanced_adaptive_grid_strategy, self).OnReseted()
self._entry_price = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(advanced_adaptive_grid_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = int(self._rsi_length.Value)
short_ma = SimpleMovingAverage()
short_ma.Length = int(self._short_ma_length.Value)
long_ma = SimpleMovingAverage()
long_ma.Length = int(self._long_ma_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, short_ma, long_ma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, short_ma)
self.DrawIndicator(area, long_ma)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_val, short_ma_val, long_ma_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
current_price = float(candle.ClosePrice)
rsi_v = float(rsi_val)
short_v = float(short_ma_val)
long_v = float(long_ma_val)
bullish = short_v > long_v
bearish = short_v < long_v
sl_pct = float(self._stop_loss_percent.Value)
tp_pct = float(self._take_profit_percent.Value)
ob = float(self._rsi_overbought.Value)
os_level = float(self._rsi_oversold.Value)
cooldown = int(self._cooldown_bars.Value)
# Check stop/TP for existing positions
if self.Position > 0 and self._entry_price > 0:
stop_price = self._entry_price * (1.0 - sl_pct / 100.0)
tp_price = self._entry_price * (1.0 + tp_pct / 100.0)
if current_price <= stop_price or current_price >= tp_price:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
return
elif self.Position < 0 and self._entry_price > 0:
stop_price = self._entry_price * (1.0 + sl_pct / 100.0)
tp_price = self._entry_price * (1.0 - tp_pct / 100.0)
if current_price >= stop_price or current_price <= tp_price:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
# Entry logic
if bullish and rsi_v < os_level and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = current_price
self._cooldown_remaining = cooldown
elif bearish and rsi_v > ob and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = current_price
self._cooldown_remaining = cooldown
# Trend reversal exit
elif self.Position > 0 and bearish and rsi_v > ob:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
elif self.Position < 0 and bullish and rsi_v < os_level:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
def CreateClone(self):
return advanced_adaptive_grid_strategy()