A estratégia Double Up é uma porta direta do MetaTrader consultor especialista DoubleUp.mq4. Ele combina um oscilador de índice de canal de commodities (CCI) com a linha principal do indicador MACD para detectar condições extremas de momento e, em seguida, aplica um modelo de dimensionamento de posição estilo martingale. Sempre que ambos os osciladores atingem a mesma zona extrema, o algoritmo se prepara para uma negociação na direção oposta. Assim que CCI retorna ao ponto médio, a estratégia abre uma nova posição longa (após fechar as posições curtas existentes) ou abre uma nova posição curta (após fechar as posições longas existentes).
O volume de cada nova posição é baseado em uma progressão exponencial (baseVolume * 2^lossCounter). Saídas perdedoras consecutivas aumentam o expoente, enquanto uma saída lucrativa redefine a progressão de acordo com o buffer de espera acumulado. Este comportamento reflete a lógica de pirâmide no código original, onde as variáveis pos e wait controlam o multiplicador aplicado.
Lógica de negociação
Assine uma única série de velas e calcule a linha principal CCI (comprimento padrão 8) e MACD (padrão rápido 13, lento 33, sinal 2).
Multiplique a leitura de MACD por um milhão para que sua magnitude corresponda ao nível limite de CCI.
Quando ambos os osciladores excederem +Threshold, prepare a estratégia para uma futura entrada vendida. Quando ambos os osciladores caírem abaixo de -Threshold, prepare-os para uma entrada longa futura.
Uma entrada longa pendente é executada assim que o valor CCI volta abaixo de +Threshold. Uma entrada curta pendente é executada quando CCI fica abaixo de -Threshold enquanto o sinalizador curto está ativo, reproduzindo a ordem exata do código original.
Antes de abrir uma nova posição, o algoritmo fecha totalmente qualquer exposição oposta. A nova ordem é despachada somente após o fechamento de todas as negociações.
As negociações de saída são ordens de mercado geradas durante reversões de sinal. O lucro ou perda realizado de cada negociação fechada alimenta os contadores martingale.
Modelo de dimensionamento de posição
As saídas perdidas aumentam o contador de perdas. Depois que o contador atinge PreWait, seu valor é adicionado ao buffer de espera e o contador de perdas é zerado.
Uma saída lucrativa transfere o valor do buffer de espera (truncado) para o contador de perdas e limpa o buffer. Os tamanhos futuros de negociação, portanto, começam em 2^lossCounter lotes.
O buffer de espera é inicializado em InitialWait e é controlado pelas regras acima.
Parâmetros
Nome
Padrão
Descrição
CciPeriod
8
Período do Commodity Channel Index.
Threshold
230
Nível absoluto usado para detectar extremos do oscilador.
MacdFastPeriod
13
Comprimento EMA rápido do cálculo MACD.
MacdSlowPeriod
33
Comprimento EMA lento do cálculo MACD.
MacdSignalPeriod
2
Comprimento do sinal EMA, necessário para corresponder à assinatura da chamada MetaTrader.
BaseVolume
0,01
Iniciando o multiplicador de volume antes de aplicar o expoente martingale.
InitialWait
0
Valor inicial do buffer de espera (variável wait no script original).
PreWait
2
Número mínimo de saídas perdedoras consecutivas necessárias antes que o buffer de espera acumule o contador de perdas.
BackShift
0
Mudança histórica para leituras de indicadores. Apenas zero é suportado nesta porta.
CandleType
Período de 15 minutos
Tipo de vela solicitado no feed de dados. Ajuste para corresponder ao período do gráfico usado em MetaTrader.
Notas
A porta atualmente suporta apenas BackShift = 0, espelhando a configuração padrão do consultor especialista original.
Cada envio e fechamento de ordens utiliza ordens de mercado. Anexe proteções separadas (stop-loss, take-profit), se necessário.
Como a estratégia duplica o tamanho da posição após perdas nas negociações, certifique-se de que os limites de margem e os controles de risco sejam apropriados para o instrumento negociado.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class DoubleUpStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _cooldownRemaining;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DoubleUpStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = default;
_prevSlow = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevFast = fast;
_prevSlow = slow;
}
}