Ver en GitHub

Estrategia extrema del punto ideal

Sweet Spot Extreme es una adaptación directa del asesor experto MetaTrader 4 "Sweet_Spot_Extreme.mq4" construido sobre el API de alto nivel de StockSharp. La estrategia busca fuertes retrocesos dentro de una tendencia existente combinando dos promedios móviles exponenciales en velas de 15 minutos con un filtro de índice de canales de productos básicos (CCI) de 30 minutos. El tamaño de la posición refleja los controles de riesgo originales, incluida la reducción de lotes al estilo MetaTrader después de rachas perdedoras.

Lógica central

  1. Confirmación de pendiente de tendencia. El EMA principal (MaPeriod, predeterminado 85) y el cierre EMA (CloseMaPeriod, predeterminado 70) se alimentan con precios medios de 15 minutos. Una configuración larga requiere que ambas EMA tengan una pendiente ascendente; una configuración corta necesita que ambos se inclinen hacia abajo.
  2. CCI filtro de agotamiento. Una segunda suscripción de vela (de 30 minutos de forma predeterminada) alimenta el CciPeriod CCI. Las operaciones largas solo se activan cuando CCI cae por debajo de BuyCciLevel (-200), mientras que las posiciones cortas requieren CCI por encima de SellCciLevel (+200).
  3. Límite de pirámide. La posición neta agregada no puede exceder MaxTradesPerSymbol × volume. Cuando aparece una nueva señal, la estrategia cierra cualquier exposición opuesta y luego suma la capacidad permitida en la dirección de la señal.
  4. Salidas. Las posiciones se cierran cuando la tendencia EMA pierde su ventaja de pendiente (reflejando la condición MQL MA <= MAprevious) o después de que el precio recorre StopPoints puntos del instrumento a favor de la posición.

Gestión de riesgos

  • Volumen basado en riesgo. El tamaño del pedido predeterminado es Portfolio.CurrentValue × MaximumRisk ÷ price. Cuando falta información sobre el capital, el motor recurre al parámetro Lots (o la estrategia Volume).
  • Ajuste de racha de pérdidas. Después de dos o más operaciones perdedoras consecutivas, el tamaño de la nueva orden se reduce en volume × losses ÷ DecreaseFactor, coincidiendo con el MQL ayudante LotsOptimized().
  • Normalización. El volumen final está alineado con el VolumeStep del instrumento, delimitado por MinVolume y recortado por Security.MaxVolume cuando se proporciona.

Parámetros

Nombre Predeterminado Descripción
MaxTradesPerSymbol 3 Número máximo de entradas agregadas permitidas por dirección.
Lots 1 Tamaño de lote fijo alternativo cuando el capital de la cartera no está disponible.
MaximumRisk 0.05 Fracción de capital utilizada para dimensionar cada nueva operación.
DecreaseFactor 6 Divisor que reduce el siguiente pedido después de pérdidas consecutivas.
StopPoints 10 Distancia objetivo de beneficio en puntos del instrumento. Establezca en 0 para desactivar.
MaPeriod 85 EMA período aplicado a velas de 15 minutos para verificar la pendiente de la tendencia.
CloseMaPeriod 70 EMA período aplicado a velas de 15 minutos para el filtro de suavizado de cierre.
CciPeriod 12 Lookback utilizado para el filtro CCI de 30 minutos.
BuyCciLevel -200 Se requiere un umbral de sobreventa de CCI para entradas largas.
SellCciLevel 200 Se requiere un umbral de sobrecompra de CCI para entradas cortas.
MinVolume 0.1 Volumen mínimo permitido después de la normalización.
TrendCandleType 15m Tipo de vela utilizado para los cálculos de EMA (precio medio).
CciCandleType 30m Tipo de vela utilizado para el filtro CCI.

Notas y limitaciones

  • StockSharp opera en modo de compensación, por lo que varios tickets MT4 se representan como una única posición agregada. Por lo tanto, la guardia MaxTradesPerSymbol limita la exposición neta en lugar de contar las órdenes individuales.
  • El EA original se basó en AccountFreeMargin para el tamaño. Este puerto lo aproxima con Portfolio.CurrentValue; ajuste MaximumRisk o Lots para que se ajuste a las especificaciones del contrato de su corredor.
  • Asegúrese de que ambas suscripciones de velas estén habilitadas en la fuente de datos; de lo contrario, los filtros EMA o CCI nunca se formarán y la estrategia permanecerá inactiva.
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>
/// EMA slope + CCI filter strategy.
/// Buys when EMA slope is up and CCI is oversold.
/// Sells when EMA slope is down and CCI is overbought.
/// Exits on EMA slope reversal.
/// </summary>
public class SweetSpotExtremeStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _buyCciLevel;
	private readonly StrategyParam<decimal> _sellCciLevel;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevEma;
	private decimal _prevPrevEma;
	private bool _hasPrevEma;

	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	public decimal BuyCciLevel
	{
		get => _buyCciLevel.Value;
		set => _buyCciLevel.Value = value;
	}

	public decimal SellCciLevel
	{
		get => _sellCciLevel.Value;
		set => _sellCciLevel.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public SweetSpotExtremeStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetDisplay("EMA Period", "Trend EMA period", "Indicators");

		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetDisplay("CCI Period", "CCI lookback", "Indicators");

		_buyCciLevel = Param(nameof(BuyCciLevel), -50m)
			.SetDisplay("Buy CCI", "Oversold CCI level for buy", "Indicators");

		_sellCciLevel = Param(nameof(SellCciLevel), 50m)
			.SetDisplay("Sell CCI", "Overbought CCI level for sell", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle series", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevEma = 0m;
		_prevPrevEma = 0m;
		_hasPrevEma = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevEma = 0;
		_prevPrevEma = 0;
		_hasPrevEma = false;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var cci = new CommodityChannelIndex { Length = CciPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, cci, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal cciValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrevEma)
		{
			_prevPrevEma = emaValue;
			_prevEma = emaValue;
			_hasPrevEma = true;
			return;
		}

		var slopeUp = emaValue > _prevEma && _prevEma > _prevPrevEma;
		var slopeDown = emaValue < _prevEma && _prevEma < _prevPrevEma;

		// Entry
		if (slopeUp && cciValue <= BuyCciLevel && Position <= 0)
		{
			BuyMarket();
		}
		else if (slopeDown && cciValue >= SellCciLevel && Position >= 0)
		{
			SellMarket();
		}
		// Exit on slope reversal
		else if (Position > 0 && emaValue < _prevEma)
		{
			SellMarket();
		}
		else if (Position < 0 && emaValue > _prevEma)
		{
			BuyMarket();
		}

		_prevPrevEma = _prevEma;
		_prevEma = emaValue;
	}
}