Ver en GitHub

Estrategia de Exp XPVT

La Estrategia de Exp XPVT es una conversión del asesor experto de MetaTrader 5 Exp_XPVT. El sistema opera cruzamientos entre el indicador Price and Volume Trend (PVT) y una media móvil configurable aplicada a la serie PVT. Cuando la línea PVT cruda cae por debajo de su variante suavizada, la estrategia abre posiciones largas, mientras que los cruces al alza activan entradas cortas. Las distancias opcionales de stop-loss y take-profit emulan el comportamiento del asesor experto original.

Lógica del indicador

  • El Price and Volume Trend acumula cambios de precio ponderados por volumen usando el precio aplicado seleccionado (cierre, apertura, mediano, etc.).
  • Un filtro de suavizado (SMA, EMA, MA suavizado, LWMA, Jurik, T3, aproximación VIDYA o Kaufman AMA) produce la línea de señal.
  • Un desplazamiento histórico (Signal Bar) recrea la lógica MT5: la estrategia compara los valores suavizados y crudos de uno y dos barras atrás para detectar cruzamientos y condiciones de salida.
  • Se puede usar volumen de tick o real para la ponderación. Si el tipo de volumen solicitado no está disponible, la estrategia recurre automáticamente a la otra fuente.

Reglas de trading

  1. En cada vela terminada, calcular el valor PVT del precio aplicado configurado y el tipo de volumen.
  2. Actualizar el indicador de suavizado y almacenar los valores más recientes según Signal Bar.
  3. Si la barra anterior mostró PVT por encima de la línea de señal, cerrar cualquier posición corta. Si, además, el PVT almacenado más reciente está por debajo o igual a la línea de señal, abrir una posición larga (si las entradas largas están habilitadas).
  4. Si la barra anterior mostró PVT por debajo de la línea de señal, cerrar cualquier posición larga. Si, además, el PVT almacenado más reciente está por encima o igual a la línea de señal, abrir una posición corta (si las entradas cortas están habilitadas).
  5. Después de entrar en una operación, se adjuntan órdenes opcionales de stop-loss y take-profit usando las distancias configuradas (expresadas en pasos de precio).
  6. La gestión de dinero imita al asesor experto original: las nuevas órdenes usan el Order Volume base configurado e incluyen la exposición opuesta para invertir completamente al cambiar de dirección.

Parámetros

  • Order Volume – volumen base para nuevas órdenes e inversiones.
  • Stop Loss – distancia en pasos de precio para el stop protector (0 lo deshabilita).
  • Take Profit – distancia en pasos de precio para el objetivo de beneficio (0 lo deshabilita).
  • Allow Buy Entry / Allow Sell Entry – habilitar la apertura de posiciones largas o cortas.
  • Allow Buy Exit / Allow Sell Exit – habilitar el cierre automático de posiciones existentes cuando aparece la configuración opuesta.
  • Candle Type – marco temporal usado para los cálculos del indicador.
  • Volume Source – elegir volumen de tick o real para la ponderación PVT.
  • Smoothing Method / Length / Phase – media móvil aplicada a la línea PVT. El parámetro de fase se usa solo en métodos estilo Jurik.
  • Applied Price – fórmula de precio que alimenta el PVT (cierre, apertura, seguimiento de tendencia, DeMark, etc.).
  • Signal Bar – desplazamiento histórico (en barras) usado para evaluar el cruzamiento, reproduciendo la implementación MT5.

Notas

  • La estrategia procesa solo velas terminadas para asegurar la estabilidad del indicador.
  • El suavizado estilo Jurik usa reflexión para reenviar el parámetro de fase cuando el indicador lo expone.
  • Cuando no hay volumen de tick ni real disponible, la estrategia recurre a volumen cero, previniendo acumulaciones espurias.
  • La llamada opcional StartProtection activa el monitoreo de posición integrado de StockSharp, coincidiendo con la invocación única en el asesor experto original.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class ExpXpvtStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ExpXpvtStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}