Ver en GitHub

Estrategia de 10 Pips EURUSD

Descripción general

La Estrategia de 10 Pips EURUSD es un sistema de ruptura que reproduce la lógica del Expert Advisor original de MetaTrader. Observa la vela completada más reciente y coloca órdenes stop por encima y por debajo de ese rango. Las órdenes se dimensionan en pips, ajustadas al tamaño de tick del instrumento actual, y opcionalmente gestionadas por un trailing stop. La implementación usa suscripciones de velas de alto nivel de StockSharp junto con actualizaciones del libro de órdenes para mantener el comportamiento cercano a la versión MQL mientras permanece neutral con el broker.

Lógica de la estrategia

  1. Suscribirse al tipo de vela seleccionado y esperar hasta que una nueva barra se active.
  2. Capturar el máximo y mínimo de la vela anterior cuando esa barra termina. Las órdenes pendientes se cancelan en este momento porque el EA original las limita a una barra.
  3. En el primer tick de la nueva barra verificar que:
    • La apertura actual se encuentra dentro del rango de la vela anterior (filtrado de gaps).
    • El precio actual está al menos tres unidades pip alejado de ambos extremos (un indicador del nivel de stop del broker).
  4. Calcular el spread actual usando el mejor bid/ask. Si no hay datos de nivel 1, la estrategia recurre al tamaño del pip.
  5. Colocar dos órdenes stop pendientes:
    • Buy Stop: activación en máximo anterior + 2 × spread con stop loss por debajo del precio de entrada en StopLossPips y, si el trailing está desactivado, take profit en máximo anterior + 2 × spread + TakeProfitPips.
    • Sell Stop: activación en mínimo anterior − spread con niveles de salida simétricos.
  6. Tan pronto como la vela se completa, o ambas órdenes se llenan/cancelan, el proceso se repite para la siguiente barra.

Gestión de posición

  • Mientras una posición está abierta, la estrategia monitorea el mejor bid/ask en cada actualización del libro de órdenes.
  • Si el trailing está desactivado, la posición se cierra cuando el precio toca el stop o take-profit fijo.
  • Si el trailing está habilitado:
    • Para operaciones largas, el trailing stop se activa una vez que el precio avanza TrailingStopPips. El stop se establece en bid − TrailingStopPips y se mueve cada vez que el precio mejora al menos TrailingStepPips.
    • Para operaciones cortas, la lógica refleja el lado largo usando el precio ask.
  • Las salidas manuales reinician todos los niveles de protección y mantienen viva cualquier orden stop del lado contrario pendiente hasta que la vela termine, reproduciendo el comportamiento straddle del EA.

Parámetros

Nombre Predeterminado Descripción
Volume 0.01 Volumen de la orden en lotes (o unidades de contrato para símbolos no FX).
StopLossPips 50 Distancia entre la entrada y el stop de protección, expresada en pips.
TakeProfitPips 150 Distancia al take-profit en pips, usada solo cuando el trailing está desactivado.
UseTrailing false Habilita la lógica del trailing stop.
TrailingStopPips 50 Distancia inicial para el trailing stop, medida en pips.
TrailingStepPips 25 Ganancia mínima (en pips) requerida para mover un trailing stop activo.
CandleType marco temporal de 15 minutos Serie de velas usada para detectar los niveles de ruptura.

Notas y recomendaciones

  • El tamaño del pip se deriva automáticamente de Security.PriceStep y emula el ajuste de dígitos MQL, por lo que la estrategia se adapta a símbolos FX de 3 y 5 dígitos.
  • Todas las distancias se recalculan en unidades de precio antes de colocar órdenes, lo que mantiene la estrategia compatible con activos no FX siempre que el tamaño del tick esté definido.
  • El fallback del nivel de stop mínimo (tres unidades pip) imita el comportamiento del EA original cuando el broker no reporta un nivel de stop.
  • Como las órdenes pendientes expiran al final de cada vela, deberías ejecutar la estrategia en el marco temporal deseado sin gaps en el flujo de velas entrante.
  • La gestión del riesgo es crucial. Considera probar con spreads realistas y modelos de comisión antes de operar con capital real.
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>
/// Breakout strategy: enters long when price breaks above previous candle high,
/// enters short when price breaks below previous candle low.
/// Uses ATR-based stop loss and take profit.
/// </summary>
public class TenPipsEurusdStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stopLossMult;
	private readonly StrategyParam<decimal> _takeProfitMult;
	private readonly StrategyParam<decimal> _trailingMult;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takePrice;
	private bool _hasPrev;

	public decimal StopLossMult { get => _stopLossMult.Value; set => _stopLossMult.Value = value; }
	public decimal TakeProfitMult { get => _takeProfitMult.Value; set => _takeProfitMult.Value = value; }
	public decimal TrailingMult { get => _trailingMult.Value; set => _trailingMult.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TenPipsEurusdStrategy()
	{
		_stopLossMult = Param(nameof(StopLossMult), 1.0m)
			.SetGreaterThanZero()
			.SetDisplay("SL Mult", "Stop loss ATR multiplier", "Risk");

		_takeProfitMult = Param(nameof(TakeProfitMult), 2.0m)
			.SetGreaterThanZero()
			.SetDisplay("TP Mult", "Take profit ATR multiplier", "Risk");

		_trailingMult = Param(nameof(TrailingMult), 0.8m)
			.SetGreaterThanZero()
			.SetDisplay("Trail Mult", "Trailing stop ATR multiplier", "Risk");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR calculation length", "Indicators");

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

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh = 0;
		_prevLow = 0;
		_entryPrice = 0;
		_stopPrice = null;
		_takePrice = null;
		_hasPrev = false;
	}

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

		var atr = new AverageTrueRange { Length = AtrPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevHigh = candle.HighPrice;
			_prevLow = candle.LowPrice;
			_hasPrev = true;
			return;
		}

		var close = candle.ClosePrice;

		// Manage existing position
		if (Position > 0)
		{
			// Trail
			var trail = close - TrailingMult * atr;
			if (_stopPrice == null || trail > _stopPrice)
				_stopPrice = trail;

			if (close <= _stopPrice || (_takePrice != null && close >= _takePrice))
			{
				SellMarket(Math.Abs(Position));
				_stopPrice = null;
				_takePrice = null;
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			var trail = close + TrailingMult * atr;
			if (_stopPrice == null || trail < _stopPrice)
				_stopPrice = trail;

			if (close >= _stopPrice || (_takePrice != null && close <= _takePrice))
			{
				BuyMarket(Math.Abs(Position));
				_stopPrice = null;
				_takePrice = null;
				_entryPrice = 0;
			}
		}

		// Entry on breakout
		if (_hasPrev && Position == 0)
		{
			if (close > _prevHigh + atr * 0.5m)
			{
				BuyMarket();
				_entryPrice = close;
				_stopPrice = close - StopLossMult * atr;
				_takePrice = close + TakeProfitMult * atr;
			}
			else if (close < _prevLow - atr * 0.5m)
			{
				SellMarket();
				_entryPrice = close;
				_stopPrice = close + StopLossMult * atr;
				_takePrice = close - TakeProfitMult * atr;
			}
		}

		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_hasPrev = true;
	}
}