Ver en GitHub

Estrategia Starter V6 Mod (Conversión a StockSharp)

Descripción general

La estrategia Starter V6 Mod es una conversión al API de alto nivel de StockSharp del asesor experto de MetaTrader 5 Starter_v6mod. El sistema original combina un oscilador Laguerre RSI, dos medias móviles exponenciales, un filtro de índice de canal de materias primas y un módulo de gestión de posiciones en cuadrícula. Este port preserva la lógica de confirmación multicapa adaptando el manejo de posiciones, la gestión de capital y las acciones de protección al entorno StockSharp.

Lógica de trading

Indicadores

  • Proxy Laguerre RSI – modelado mediante un RSI normalizado de 14 periodos para emular la escala 0-1 utilizada por el oscilador Laguerre original. El par de niveles LevelDown / LevelUp (por defecto 0,15 / 0,85) define las zonas de sobrevendido y sobrecomprado.
  • EMA lenta (120) y EMA rápida (40) – calculadas sobre el precio mediano de la vela. Su desplazamiento relativo actúa como filtro de dirección de tendencia. El parámetro AngleThreshold convierte la diferencia de EMA en una distancia en ticks que condiciona las direcciones de trading.
  • Índice de Canal de Materias Primas (14) – confirma la dirección del impulso requiriendo valores negativos para entradas largas y positivos para entradas cortas.

Criterios de entrada

  1. Determinar el sesgo de tendencia a partir de la diferencia de EMA:
    • Si la EMA lenta menos la EMA rápida es menor que -AngleThreshold ticks, solo se pueden iniciar posiciones largas.
    • Si la diferencia es mayor que AngleThreshold, solo se pueden iniciar posiciones cortas.
    • De lo contrario, el mercado se considera lateral y no se abren nuevas posiciones.
  2. Cuando el sesgo de tendencia permite una dirección, verificar los filtros de oscilador e impulso:
    • Configuración larga – proxy Laguerre por debajo de LevelDown, EMA lenta < EMA lenta previa, EMA rápida < EMA rápida previa, y CCI < 0.
    • Configuración corta – proxy Laguerre por encima de LevelUp, EMA lenta > EMA lenta previa, EMA rápida > EMA rápida previa, y CCI > 0.
  3. Espaciado de cuadrícula – al apilar posiciones en la misma dirección, el precio actual debe estar al menos GridStepPips por debajo de la entrada larga más baja o por encima de la entrada corta más alta. Esto replica la lógica de promediado del EA original.
  4. Recuento de posiciones – el número total de entradas simultáneas en cuadrícula no puede superar MaxOpenTrades.

Criterios de salida

  • Salidas Laguerre – los largos cierran cuando el oscilador cruza por encima de LevelUp; los cortos cierran cuando cae por debajo de LevelDown.
  • Stop-loss / Take-profit – expresados en pips, convertidos a incrementos de precio del instrumento. La conversión rastrea el ajuste original para símbolos con precios de 3/5 decimales.
  • Trailing stop – se activa después de que el precio avanza (TrailingStopPips + TrailingStepPips) y luego sigue el precio con un desplazamiento de TrailingStopPips.
  • Protecciones del viernes – no se permiten nuevas operaciones después de las 18:00 (hora del terminal) y todas las posiciones abiertas se liquidan después de las 20:00.

Gestión de capital

  • Dimensionamiento de volumen – fijo (UseManualVolume = true) o basado en riesgo. En modo riesgo, el volumen es igual a (equity * RiskPercent) / (distancia StopLoss en unidades de precio).
  • Límite de capital – el trading se detiene cuando el capital actual cae por debajo de EquityCutoff.
  • Límite de pérdidas diarias – si la estrategia registra MaxLossesPerDay salidas perdedoras en la fecha actual, no se abren más posiciones.
  • Recuperación de pérdidas – después de cada salida perdedora, el tamaño de la siguiente posición se divide por DecreaseFactor^pérdidasHoy, replicando la lógica de escalado de posiciones original.

Notas de implementación

  • La conversión utiliza el pipeline SubscribeCandles().Bind(...) de alto nivel de StockSharp para transmitir velas terminadas y valores de indicadores a la lógica de decisión.
  • StockSharp no incluye un Laguerre RSI nativo, por lo que se utiliza un RSI normalizado como proxy. Los umbrales coinciden con el rango 0-1 de Laguerre.
  • El filtro de ángulo EMA se reproduce midiendo la diferencia entre los valores de EMA lenta y rápida en ticks, proporcionando una puerta direccional similar al indicador personalizado emaangle original.
  • La gestión manual de stops y trailing se realiza dentro de la rutina de procesamiento de velas para mantener paridad con las modificaciones de trailing de MQL.
  • El registro contable de la cuadrícula rastrea el precio de entrada promedio, el precio de llenado más bajo/alto y los niveles de trailing para emular el flujo de trabajo multi-posición de MQL mientras se trabaja dentro del modelo de posición agregada de StockSharp.

Parámetros

Nombre Por defecto Descripción
UseManualVolume false Alternar entre dimensionamiento de posición fijo y basado en riesgo.
ManualVolume 1 Volumen usado cuando el dimensionamiento manual está habilitado o el basado en riesgo no se puede calcular.
RiskPercent 5 Porcentaje del capital arriesgado por operación cuando el dimensionamiento automático está activo.
StopLossPips 35 Distancia del stop-loss en pips.
TakeProfitPips 10 Distancia del take-profit en pips.
TrailingStopPips 0 Distancia del trailing stop en pips (0 deshabilita el trailing).
TrailingStepPips 5 Avance mínimo antes de que el trailing stop comience a seguir el precio.
DecreaseFactor 1.6 Factor aplicado para reducir el tamaño después de cada pérdida.
MaxLossesPerDay 3 Máximo de salidas perdedoras permitidas por día calendario.
EquityCutoff 800 Umbral de capital que detiene las nuevas operaciones.
MaxOpenTrades 10 Número máximo de entradas simultáneas en cuadrícula.
GridStepPips 30 Espaciado mínimo entre entradas apiladas en la misma dirección.
LongEmaPeriod 120 Periodo de la EMA lenta.
ShortEmaPeriod 40 Periodo de la EMA rápida.
CciPeriod 14 Periodo del Índice de Canal de Materias Primas.
AngleThreshold 3 Umbral de diferencia de EMA expresado en ticks.
LevelUp 0.85 Nivel superior de Laguerre.
LevelDown 0.15 Nivel inferior de Laguerre.
CandleType 15m Marco temporal de velas utilizado para los cálculos.

Consejos de uso

  1. Configure el parámetro CandleType para que coincida con el marco temporal utilizado en la configuración original de MT5 (el EA a menudo se despliega en gráficos de 15 minutos).
  2. Alinee la configuración de riesgo con las especificaciones de la cuenta. Al usar dimensionamiento basado en riesgo, asegúrese de que StopLossPips refleje la volatilidad del instrumento, ya que afecta directamente al volumen calculado.
  3. Revise los horarios de trading de la bolsa. La protección del viernes integrada asume que el reloj del servidor se alinea con el cierre de sesión deseado.
  4. Habilite el dibujo en gráfico (a través de CreateChartArea) para visualizar EMA, proxy RSI, CCI y operaciones ejecutadas para depuración u optimización.
  5. Al trasladar conjuntos de parámetros de backtests de MT5, recuerde que el proxy RSI aproxima el oscilador Laguerre; puede ser necesario un ajuste fino de umbrales para coincidir con el timing de señal original.

Archivos

  • CS/StarterV6ModStrategy.cs – Implementación de la estrategia StockSharp.
  • README.md – Documentación en inglés (este archivo).
  • README_zh.md – Documentación en chino simplificado.
  • README_ru.md – Documentación en ruso.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Conversion of the Starter_v6mod Expert Advisor using the high-level StockSharp API.
/// </summary>
public class StarterV6ModStrategy : Strategy
{
	private readonly StrategyParam<bool> _useManualVolume;
	private readonly StrategyParam<decimal> _manualVolume;
	private readonly StrategyParam<decimal> _riskPercent;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<int> _trailingStopPips;
	private readonly StrategyParam<int> _trailingStepPips;
	private readonly StrategyParam<decimal> _decreaseFactor;
	private readonly StrategyParam<int> _maxLossesPerDay;
	private readonly StrategyParam<decimal> _equityCutoff;
	private readonly StrategyParam<int> _maxOpenTrades;
	private readonly StrategyParam<int> _gridStepPips;
	private readonly StrategyParam<int> _longEmaPeriod;
	private readonly StrategyParam<int> _shortEmaPeriod;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _angleThreshold;
	private readonly StrategyParam<decimal> _levelUp;
	private readonly StrategyParam<decimal> _levelDown;
	private readonly StrategyParam<DataType> _candleType;

	private ExponentialMovingAverage _longEma;
	private ExponentialMovingAverage _shortEma;
	private CommodityChannelIndex _cci;
	private RelativeStrengthIndex _laguerreProxy;

	private decimal? _prevLongEma;
	private decimal? _prevShortEma;

	/// <summary>
	/// Use manual volume instead of risk calculation.
	/// </summary>
	public bool UseManualVolume
	{
		get => _useManualVolume.Value;
		set => _useManualVolume.Value = value;
	}

	/// <summary>
	/// Manual volume for each new entry.
	/// </summary>
	public decimal ManualVolume
	{
		get => _manualVolume.Value;
		set => _manualVolume.Value = value;
	}

	/// <summary>
	/// Risk percentage used when position sizing is automatic.
	/// </summary>
	public decimal RiskPercent
	{
		get => _riskPercent.Value;
		set => _riskPercent.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in pips.
	/// </summary>
	public int StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in pips.
	/// </summary>
	public int TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

	/// <summary>
	/// Trailing stop distance in pips.
	/// </summary>
	public int TrailingStopPips
	{
		get => _trailingStopPips.Value;
		set => _trailingStopPips.Value = value;
	}

	/// <summary>
	/// Additional distance required before the trailing stop starts to follow the price.
	/// </summary>
	public int TrailingStepPips
	{
		get => _trailingStepPips.Value;
		set => _trailingStepPips.Value = value;
	}

	/// <summary>
	/// Multiplier used to reduce the position size after losses.
	/// </summary>
	public decimal DecreaseFactor
	{
		get => _decreaseFactor.Value;
		set => _decreaseFactor.Value = value;
	}

	/// <summary>
	/// Maximum number of losing trades allowed per day.
	/// </summary>
	public int MaxLossesPerDay
	{
		get => _maxLossesPerDay.Value;
		set => _maxLossesPerDay.Value = value;
	}

	/// <summary>
	/// Equity threshold below which the strategy stops opening new trades.
	/// </summary>
	public decimal EquityCutoff
	{
		get => _equityCutoff.Value;
		set => _equityCutoff.Value = value;
	}

	/// <summary>
	/// Maximum number of simultaneously opened grid positions.
	/// </summary>
	public int MaxOpenTrades
	{
		get => _maxOpenTrades.Value;
		set => _maxOpenTrades.Value = value;
	}

	/// <summary>
	/// Grid step in pips used when stacking positions.
	/// </summary>
	public int GridStepPips
	{
		get => _gridStepPips.Value;
		set => _gridStepPips.Value = value;
	}

	/// <summary>
	/// Period for the slow EMA trend filter.
	/// </summary>
	public int LongEmaPeriod
	{
		get => _longEmaPeriod.Value;
		set => _longEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period for the fast EMA trend filter.
	/// </summary>
	public int ShortEmaPeriod
	{
		get => _shortEmaPeriod.Value;
		set => _shortEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period for the CCI momentum filter.
	/// </summary>
	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	/// <summary>
	/// Threshold in ticks for the EMA spread trend detector.
	/// </summary>
	public decimal AngleThreshold
	{
		get => _angleThreshold.Value;
		set => _angleThreshold.Value = value;
	}

	/// <summary>
	/// Upper Laguerre RSI level.
	/// </summary>
	public decimal LevelUp
	{
		get => _levelUp.Value;
		set => _levelUp.Value = value;
	}

	/// <summary>
	/// Lower Laguerre RSI level.
	/// </summary>
	public decimal LevelDown
	{
		get => _levelDown.Value;
		set => _levelDown.Value = value;
	}

	/// <summary>
	/// Candle data type used by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="StarterV6ModStrategy"/> class.
	/// </summary>
	public StarterV6ModStrategy()
	{
		_useManualVolume = Param(nameof(UseManualVolume), true)
		.SetDisplay("Manual Volume", "Use manual volume instead of risk-based sizing", "Money Management");

		_manualVolume = Param(nameof(ManualVolume), 1m)
		.SetRange(0.01m, 100m)
		.SetDisplay("Volume", "Manual volume per trade", "Money Management")
		;

		_riskPercent = Param(nameof(RiskPercent), 5m)
		.SetRange(0.5m, 20m)
		.SetDisplay("Risk %", "Risk percentage when auto-sizing trades", "Money Management")
		;

		_stopLossPips = Param(nameof(StopLossPips), 35)
		.SetRange(0, 500)
		.SetDisplay("Stop Loss", "Stop-loss distance in pips", "Risk Management");

		_takeProfitPips = Param(nameof(TakeProfitPips), 10)
		.SetRange(0, 500)
		.SetDisplay("Take Profit", "Take-profit distance in pips", "Risk Management");

		_trailingStopPips = Param(nameof(TrailingStopPips), 0)
		.SetRange(0, 500)
		.SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Risk Management");

		_trailingStepPips = Param(nameof(TrailingStepPips), 5)
		.SetRange(0, 500)
		.SetDisplay("Trailing Step", "Additional distance before trailing activates", "Risk Management");

		_decreaseFactor = Param(nameof(DecreaseFactor), 1.6m)
		.SetRange(1m, 10m)
		.SetDisplay("Decrease Factor", "Volume reduction factor after losses", "Money Management");

		_maxLossesPerDay = Param(nameof(MaxLossesPerDay), 3)
		.SetRange(0, 20)
		.SetDisplay("Daily Loss Limit", "Maximum number of losses per day", "Risk Management");

		_equityCutoff = Param(nameof(EquityCutoff), 800m)
		.SetRange(0m, 1_000_000m)
		.SetDisplay("Equity Cutoff", "Stop trading if equity drops below this value", "Risk Management");

		_maxOpenTrades = Param(nameof(MaxOpenTrades), 10)
		.SetRange(1, 100)
		.SetDisplay("Max Trades", "Maximum simultaneous grid positions", "General");

		_gridStepPips = Param(nameof(GridStepPips), 30)
		.SetRange(0, 500)
		.SetDisplay("Grid Step", "Minimum pip distance between stacked entries", "General");

		_longEmaPeriod = Param(nameof(LongEmaPeriod), 120)
		.SetRange(10, 400)
		.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
		;

		_shortEmaPeriod = Param(nameof(ShortEmaPeriod), 40)
		.SetRange(5, 200)
		.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
		;

		_cciPeriod = Param(nameof(CciPeriod), 14)
		.SetRange(5, 100)
		.SetDisplay("CCI Period", "CCI indicator length", "Indicators")
		;

		_angleThreshold = Param(nameof(AngleThreshold), 3m)
		.SetRange(0m, 50m)
		.SetDisplay("Angle Threshold", "EMA spread threshold measured in ticks", "Indicators");

		_levelUp = Param(nameof(LevelUp), 0.85m)
		.SetRange(0.1m, 1m)
		.SetDisplay("Laguerre Up", "Upper Laguerre RSI level", "Indicators");

		_levelDown = Param(nameof(LevelDown), 0.15m)
		.SetRange(0m, 0.9m)
		.SetDisplay("Laguerre Down", "Lower Laguerre RSI level", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
		.SetDisplay("Candle Type", "Timeframe used for analysis", "General");
	}

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

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

		_longEma = null;
		_shortEma = null;
		_cci = null;
		_laguerreProxy = null;
		_prevLongEma = null;
		_prevShortEma = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_longEma = new ExponentialMovingAverage { Length = LongEmaPeriod };
		_shortEma = new ExponentialMovingAverage { Length = ShortEmaPeriod };
		_cci = new CommodityChannelIndex { Length = CciPeriod };
		_laguerreProxy = new RelativeStrengthIndex { Length = 14 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_longEma, _shortEma, _cci, _laguerreProxy, ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent));

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _longEma);
			DrawIndicator(area, _shortEma);
			DrawIndicator(area, _cci);
			DrawIndicator(area, _laguerreProxy);
			DrawOwnTrades(area);
		}
	}

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

		if (_prevLongEma is null || _prevShortEma is null)
		{
			_prevLongEma = longEmaValue;
			_prevShortEma = shortEmaValue;
			return;
		}

		if (Position != 0)
		{
			_prevLongEma = longEmaValue;
			_prevShortEma = shortEmaValue;
			return;
		}

		var laguerre = rsiValue / 100m;

		// Buy: RSI low (oversold), EMAs falling (pullback), CCI negative
		var buySignal = laguerre < LevelDown && cciValue < 0m;

		// Sell: RSI high (overbought), EMAs rising, CCI positive
		var sellSignal = laguerre > LevelUp && cciValue > 0m;

		if (buySignal)
			BuyMarket();
		else if (sellSignal)
			SellMarket();

		_prevLongEma = longEmaValue;
		_prevShortEma = shortEmaValue;
	}
}