Ver en GitHub

VIDYA N Barras Fronteras Martingale

Descripción general

La estrategia original MetaTrader combina el indicador de canal "VIDYA N Bars Borders" con un módulo de dimensionamiento de posición martingala. El puerto StockSharp mantiene la idea de comprar cuando el precio cae por debajo de la banda inferior adaptativa y vender cuando el precio sube por encima de la banda superior. El centro del canal es producido por una media móvil adaptativa (análogo de VIDYA) y su ancho está controlado por una envolvente de rango verdadero promedio. Un bloque de administración de dinero aumenta el tamaño de la operación después de perder operaciones mientras se respetan los límites máximos de posición y exposición.

Lógica comercial

  1. Suscríbase a las velas de período de tiempo seleccionadas.
  2. Calcule una media móvil adaptativa de Kaufman como sustituto de VIDYA y un canal ATR a su alrededor.
  3. Cuando el cierre de una vela terminada cruce por debajo de la banda inferior, abra o invierta a una posición larga (a menos que la bandera Reverse esté habilitada, en cuyo caso se abre una venta corta).
  4. Cuando el cierre cruce por encima de la banda superior, abra o invierta a una posición corta (o larga si Reverse es verdadero).
  5. Haga cumplir una distancia de precio mínima entre entradas consecutivas para evitar volver a ingresar demasiado cerca del llenado anterior.
  6. Si el beneficio flotante en la posición abierta alcanza el objetivo monetario especificado, aplana todo y espera la siguiente señal.
  7. Después de cada operación cerrada, el siguiente volumen base se restablece al tamaño inicial (después de una operación rentable) o se multiplica por el índice martingala (después de una operación perdedora). El volumen resultante se alinea con el paso del instrumento y se aplican límites de volumen total y por operación.

Parámetros

Nombre Descripción
Candle Type Tipo de datos de velas para negociar.
CMO Period Ventana de ratio de eficiencia para la media móvil adaptativa.
EMA Period Período de suavizado de la media móvil adaptativa.
ATR Period Número de barras para el ancho medio del canal ATR.
Profit Target Umbral de beneficio monetario que desencadena una salida total.
Increase Ratio Multiplicador aplicado al siguiente volumen comercial después de una operación perdedora.
Max Position Volume Techo rígido para un único volumen de pedido/posición.
Max Total Volume Límite superior de la exposición total abierta por la estrategia.
Max Positions Número máximo de posiciones concurrentes (el puerto mantiene una posición neta).
Minimum Step Distancia mínima entre dos entradas consecutivas, medida en puntos.
Base Volume Tamaño inicial del pedido antes de los ajustes de martingala.
Reverse Signals Invierte la interpretación larga/corta de la ruptura del canal.

Notas de implementación

  • StockSharp no incluye una implementación directa de VIDYA. La estrategia utiliza KaufmanAdaptiveMovingAverage con eficiencia configurable y ventanas de suavizado para imitar el comportamiento adaptativo de VIDYA. Esto mantiene la capacidad de respuesta cerca del indicador original mientras se basa en componentes integrados.
  • Sólo se gestiona una posición neta a la vez. La versión MetaTrader puso en cola varias entradas pendientes; en StockSharp cada señal abre una nueva posición o invierte la actual. La escala Martingale se aplica al siguiente tamaño de entrada en lugar de agregar nuevas capas inmediatamente.
  • La alineación mínima de pasos y volúmenes depende de los metadatos del instrumento (PriceStep, VolumeStep, MinVolume, MaxVolume). Proporcione estos valores al configurar la estrategia para obtener límites de ejecución precisos.
  • El seguimiento de beneficios se basa en la estrategia PnL y el último cierre de vela, lo cual es suficiente para realizar pruebas retrospectivas de alto nivel. Para operar en vivo, conecte la estrategia a una cartera que actualice los valores PnL realizados.

Archivos

  • CS/VidyaNBarsBordersMartingaleStrategy.cs — Implementación en C# de la estrategia.
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;

/// <summary>
/// Simplified from "VIDYA N Bars Borders Martingale" MetaTrader expert.
/// Uses EMA as adaptive MA proxy and a range-based channel from recent N bars.
/// Buys when price closes below lower band, sells when above upper band.
/// Includes simple martingale volume increase on losing trades.
/// </summary>
public class VidyaNBarsBordersMartingaleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _rangePeriod;
	private readonly StrategyParam<decimal> _martingaleMultiplier;

	private ExponentialMovingAverage _ema;
	private readonly Queue<decimal> _highHistory = new();
	private readonly Queue<decimal> _lowHistory = new();
	private decimal _currentVolume;
	private decimal _entryPrice;

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

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

	public int RangePeriod
	{
		get => _rangePeriod.Value;
		set => _rangePeriod.Value = value;
	}

	public decimal MartingaleMultiplier
	{
		get => _martingaleMultiplier.Value;
		set => _martingaleMultiplier.Value = value;
	}

	public VidyaNBarsBordersMartingaleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Trading candle type", "General");

		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "Smoothing period for adaptive MA proxy", "Indicators");

		_rangePeriod = Param(nameof(RangePeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Range Period", "Number of bars for high/low range channel", "Indicators");

		_martingaleMultiplier = Param(nameof(MartingaleMultiplier), 1.25m)
			.SetDisplay("Martingale Multiplier", "Volume multiplier after losing trade", "Risk");
	}

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

		_ema = new ExponentialMovingAverage { Length = EmaPeriod };
		_highHistory.Clear();
		_lowHistory.Clear();
		_currentVolume = Volume > 0 ? Volume : 1;
		_entryPrice = 0;

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

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

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

		// Track high/low for range calculation
		_highHistory.Enqueue(candle.HighPrice);
		_lowHistory.Enqueue(candle.LowPrice);

		if (_highHistory.Count > RangePeriod)
		{
			_highHistory.Dequeue();
			_lowHistory.Dequeue();
		}

		if (!_ema.IsFormed || _highHistory.Count < RangePeriod)
			return;

		// Compute range from recent bars
		decimal highest = decimal.MinValue;
		decimal lowest = decimal.MaxValue;
		var highs = _highHistory.ToArray();
		var lows = _lowHistory.ToArray();
		foreach (var h in highs)
			if (h > highest) highest = h;
		foreach (var l in lows)
			if (l < lowest) lowest = l;

		var range = (highest - lowest) * 0.75m;
		if (range <= 0)
			return;

		var upper = emaValue + range;
		var lower = emaValue - range;
		var close = candle.ClosePrice;
		var baseVolume = Volume > 0 ? Volume : 1;
		var maxVolume = baseVolume * 8;
		var nextVolume = _currentVolume;

		if (close < lower)
		{
			// Price below lower band -> buy signal
			if (Position < 0)
			{
				var wasLoss = close > _entryPrice;
				nextVolume = wasLoss
					? Math.Min(_currentVolume * MartingaleMultiplier, maxVolume)
					: baseVolume;
			}

			if (Position <= 0)
			{
				BuyMarket(Position < 0 ? Math.Abs(Position) + nextVolume : nextVolume);
				_currentVolume = nextVolume;
				_entryPrice = close;
			}
		}
		else if (close > upper)
		{
			// Price above upper band -> sell signal
			if (Position > 0)
			{
				var wasLoss = close < _entryPrice;
				nextVolume = wasLoss
					? Math.Min(_currentVolume * MartingaleMultiplier, maxVolume)
					: baseVolume;
			}

			if (Position >= 0)
			{
				SellMarket(Position > 0 ? Math.Abs(Position) + nextVolume : nextVolume);
				_currentVolume = nextVolume;
				_entryPrice = close;
			}
		}
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_ema = null;
		_highHistory.Clear();
		_lowHistory.Clear();
		_currentVolume = 0;
		_entryPrice = 0;

		base.OnReseted();
	}
}