Para Max V2 é uma porta do MetaTrader 4 consultor especialista for_max_v2.mq4. A estratégia espera por padrões específicos de consumo de duas velas e, em seguida, coloca um par simétrico de ordens de compra e parada de venda em torno da vela mais recente. Depois que uma ordem de rompimento é preenchida, a ordem pendente oposta é removida e a posição é gerenciada com paradas fixas, níveis de take-profit opcionais e uma rotina de rastreamento que primeiro bloqueia um pequeno lucro no ponto de equilíbrio e depois segue o preço.
Lógica estratégica
Detecção de padrões envolventes
O consultor especialista original expõe dois blocos de entrada e ambos são preservados:
Configuração tipo 1 – verifica as velas Max Search anteriores (pulando a barra atual) e espera que a mínima mais baixa dentro desse intervalo ocorra há duas barras ou que a máxima mais alta ocorra há duas barras. Quando isso acontece, a vela duas barras atrás deve engolir a vela anterior (máxima mais alta e mínima mais baixa). A configuração envolve a vela acabada mais recente.
Configuração tipo 2 – também verifica as velas Max Search anteriores, mas procura o extremo que apareceu uma barra atrás. Além disso, a vela uma barra atrás deve engolir a vela duas barras atrás. Um straddle é então colocado em torno da vela mais recente. Ambas as configurações podem coexistir; cada um gerencia seus próprios pedidos pendentes e relógio de vencimento.
Colocação de pedido pendente
Preços de entrada – as ordens de compra são colocadas na máxima da vela anterior mais Gap Points, as ordens de stop de venda na mínima da vela anterior menos Gap Points.
Stop-loss – para o Tipo 1, o stop longo está ancorado na mínima da vela duas barras atrás (menos o gap) e o stop curto na máxima dessa vela (mais o gap). O tipo 2 usa a vela anterior para ambos os lados.
Realização de lucro – opcional. Os alvos longos somam Gap Points + Buy Take Profit Points à máxima anterior e os vendidos subtraem Gap Points + Sell Take Profit Points da mínima anterior. Definir as entradas de lucro como 0 desativa as respectivas metas.
Expiração – cada straddle carrega um carimbo de data/hora de validade calculado como Order Expiry (bars) multiplicado pelo período de vela configurado. Se as ordens pendentes ainda estiverem funcionando quando o carimbo de data/hora for atingido, ambos os lados serão cancelados.
Gestão de posição
Assim que um buy-stop for preenchido, quaisquer ordens de sell-stop restantes de qualquer configuração serão canceladas; a regra simétrica se aplica após uma entrada curta.
Stops e metas são monitorados em velas concluídas. Se a mínima de uma vela atingir o stop longo (ou a máxima atingir o stop curto), a posição será fechada com uma ordem de mercado. A mesma abordagem é usada para os níveis de take-profit.
A rotina de ponto de equilíbrio (Break-even Trigger e Break-even Offset) move o stop para o preço de entrada mais/menos o deslocamento configurado assim que a posição avança pelo valor de gatilho.
O bloco final mantém os pontos de parada Long/Short Trailing Buffer longe da melhor excursão, mas somente depois que o preço tiver percorrido uma distância suficiente (e, opcionalmente, somente depois que a negociação já for lucrativa). Trailing Step evita ajustes excessivamente frequentes, exigindo uma melhoria mínima antes que o stop seja apertado novamente.
Parâmetros
Volume – volume da ordem para cada ordem stop pendente.
Buy Take Profit (pontos) – distância em pontos usada para calcular o longo take-profit (definido como 0 para desativar).
Sell Take Profit (pontos) – distância em pontos usada para calcular o short take-profit (definido como 0 para desativar).
Gap (pontos) – buffer adicionado aos máximos/mínimos antes de colocar entradas de stop e dobrado na distância de take-profit.
Profundidade de pesquisa – número de velas concluídas digitalizadas ao verificar configurações de engolfamento Tipo 1 e Tipo 2.
Expiração do pedido (barras) – número de comprimentos de vela que um straddle pendente permanece ativo antes de ambos os lados serem cancelados.
Gatilho do ponto de equilíbrio (pontos) – limite de lucro que ativa o ajuste do ponto de equilíbrio.
Compensação do ponto de equilíbrio (pontos) – buffer adicional adicionado ao preço de entrada quando o stop de equilíbrio é colocado.
Long Trailing Buffer (pontos) – distância final para posições longas quando o ponto de equilíbrio for atingido.
Short Trailing Buffer (pontos) – distância final para posições curtas quando o ponto de equilíbrio for atingido.
Trailing Step (pontos) – melhoria mínima na localização da parada necessária antes de atualizar o trailing stop novamente.
Trail Only After Profit – se habilitado, o trailing espera até que a posição ultrapasse o buffer antes de ser ativado.
Tipo de vela – período de tempo das velas usadas para detecção de padrões, vencimento de pedidos e processamento de saída.
Notas adicionais
As compensações de preço expressas em “pontos” dependem do PriceStep do título. Símbolos com cinco (ou três) casas decimais são convertidos automaticamente em tamanhos de pip fracionários, assim como em MetaTrader.
Stop Loss e Take Profits são executados por meio de ordens de mercado dentro da estratégia para espelhar o comportamento do EA de gerenciar níveis em velas fechadas.
A estratégia não implementa a função vhod_3 não utilizada da fonte original; apenas os dois blocos de entrada ativos foram portados.
Este pacote contém apenas a implementação C#; nenhuma versão do Python é fornecida.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// For Max V2: N-bar engulfing breakout with EMA filter and ATR stops.
/// </summary>
public class ForMaxV2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _lookback;
private decimal _entryPrice;
private decimal _prevHigh;
private decimal _prevLow;
private readonly decimal[] _highs = new decimal[10];
private readonly decimal[] _lows = new decimal[10];
private int _barCount;
public ForMaxV2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 30)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
_lookback = Param(nameof(Lookback), 10)
.SetDisplay("Lookback", "N-bar channel lookback.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_barCount = 0;
_prevHigh = 0;
_prevLow = 0;
Array.Clear(_highs, 0, _highs.Length);
Array.Clear(_lows, 0, _lows.Length);
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_barCount = 0;
_prevHigh = 0;
_prevLow = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
var len = Math.Min(Lookback, _highs.Length);
var idx = _barCount % len;
_highs[idx] = candle.HighPrice;
_lows[idx] = candle.LowPrice;
_barCount++;
if (_barCount < len || atrVal <= 0)
return;
var high = decimal.MinValue;
var low = decimal.MaxValue;
for (var i = 0; i < len; i++)
{
if (_highs[i] > high) high = _highs[i];
if (_lows[i] < low) low = _lows[i];
}
var close = candle.ClosePrice;
if (_prevHigh == 0 || _prevLow == 0)
{
_prevHigh = high;
_prevLow = low;
return;
}
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > _prevHigh && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < _prevLow && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevHigh = high;
_prevLow = low;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class for_max_v2_strategy(Strategy):
def __init__(self):
super(for_max_v2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 30) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._lookback = self.Param("Lookback", 10) \
.SetDisplay("Lookback", "N-bar channel lookback.", "Indicators")
self._entry_price = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._bar_count = 0
self._highs = []
self._lows = []
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def Lookback(self):
return self._lookback.Value
def OnStarted2(self, time):
super(for_max_v2_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._bar_count = 0
self._prev_high = 0.0
self._prev_low = 0.0
self._highs = [0.0] * 10
self._lows = [0.0] * 10
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
length = min(self.Lookback, 10)
idx = self._bar_count % length
self._highs[idx] = float(candle.HighPrice)
self._lows[idx] = float(candle.LowPrice)
self._bar_count += 1
if self._bar_count < length or av <= 0:
return
high = max(self._highs[i] for i in range(length))
low = min(self._lows[i] for i in range(length))
close = float(candle.ClosePrice)
if self._prev_high == 0 or self._prev_low == 0:
self._prev_high = high
self._prev_low = low
return
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 1.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 1.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close > self._prev_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._prev_low and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_high = high
self._prev_low = low
def OnReseted(self):
super(for_max_v2_strategy, self).OnReseted()
self._entry_price = 0.0
self._bar_count = 0
self._prev_high = 0.0
self._prev_low = 0.0
self._highs = []
self._lows = []
def CreateClone(self):
return for_max_v2_strategy()