A Estratégia Exp Amstell é um sistema de negociação em grade convertido do MetaTrader 4 consultor especialista original exp_Amstell.mq4. Ele coloca continuamente ordens de compra e venda de mercado sempre que o preço se afasta de um número configurável de pontos do preenchimento mais recente. Cada negociação individual é gerida de forma independente: uma vez que o mercado se move pela distância de lucro especificada, a estratégia envia uma ordem de compensação para capturar o lucro para essa única camada.
Ao contrário dos sistemas orientados por impulso, o Exp Amstell permanece ativo o tempo todo. Não espera pelas confirmações dos indicadores e, em vez disso, acumula posições em ambos os lados do livro à medida que o mercado oscila. Este comportamento o torna altamente sensível às distâncias dos pontos escolhidos e ao tamanho de cada ordem.
Lógica de negociação
Processamento baseado em ticks. A estratégia assina cotações de nível 1 e reage a cada mudança no melhor lance e no melhor pedido, assim como a função start() no código MQL original.
Pilhas longas e curtas independentes. Ordens de compra são permitidas quando não há negociações longas abertas ou quando o preço de venda caiu pelo menos na distância de reentrada da última entrada longa. As ordens de venda usam a condição simétrica no preço de oferta.
Take Profit por negociação. Cada camada aberta é rastreada separadamente. Quando o bid (para comprados) ou o pedido (para vendidos) avança pelos pontos de take-profit configurados, a estratégia fecha apenas essa camada com uma ordem de mercado. Outras camadas permanecem intocadas.
Emulação FIFO. As negociações executadas são registradas na ordem FIFO para reproduzir a contabilidade baseada em tickets que MetaTrader aplica às posições cobertas. Isto garante que os preenchimentos parciais reduzam primeiro a camada pendente mais antiga.
Reconhecimento do portfólio líquido. StockSharp mantém posições líquidas. Se uma nova ordem de compra compensar uma camada curta aberta, a estratégia removerá essa posição curta de sua pilha sintética antes de registrar o restante como uma nova posição longa.
Parâmetros
Nome
Tipo
Padrão
Descrição
TradeVolume
decimal
0.1
Volume de cada ordem de mercado que abre uma nova camada de grade.
TakeProfitPoints
int
30
Distância em MetaTrader pontos que devem ser cobertos pelo preço antes que uma camada individual seja fechada.
ReentryDistancePoints
int
10
Distância mínima de ponto da última entrada antes de adicionar outro pedido do mesmo lado.
A estratégia converte automaticamente os pontos em etapas de preço reais usando o PriceStep do instrumento. As cotações de cinco e três dígitos recebem o multiplicador específico de MetaTrader para que 1 point seja igual a 0.0001 (ou 0.01 para símbolos no estilo JPY).
Notas de implementação
Os dados do nível 1 são suficientes; nenhuma assinatura de vela é necessária. A estratégia declara isso substituindo GetWorkingSecurities() e solicitando (Security, DataType.Level1).
StartProtection() é invocado durante OnStarted para garantir que o corredor feche qualquer posição restante se a estratégia parar inesperadamente.
Todos os comentários dentro do arquivo C# permanecem em inglês, correspondendo às diretrizes do projeto.
Como StockSharp usa posições líquidas, a porta não pode manter compras e vendas opostas abertas simultaneamente. Quando ambos os lados negociam ao mesmo tempo, a ordem mais recente nivelará a exposição existente antes de criar uma nova camada.
Dicas de uso
Calibre as distâncias dos pontos. Distâncias menores criam grades mais densas que podem ser negociadas em excesso em mercados voláteis. Distâncias maiores reduzem a atividade, mas aumentam o rebaixamento por camada.
Dimensione os pedidos com prudência. Os sistemas de grade acumulam exposição rapidamente. Teste volumes conservadores no Designer/Backtester antes de mudar para negociação ao vivo.
Considere controles de risco manuais. O especialista original não tem stop-loss global. Combine a estratégia com proteções em nível de portfólio para limitar o risco.
Monitore a qualidade da execução. O algoritmo pressupõe que as ordens de mercado sejam preenchidas perto da melhor oferta/venda. A derrapagem afeta diretamente as distâncias de lucro alcançadas.
Fonte
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()