La estrategia CCI and Martin busca reversiones bruscas después de una secuencia corta bajista o alcista y confirma el movimiento con el Índice de Canal de Materias Primas (CCI). La lógica replica el asesor experto original de MetaTrader 5 mientras usa la API de alto nivel de StockSharp. La estrategia trabaja solo con velas terminadas y puede operar en cualquier instrumento para el que estén disponibles los valores de CCI y los pasos de precio.
Reglas de Trading
Configuración alcista
Las velas -2 y -1 deben ser ambas bajistas (apertura mayor que cierre).
La vela 0 debe cerrar por encima de su apertura y por encima de la apertura de la vela -1.
El CCI en la vela -1 debe estar por debajo de +5, por debajo del valor de la vela -2, y tanto -2 como -3 deben mostrar una secuencia decreciente. El CCI actual (vela 0) debe girar hacia arriba por encima del valor anterior.
Cuando todas las condiciones se cumplen y no hay posición abierta, la estrategia entra en un trade largo.
Configuración bajista
Las velas -2 y -1 deben ser ambas alcistas (apertura menor que cierre).
La vela 0 debe cerrar por debajo de su apertura y por debajo de la apertura de la vela -1.
El CCI en la vela -1 debe estar por encima de -5, por encima del valor de la vela -2, y tanto -2 como -3 deben formar una secuencia creciente. El CCI actual (vela 0) debe girar hacia abajo por debajo del valor anterior.
Cuando todas las condiciones se cumplen y no hay posición abierta, la estrategia entra en un trade corto.
El algoritmo monitorea solo velas completadas. La implementación MQL original esperaba 40 segundos después de la apertura del minuto para evitar señales prematuras; el uso de velas terminadas hace innecesario este filtro.
Gestión de Riesgo
Las distancias de stop-loss y take-profit se definen en pips. Se convierten a offsets de precio multiplicando el paso de precio del instrumento por diez cuando el paso corresponde a una cotización de 3 o 5 dígitos, reflejando el cálculo original de pips.
El trailing stop se activa después de que el precio avanza la distancia del trailing stop más el paso de trailing. El stop se mueve entonces para mantener la distancia de trailing y solo avanza cuando la mejora de precio supera el paso configurado.
Si el stop-loss o el take-profit se establece en cero, la salida respectiva se deshabilita. El trailing requiere que tanto la distancia del stop como el paso sean positivos.
Gestión de Volumen
Dos motores opcionales de dimensionamiento de posición pueden alterar el tamaño del lote después de cada trade.
Escalado Martingala multiplica el volumen actual por el coeficiente martingala una vez que el número de pérdidas consecutivas alcanza el disparador. El escalado se detiene después del número configurado de pasos martingala. Cualquier trade rentable restablece el volumen al valor inicial.
Ajustes por pasos incrementan el volumen en una cantidad fija ya sea después de pérdidas o después de ganancias, dependiendo del modo seleccionado. El incremento se normaliza al paso de volumen del instrumento y está limitado por el parámetro de volumen máximo. Cuando se supera el límite o un trade no cumple la condición de disparador, el volumen vuelve al tamaño inicial.
El asesor experto original prohíbe habilitar la lógica martingala y de pasos simultáneamente; el port en C# aplica la misma restricción.
Parámetros
CandleType – serie de velas usada para análisis.
CciPeriod – longitud de promedio para el Índice de Canal de Materias Primas.
InitialVolume – tamaño base de la orden antes de cualquier escalado.
StopLossPips – distancia del stop-loss expresada en pips.
TakeProfitPips – distancia del take-profit expresada en pips.
TrailingStopPips – distancia del trailing stop en pips (0 deshabilita el trailing).
TrailingStepPips – mejora de precio mínima requerida antes de que el trailing stop se mueva.
EnableMartingale – activa el escalado estilo martingala después de pérdidas.
MartingaleCoefficient – multiplicador aplicado al volumen actual para trades martingala.
MartingaleTriggerLosses – número de trades perdedores consecutivos necesarios antes del escalado.
MartingaleMaxSteps – número máximo de multiplicaciones martingala.
EnableStepAdjustments – habilita incrementos de volumen basados en pasos.
StepVolumeIncrement – incremento absoluto aplicado cuando la regla de pasos se activa.
StepVolumeMax – límite superior para el volumen basado en pasos.
StepAdjustmentMode – selecciona si el incremento por pasos se activa después de una pérdida o después de una ganancia.
Notas
La estrategia asume que las órdenes de mercado se ejecutan cerca del precio solicitado. La lógica protectora recalcula los stops en cada vela terminada para emular el trailing basado en ticks del EA original.
Si el paso de precio del instrumento no corresponde a la cotización FX clásica, la conversión de pips sigue funcionando, pero las distancias basadas en pips pueden representar valores monetarios 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()