A estratégia CCI and Martin busca reversões bruscas após uma curta sequência baixista ou altista e confirma o movimento com o Índice de Canal de Commodities (CCI). A lógica replica o consultor especialista original do MetaTrader 5 enquanto usa a API de alto nível do StockSharp. A estratégia trabalha apenas com velas terminadas e pode operar em qualquer instrumento para o qual valores de CCI e passos de preço estejam disponíveis.
Regras de Trading
Configuração altista
As velas -2 e -1 devem ser ambas baixistas (abertura maior que fechamento).
A vela 0 deve fechar acima de sua abertura e acima da abertura da vela -1.
O CCI na vela -1 deve estar abaixo de +5, abaixo do valor da vela -2, e tanto -2 quanto -3 devem mostrar uma sequência decrescente. O CCI atual (vela 0) deve girar para cima acima do valor anterior.
Quando todas as condições se cumprem e nenhuma posição está aberta, a estratégia entra em um trade comprado.
Configuração baixista
As velas -2 e -1 devem ser ambas altistas (abertura menor que fechamento).
A vela 0 deve fechar abaixo de sua abertura e abaixo da abertura da vela -1.
O CCI na vela -1 deve estar acima de -5, acima do valor da vela -2, e tanto -2 quanto -3 devem formar uma sequência crescente. O CCI atual (vela 0) deve girar para baixo abaixo do valor anterior.
Quando todas as condições se cumprem e nenhuma posição está aberta, a estratégia entra em um trade vendido.
O algoritmo monitora apenas velas completadas. A implementação MQL original esperava 40 segundos após a abertura do minuto para evitar sinais prematuros; o uso de velas terminadas torna este filtro desnecessário.
Gestão de Risco
As distâncias de stop-loss e take-profit são definidas em pips. São convertidas em offsets de preço multiplicando o passo de preço do instrumento por dez quando o passo corresponde a uma cotação de 3 ou 5 dígitos, refletindo o cálculo original de pips.
O trailing stop se torna ativo depois que o preço avança pela distância do trailing stop mais o passo de trailing. O stop é então movido para manter a distância de trailing e apenas avança quando a melhoria de preço supera o passo configurado.
Se o stop-loss ou o take-profit for definido como zero, a saída respectiva é desabilitada. O trailing requer que tanto a distância do stop quanto o passo sejam positivos.
Gestão de Volume
Dois motores opcionais de dimensionamento de posição podem alterar o tamanho do lote após cada trade.
Escalonamento Martingale multiplica o volume atual pelo coeficiente martingale assim que o número de perdas consecutivas atinge o gatilho. O escalonamento para após o número configurado de passos martingale. Qualquer trade lucrativo redefine o volume ao valor inicial.
Ajustes por passos incrementam o volume em uma quantidade fixa, seja após perdas ou após lucros, dependendo do modo selecionado. O incremento é normalizado ao passo de volume do instrumento e limitado pelo parâmetro de volume máximo. Quando o limite é excedido ou um trade não cumpre a condição de gatilho, o volume retorna ao tamanho inicial.
O consultor especialista original proíbe habilitar a lógica martingale e de passos simultaneamente; o port em C# aplica a mesma restrição.
Parâmetros
CandleType – série de velas usada para análise.
CciPeriod – comprimento de média para o Índice de Canal de Commodities.
InitialVolume – tamanho base da ordem antes de qualquer escalonamento.
StopLossPips – distância do stop-loss expressa em pips.
TakeProfitPips – distância do take-profit expressa em pips.
TrailingStopPips – distância do trailing stop em pips (0 desabilita o trailing).
TrailingStepPips – melhoria mínima de preço necessária antes que o trailing stop se mova.
EnableMartingale – ativa o escalonamento estilo martingale após perdas.
MartingaleCoefficient – multiplicador aplicado ao volume atual para trades martingale.
MartingaleTriggerLosses – número de trades perdedores consecutivos necessários antes do escalonamento.
MartingaleMaxSteps – número máximo de multiplicações martingale.
EnableStepAdjustments – habilita incrementos de volume baseados em passos.
StepVolumeIncrement – incremento absoluto aplicado quando a regra de passos é ativada.
StepVolumeMax – limite superior para o volume baseado em passos.
StepAdjustmentMode – seleciona se o incremento por passos é ativado após uma perda ou após um lucro.
Notas
A estratégia assume que as ordens de mercado são executadas próximas ao preço solicitado. A lógica protetora recalcula os stops em cada vela terminada para emular o trailing baseado em ticks do EA original.
Se o passo de preço do instrumento não corresponde à cotação FX clássica, a conversão de pips ainda funciona, mas as distâncias baseadas em pips podem representar valores monetários diferentes.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// CCI based strategy with martingale-style entry.
/// Buys when CCI crosses above oversold level, sells when CCI crosses below overbought level.
/// </summary>
public class CCIAndMartinStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<decimal> _oversold;
private decimal? _prevCci;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
public CCIAndMartinStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_cciPeriod = Param(nameof(CciPeriod), 27)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI indicator length", "Indicators");
_overbought = Param(nameof(Overbought), 100m)
.SetDisplay("Overbought", "CCI overbought level", "Indicators");
_oversold = Param(nameof(Oversold), -100m)
.SetDisplay("Oversold", "CCI oversold level", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCci = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = null;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(cci, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, cci);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevCci = cciValue;
return;
}
if (_prevCci == null)
{
_prevCci = cciValue;
return;
}
var prev = _prevCci.Value;
_prevCci = cciValue;
// Buy signal: CCI crosses above oversold level from below
if (prev < Oversold && cciValue >= Oversold && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Sell signal: CCI crosses below overbought level from above
else if (prev > Overbought && cciValue <= Overbought && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
}
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 CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class cci_and_martin_strategy(Strategy):
def __init__(self):
super(cci_and_martin_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._cci_period = self.Param("CciPeriod", 27) \
.SetDisplay("CCI Period", "CCI indicator length", "Indicators")
self._overbought = self.Param("Overbought", 100.0) \
.SetDisplay("Overbought", "CCI overbought level", "Indicators")
self._oversold = self.Param("Oversold", -100.0) \
.SetDisplay("Oversold", "CCI oversold level", "Indicators")
self._prev_cci = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def CciPeriod(self):
return self._cci_period.Value
@property
def Overbought(self):
return self._overbought.Value
@property
def Oversold(self):
return self._oversold.Value
def OnReseted(self):
super(cci_and_martin_strategy, self).OnReseted()
self._prev_cci = None
def OnStarted2(self, time):
super(cci_and_martin_strategy, self).OnStarted2(time)
self._prev_cci = None
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(cci, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def _on_process(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
cv = float(cci_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_cci = cv
return
if self._prev_cci is None:
self._prev_cci = cv
return
prev = self._prev_cci
self._prev_cci = cv
ob = float(self.Overbought)
os_level = float(self.Oversold)
# Buy signal: CCI crosses above oversold level from below
if prev < os_level and cv >= os_level and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Sell signal: CCI crosses below overbought level from above
elif prev > ob and cv <= ob and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return cci_and_martin_strategy()