Estrategia de Cuadrícula Dinámica Ilan 1.6
La estrategia Ilan 1.6 Dynamic es un asesor experto clásico de cuadrícula y martingala. Abre una operación inicial en una dirección seleccionada y coloca órdenes adicionales cada vez que el precio se mueve en contra de la posición por un paso fijo. El volumen de las nuevas órdenes crece geométricamente por un exponente de lote. Todas las posiciones de la cesta se cierran cuando el precio regresa al precio de entrada promedio más una distancia de take profit. Un stop trailing puede proteger opcionalmente las ganancias si el precio se mueve lo suficiente en la dirección favorable.
El algoritmo se basa únicamente en el movimiento del precio y no utiliza indicadores. Dado que el tamaño de la posición aumenta después de cada movimiento adverso, el sistema conlleva un riesgo elevado pero puede capturar reversiones rápidas.
Detalles
- Entrada
- La primera orden se abre en la dirección configurada.
- Se añaden órdenes adicionales cada
PipStep puntos contra la posición actual, hasta MaxTrades.
- Volumen de cada nueva orden =
InitialVolume * LotExponent^N.
- Salida
- Cerrar todo cuando el precio toca
AveragePrice ± TakeProfit.
- Stop trailing opcional que comienza después de
TrailStart puntos de ganancia y sigue el precio a distancia TrailStop.
- Gestión de posición
- Solo serie larga o solo corta a la vez.
- Tras cerrar la cesta, la estrategia reinicia desde la dirección inicial.
- Parámetros
InitialVolume – volumen de la primera orden (predeterminado 1).
LotExponent – multiplicador para el tamaño de órdenes posteriores (predeterminado 1.6).
PipStep – distancia en puntos entre niveles de cuadrícula (predeterminado 30).
TakeProfit – objetivo de ganancia desde el precio promedio en puntos (predeterminado 10).
MaxTrades – número máximo de órdenes activas (predeterminado 10).
StartLong – abrir la primera operación como largo si es verdadero (predeterminado true).
UseTrailingStop – activar stop trailing (predeterminado false).
TrailStart – ganancia en puntos para iniciar el trailing (predeterminado 10).
TrailStop – distancia de trailing en puntos (predeterminado 10).
CandleType – marco temporal de velas (predeterminado 1 minuto).
- Filtros
- Categoría: Grid
- Dirección: Ambos
- Indicadores: Ninguno
- Stops: Opcional
- Complejidad: Moderado
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Alto
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Grid averaging strategy based on the Ilan 1.6 Dynamic expert advisor.
/// Adds positions when price moves against the current one and closes the
/// whole basket on a take profit.
/// Each grid level trades 1 unit; closing flattens via multiple market orders.
/// </summary>
public class Ilan16DynamicStrategy : Strategy
{
private readonly StrategyParam<decimal> _pipStep;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _maxTrades;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _startLong;
private int _tradeCount;
private decimal _lastEntryPrice;
private decimal _avgPrice;
private bool _isLong;
/// <summary>
/// Distance in price steps between grid levels.
/// </summary>
public decimal PipStep { get => _pipStep.Value; set => _pipStep.Value = value; }
/// <summary>
/// Profit target from average price in price steps.
/// </summary>
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
/// <summary>
/// Maximum number of averaging entries.
/// </summary>
public int MaxTrades { get => _maxTrades.Value; set => _maxTrades.Value = value; }
/// <summary>
/// Type of candles to process.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Open first trade as long if true.
/// </summary>
public bool StartLong { get => _startLong.Value; set => _startLong.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public Ilan16DynamicStrategy()
{
_pipStep = Param(nameof(PipStep), 50000m)
.SetGreaterThanZero()
.SetDisplay("Pip Step", "Distance in price steps between grid levels", "Trading")
.SetOptimize(10000m, 100000m, 10000m);
_takeProfit = Param(nameof(TakeProfit), 30000m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Profit target from average price in price steps", "Trading")
.SetOptimize(10000m, 100000m, 10000m);
_maxTrades = Param(nameof(MaxTrades), 3)
.SetGreaterThanZero()
.SetDisplay("Max Trades", "Maximum number of averaging entries", "Trading")
.SetOptimize(2, 10, 1);
_startLong = Param(nameof(StartLong), true)
.SetDisplay("Start Long", "Open first trade as long", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_isLong = StartLong;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var step = Security.PriceStep ?? 1m;
var price = candle.ClosePrice;
// No position - open initial entry
if (Position == 0)
{
if (_isLong)
BuyMarket();
else
SellMarket();
_tradeCount = 1;
_lastEntryPrice = price;
_avgPrice = price;
return;
}
// Check take profit: close entire basket
if (_isLong && price >= _avgPrice + TakeProfit * step)
{
CloseAll();
return;
}
else if (!_isLong && price <= _avgPrice - TakeProfit * step)
{
CloseAll();
return;
}
// Check for grid averaging entry (price moved against us)
if (_isLong && _tradeCount < MaxTrades && _lastEntryPrice - price >= PipStep * step)
{
BuyMarket();
_tradeCount++;
_avgPrice = (_avgPrice * (_tradeCount - 1) + price) / _tradeCount;
_lastEntryPrice = price;
}
else if (!_isLong && _tradeCount < MaxTrades && price - _lastEntryPrice >= PipStep * step)
{
SellMarket();
_tradeCount++;
_avgPrice = (_avgPrice * (_tradeCount - 1) + price) / _tradeCount;
_lastEntryPrice = price;
}
}
private void CloseAll()
{
var pos = Position;
if (pos > 0)
{
// Close long: sell abs(pos) times
for (var i = 0; i < (int)Math.Abs(pos); i++)
SellMarket();
}
else if (pos < 0)
{
// Close short: buy abs(pos) times
for (var i = 0; i < (int)Math.Abs(pos); i++)
BuyMarket();
}
ResetState();
}
private void ResetState()
{
_tradeCount = 0;
_lastEntryPrice = 0m;
_avgPrice = 0m;
_isLong = StartLong;
}
}
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.Strategies import Strategy
class ilan_16_dynamic_strategy(Strategy):
def __init__(self):
super(ilan_16_dynamic_strategy, self).__init__()
self._pip_step = self.Param("PipStep", 50000.0) \
.SetGreaterThanZero() \
.SetDisplay("Pip Step", "Distance in price steps between grid levels", "Trading") \
.SetOptimize(10000.0, 100000.0, 10000.0)
self._take_profit = self.Param("TakeProfit", 30000.0) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit", "Profit target from average price in price steps", "Trading") \
.SetOptimize(10000.0, 100000.0, 10000.0)
self._max_trades = self.Param("MaxTrades", 3) \
.SetGreaterThanZero() \
.SetDisplay("Max Trades", "Maximum number of averaging entries", "Trading") \
.SetOptimize(2, 10, 1)
self._start_long = self.Param("StartLong", True) \
.SetDisplay("Start Long", "Open first trade as long", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._trade_count = 0
self._last_entry_price = 0.0
self._avg_price = 0.0
self._is_long = True
@property
def pip_step(self):
return self._pip_step.Value
@property
def take_profit(self):
return self._take_profit.Value
@property
def max_trades(self):
return self._max_trades.Value
@property
def start_long(self):
return self._start_long.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ilan_16_dynamic_strategy, self).OnReseted()
self._reset_state()
def _reset_state(self):
self._trade_count = 0
self._last_entry_price = 0.0
self._avg_price = 0.0
self._is_long = self.start_long
def OnStarted2(self, time):
super(ilan_16_dynamic_strategy, self).OnStarted2(time)
self._is_long = self.start_long
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
step = self.Security.PriceStep if self.Security.PriceStep is not None else 1.0
step = float(step)
price = float(candle.ClosePrice)
# No position - open initial entry
if self.Position == 0:
if self._is_long:
self.BuyMarket()
else:
self.SellMarket()
self._trade_count = 1
self._last_entry_price = price
self._avg_price = price
return
# Check take profit: close entire basket
if self._is_long and price >= self._avg_price + float(self.take_profit) * step:
self._close_all()
return
elif not self._is_long and price <= self._avg_price - float(self.take_profit) * step:
self._close_all()
return
# Check for grid averaging entry (price moved against us)
if self._is_long and self._trade_count < self.max_trades and self._last_entry_price - price >= float(self.pip_step) * step:
self.BuyMarket()
self._trade_count += 1
self._avg_price = (self._avg_price * (self._trade_count - 1) + price) / self._trade_count
self._last_entry_price = price
elif not self._is_long and self._trade_count < self.max_trades and price - self._last_entry_price >= float(self.pip_step) * step:
self.SellMarket()
self._trade_count += 1
self._avg_price = (self._avg_price * (self._trade_count - 1) + price) / self._trade_count
self._last_entry_price = price
def _close_all(self):
pos = self.Position
if pos > 0:
for i in range(int(abs(pos))):
self.SellMarket()
elif pos < 0:
for i in range(int(abs(pos))):
self.BuyMarket()
self._reset_state()
def CreateClone(self):
return ilan_16_dynamic_strategy()