Ver en GitHub

Sistema oscilador de vórtice 4153

Descripción general

La estrategia reproduce el experto MetaTrader 4 "Vortex Oscillator System" utilizando la estrategia de alto nivel de StockSharp API. Deriva un oscilador Vortex normalizado combinando los componentes estándar del indicador Vortex y reacciona cada vez que el impulso escapa de una banda neutral configurable. El algoritmo opera con un solo símbolo y siempre funciona con posiciones completamente cerradas o invertidas.

Reglas comerciales

  • Una suscripción de vela definida por CandleType alimenta un indicador Vortex con un período VortexLength. El oscilador se calcula como (VI+ - VI-) / (VI+ + VI-), lo que mantiene las lecturas en el rango [-1, 1].
  • Una configuración larga se activa cuando el oscilador cae por debajo de BuyThreshold y, si UseBuyStopLoss está habilitado, permanece por encima de BuyStopLossLevel. Se activa una configuración corta cuando el oscilador se eleva por encima de SellThreshold y, si UseSellStopLoss está habilitado, permanece por debajo de SellStopLossLevel.
  • Siempre que el oscilador regresa dentro de la banda neutral delimitada por BuyThreshold y SellThreshold, ambas configuraciones se borran, por lo que la siguiente ruptura debe ocurrir desde un estado neutral.
  • Si una configuración larga está activa mientras la posición actual es plana o corta, la estrategia envía una compra de mercado para lotes de Volumen más cualquier cantidad necesaria para cubrir una posición corta existente. Las configuraciones cortas reflejan ese comportamiento al vender lotes de volumen más la cantidad larga pendiente.
  • Las posiciones abiertas se pueden cerrar sin una configuración opuesta: si UseBuyStopLoss está habilitado y el oscilador toca BuyStopLossLevel, la operación larga se liquida; UseBuyTakeProfit sale de una posición larga una vez que el oscilador excede BuyTakeProfitLevel. Los controles equivalentes que utilizan SellStopLossLevel y SellTakeProfitLevel administran posiciones cortas cuando sus respectivos conmutadores están habilitados.

Parámetros

  • VortexLength: número de velas utilizadas para calcular los valores VI+ y VI-.
  • CandleType: período de tiempo o tipo de datos solicitados de la fuente de datos del mercado.
  • Volumen: tamaño de pedido base para nuevas entradas; Las órdenes de reversión agregan automáticamente la cantidad necesaria para aplanar la posición actual.
  • BuyThreshold: nivel del oscilador que arma una configuración larga una vez superado.
  • UseBuyStopLoss: requiere que el oscilador permanezca por encima de BuyStopLossLevel antes de que se pueda activar una entrada larga.
  • BuyStopLossLevel: nivel del oscilador que cierra inmediatamente una posición larga cuando el filtro de parada está habilitado.
  • UseBuyTakeProfit: alterna la obtención de beneficios basada en el oscilador para operaciones largas.
  • BuyTakeProfitLevel: nivel del oscilador que obtiene ganancias en posiciones largas cuando el filtro de toma de ganancias está activo.
  • SellThreshold: nivel del oscilador que arma una configuración corta una vez superado.
  • UseSellStopLoss: requiere que el oscilador permanezca por debajo de SellStopLossLevel antes de que se pueda activar una entrada corta.
  • SellStopLossLevel: nivel del oscilador que cierra inmediatamente una posición corta cuando el filtro de parada está habilitado.
  • UseSellTakeProfit: alterna la obtención de beneficios basada en el oscilador para operaciones cortas.
  • SellTakeProfitLevel: nivel del oscilador que obtiene ganancias en posiciones cortas cuando el filtro de toma de ganancias está activo.

Notas adicionales

  • La estrategia dibuja velas y ejecuta operaciones en el gráfico automáticamente; la lógica del oscilador interno no agrega paneles personalizados.
  • Debido a que el oscilador está normalizado, los umbrales predeterminados -0.75, 0.75, -1.00 y 1.00 se traducen directamente del asesor experto original y se pueden optimizar utilizando el sistema de parámetros de StockSharp.
  • La lógica nunca mantiene posiciones largas y cortas simultáneas; cada reversión cierra primero la exposición actual y luego abre el lado opuesto en una única orden de mercado.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Vortex Oscillator System: RSI momentum breakout with EMA filter and ATR stops.
/// </summary>
public class VortexOscillatorSystemMomentumStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevRsi;
	private decimal _entryPrice;

	public VortexOscillatorSystemMomentumStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_emaLength = Param(nameof(EmaLength), 50)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

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

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

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

		_prevRsi = 0;
		_entryPrice = 0;
	}

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

		_prevRsi = 0;
		_entryPrice = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (_prevRsi == 0 || atrVal <= 0)
		{
			_prevRsi = rsiVal;
			return;
		}

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal < 40)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal > 60)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevRsi = rsiVal;
	}
}