La estrategia de reequilibrio de Grid es una versión StockSharp de alto nivel del asesor experto "Grid" de Mission Automate. La estrategia alterna entre ciclos de grilla largos y cortos y siempre mantiene una escalera de órdenes límite en la dirección activa. Una vez que la posición agregada alcanza un nivel común de toma de ganancias, el ciclo se cierra, se eliminan todas las órdenes pendientes y el siguiente ciclo comienza en la dirección opuesta.
como funciona
Inicio del ciclo – Cuando no hay posiciones ni órdenes pendientes, la estrategia abre una posición de mercado en la dirección definida por FirstTradeSide usando StartVolume lotes.
Colocación de la cuadrícula: después de cada orden ejecutada en la dirección activa, el algoritmo coloca una nueva orden límite a una distancia de GridStepPoints (convertida en precio por el instrumento PriceStep). El volumen del siguiente pedido es igual al volumen del último pedido ejecutado multiplicado por LotMultiplier.
Obtención de ganancias basada en el promedio: para cada orden ejecutada, se recalcula el precio de entrada promedio ponderado. La obtención de beneficios para toda la cesta se establece en el precio medio más/menos TargetPoints (también convertido mediante PriceStep). Los máximos y mínimos de las velas se utilizan para modelar el comportamiento de activación del corredor.
Finalización del ciclo: cuando se alcanza el nivel de obtención de beneficios, la estrategia cierra toda la posición con una orden de mercado, cancela las órdenes pendientes restantes, recuerda la dirección del ciclo finalizado y cambia la dirección del siguiente.
Parámetros
FirstTradeSide – dirección del primer ciclo (Buy o Sell). Cada ciclo completado cambia automáticamente la dirección.
StartVolume – tamaño de lote de la orden de mercado inicial en cada ciclo.
LotMultiplier: multiplicador aplicado al volumen de pedido ejecutado más reciente al preparar el siguiente nivel de la cuadrícula. Los valores mayores que uno crean una progresión similar a una martingala.
GridStepPoints – distancia entre niveles de la cuadrícula expresada en puntos. La estrategia lo multiplica por Security.PriceStep para obtener la diferencia de precio absoluta.
TargetPoints – distancia de obtención de beneficios desde el precio de entrada medio ponderado, medida en puntos.
CandleType: serie de velas utilizadas para monitorear los extremos de precios para activar salidas.
Gestión de riesgos y comportamiento.
No se utiliza ningún límite de pérdidas explícito; la red sigue agregando exposición mientras el mercado se mueve en contra de la posición.
Sólo hay una orden pendiente activa a la vez. Cuando se completa el pedido, se programa inmediatamente el siguiente nivel.
El ciclo no puede comenzar hasta que tanto la posición como la cola pendiente estén vacías y el instrumento tenga un PriceStep válido.
La conversión mantiene todos los cálculos dentro de la estrategia sin tocar colecciones globales o buffers de indicadores, siguiendo las reglas del proyecto.
Las órdenes pendientes se cancelan cada vez que finaliza un ciclo, lo que evita límites huérfanos de ciclos anteriores.
Notas
Todas las configuraciones basadas en puntos se convierten a precios con Security.PriceStep. Si el paso es cero la estrategia espera hasta que el instrumento lo proporcione.
La implementación se basa exclusivamente en el nivel alto API (SubscribeCandles, Bind, BuyMarket, SellMarket, BuyLimit, SellLimit) según sea necesario.
En esta tarea no se incluye intencionalmente una versión de Python.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Grid Rebalance strategy: RSI + EMA crossover-based.
/// Buys when close crosses above EMA with RSI confirmation.
/// Sells when close crosses below EMA with RSI confirmation.
/// </summary>
public class GridRebalanceStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _emaPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public GridRebalanceStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
decimal? prevClose = null;
decimal? prevEma = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ema, (candle, rsiVal, emaVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (prevClose.HasValue && prevEma.HasValue)
{
var crossUp = prevClose.Value <= prevEma.Value && close > emaVal;
var crossDown = prevClose.Value >= prevEma.Value && close < emaVal;
if (crossUp && rsiVal < 55m && Position <= 0)
BuyMarket();
else if (crossDown && rsiVal > 45m && Position >= 0)
SellMarket();
}
prevClose = close;
prevEma = emaVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
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 RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class grid_rebalance_strategy(Strategy):
"""
Grid Rebalance: RSI + EMA crossover strategy.
Buys when close crosses above EMA with RSI < 55.
Sells when close crosses below EMA with RSI > 45.
"""
def __init__(self):
super(grid_rebalance_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = None
self._prev_ema = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(grid_rebalance_strategy, self).OnReseted()
self._prev_close = None
self._prev_ema = None
def OnStarted2(self, time):
super(grid_rebalance_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, rsi_val, ema_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
rsi = float(rsi_val)
ema = float(ema_val)
if self._prev_close is not None and self._prev_ema is not None:
cross_up = self._prev_close <= self._prev_ema and close > ema
cross_down = self._prev_close >= self._prev_ema and close < ema
if cross_up and rsi < 55.0 and self.Position <= 0:
self.BuyMarket()
elif cross_down and rsi > 45.0 and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_ema = ema
def CreateClone(self):
return grid_rebalance_strategy()