La Estrategia Exp Amstell es un sistema de comercio en red convertido del MetaTrader 4 asesor experto original exp_Amstell.mq4. Coloca continuamente órdenes de mercado de compra y venta cada vez que el precio se aleja una cantidad configurable de puntos del cumplimiento más reciente. Cada operación individual se gestiona de forma independiente: una vez que el mercado se mueve a la distancia de obtención de beneficios especificada, la estrategia envía una orden de compensación para capturar las ganancias de esa única capa.
A diferencia de los sistemas impulsados por el impulso, Exp Amstell permanece activo en todo momento. No espera confirmaciones de indicadores y, en cambio, acumula posiciones en ambos lados del libro a medida que el mercado oscila. Este comportamiento lo hace muy sensible a las distancias de los puntos elegidos y al tamaño de cada orden.
Lógica de trading
Procesamiento basado en ticks. La estrategia se suscribe a cotizaciones de nivel 1 y reacciona a cada cambio en la mejor oferta y la mejor demanda, al igual que la función start() en el código original MQL.
Stacks largos y cortos independientes. Se permiten órdenes de compra cuando no hay operaciones largas abiertas o cuando el precio de venta ha bajado al menos la distancia de reingreso desde la última entrada larga. Las órdenes de venta utilizan la condición simétrica en el precio de oferta.
Obtención de beneficios por operación. Cada capa abierta se rastrea por separado. Cuando la oferta (para largos) o la demanda (para cortos) avanza por los puntos de toma de ganancias configurados, la estrategia cierra solo esa capa con una orden de mercado. Otras capas permanecen intactas.
Emulación FIFO. Las operaciones ejecutadas se registran en orden FIFO para reproducir la contabilidad basada en tickets que MetaTrader aplica a las posiciones cubiertas. Esto garantiza que los rellenos parciales reduzcan primero la capa pendiente más antigua.
Conocimiento neto de la cartera. StockSharp mantiene posiciones netas. Si una nueva orden de compra compensa una capa corta abierta, la estrategia elimina esa posición corta de su pila sintética antes de registrar el resto como una nueva posición larga.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
TradeVolume
decimal
0.1
Volumen de cada orden de mercado que abre una nueva capa de la grilla.
TakeProfitPoints
int
30
Distancia en MetaTrader puntos que debe cubrir el precio antes de cerrar una capa individual.
ReentryDistancePoints
int
10
Distancia mínima de puntos desde la última entrada antes de agregar otra orden en el mismo lado.
La estrategia convierte automáticamente puntos en incrementos de precios reales utilizando el PriceStep del instrumento. Las cotizaciones de cinco y tres dígitos reciben el multiplicador específico de MetaTrader, de modo que 1 point sea igual a 0.0001 (o 0.01 para símbolos de estilo JPY).
Notas de implementación
Los datos de nivel 1 son suficientes; no se requiere suscripción a velas. La estrategia declara esto anulando GetWorkingSecurities() y solicitando (Security, DataType.Level1).
StartProtection() se invoca durante OnStarted para garantizar que el corredor cierre cualquier posición sobrante si la estrategia se detiene inesperadamente.
Todos los comentarios dentro del archivo C# permanecen en inglés, cumpliendo con las pautas del proyecto.
Debido a que StockSharp utiliza posiciones netas, el puerto no puede mantener abiertas las compras y ventas opuestas simultáneamente. Cuando ambas partes comercian al mismo tiempo, la nueva orden aplanará la exposición existente antes de crear una nueva capa.
Consejos de uso
Calibre las distancias de los puntos. Las distancias más pequeñas crean redes más densas que pueden realizar operaciones excesivas en mercados volátiles. Las distancias más grandes reducen la actividad pero aumentan la reducción por capa.
Tamaño de los pedidos con prudencia. Los sistemas de red acumulan exposición rápidamente. Pruebe volúmenes conservadores en Designer/Backtester antes de cambiar al comercio real.
Considere controles de riesgo manuales. El experto original no tiene un límite de pérdidas global. Combine la estrategia con protecciones a nivel de cartera para limitar el riesgo de cola.
Monitorear la calidad de ejecución. El algoritmo supone que las órdenes de mercado se ejecutan cerca de la mejor oferta/demanda. El deslizamiento afecta directamente las distancias de obtención de beneficios alcanzadas.
Fuente
Convertido de MQL/9027/exp_Amstell.mq4.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Exp Amstell: Grid-style strategy that scales into positions
/// on ATR-based price movements and closes on profit targets.
/// </summary>
public class ExpAmstellStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _emaLength;
private decimal _entryPrice;
private decimal _prevEma;
private int _gridCount;
public ExpAmstellStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period for grid distance.", "Indicators");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "EMA period for trend.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevEma = 0;
_gridCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_prevEma = 0;
_gridCount = 0;
var atr = new AverageTrueRange { Length = AtrLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrVal, decimal emaVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0 || _prevEma == 0)
{
_prevEma = emaVal;
return;
}
var close = candle.ClosePrice;
// Grid exit: take profit at 1.5 ATR or stop at 3 ATR
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (close <= _entryPrice - atrVal * 3m)
{
SellMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (_gridCount < 3 && close <= _entryPrice - atrVal)
{
// Scale in: add to position on pullback
_entryPrice = (_entryPrice + close) / 2m;
_gridCount++;
BuyMarket();
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (close >= _entryPrice + atrVal * 3m)
{
BuyMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (_gridCount < 3 && close >= _entryPrice + atrVal)
{
_entryPrice = (_entryPrice + close) / 2m;
_gridCount++;
SellMarket();
}
}
// Entry: EMA direction determines initial direction
if (Position == 0)
{
if (close > emaVal && emaVal > _prevEma)
{
_entryPrice = close;
_gridCount = 0;
BuyMarket();
}
else if (close < emaVal && emaVal < _prevEma)
{
_entryPrice = close;
_gridCount = 0;
SellMarket();
}
}
_prevEma = emaVal;
}
}
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 AverageTrueRange, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_amstell_strategy(Strategy):
"""
Grid-style strategy that scales into positions on ATR-based price movements.
Enters based on EMA direction, scales in on pullbacks, exits on ATR-based TP/SL.
"""
def __init__(self):
super(exp_amstell_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period for grid distance", "Indicators")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period for trend", "Indicators")
self._entry_price = 0.0
self._prev_ema = 0.0
self._grid_count = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_amstell_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_ema = 0.0
self._grid_count = 0
def OnStarted2(self, time):
super(exp_amstell_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._prev_ema = 0.0
self._grid_count = 0
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(atr, 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, atr_val, ema_val):
if candle.State != CandleStates.Finished:
return
atr_val = float(atr_val)
ema_val = float(ema_val)
if atr_val <= 0 or self._prev_ema == 0:
self._prev_ema = ema_val
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + atr_val * 1.5:
self.SellMarket()
self._entry_price = 0.0
self._grid_count = 0
elif close <= self._entry_price - atr_val * 3.0:
self.SellMarket()
self._entry_price = 0.0
self._grid_count = 0
elif self._grid_count < 3 and close <= self._entry_price - atr_val:
self._entry_price = (self._entry_price + close) / 2.0
self._grid_count += 1
self.BuyMarket()
elif self.Position < 0:
if close <= self._entry_price - atr_val * 1.5:
self.BuyMarket()
self._entry_price = 0.0
self._grid_count = 0
elif close >= self._entry_price + atr_val * 3.0:
self.BuyMarket()
self._entry_price = 0.0
self._grid_count = 0
elif self._grid_count < 3 and close >= self._entry_price + atr_val:
self._entry_price = (self._entry_price + close) / 2.0
self._grid_count += 1
self.SellMarket()
if self.Position == 0:
if close > ema_val and ema_val > self._prev_ema:
self._entry_price = close
self._grid_count = 0
self.BuyMarket()
elif close < ema_val and ema_val < self._prev_ema:
self._entry_price = close
self._grid_count = 0
self.SellMarket()
self._prev_ema = ema_val
def CreateClone(self):
return exp_amstell_strategy()