Ver en GitHub

Estrategia de Trading IStochastic

Descripción general

La Estrategia de Trading IStochastic es un port directo de StockSharp del asesor experto "IStochastic_Trading" de MetaTrader 5. El bot usa el Oscilador Estocástico para detectar condiciones de sobrecompra y sobreventa y luego construye una escalera de posiciones al estilo martingala mientras gestiona cada entrada con stop-loss, take-profit y un trailing stop. La implementación opera en velas terminadas obtenidas a través de la API de alto nivel de StockSharp y se basa únicamente en órdenes de mercado.

Lógica de trading

  1. Calcular un Oscilador Estocástico con longitud %K, suavizado %D y un factor de ralentización adicional configurables.
  2. Cuando no hay posiciones activas, evaluar la vela terminada más reciente:
    • Abrir una posición larga si %K está por encima de %D y %D está por debajo de la zona de compra configurada.
    • Abrir una posición corta si %K está por debajo de %D y %D está por encima de la zona de venta configurada.
  3. Cuando existe una posición, monitorear el último relleno en la escalera:
    • Si el mercado se mueve contra la operación al menos el gap configurado (en pips), abrir una nueva posición en la misma dirección con el doble del volumen anterior, siempre que no se exceda el número máximo de posiciones.
  4. Para cada entrada mantener niveles de stop-loss y take-profit por operación derivados de distancias en pips convertidas a puntos de precio usando el PriceStep del instrumento y el número de decimales. Si el precio de cierre alcanza el stop o el objetivo, la estrategia sale de la posición específica con una orden de mercado.
  5. Aplicar un trailing stop después de cada cierre de vela. Cuando la operación se mueve suficientemente en la dirección favorable, el precio del stop se ajusta por el paso de trailing especificado, aproximando el comportamiento de trailing por posición del terminal.

Parámetros

Nombre Predeterminado Descripción
OrderVolume 0.1 Tamaño inicial de la posición en lotes. Las entradas adicionales doblan el volumen anterior.
TakeProfitPips 50 Distancia del take-profit medida en pips. El valor se convierte a puntos de precio internamente.
StopLossPips 50 Distancia del stop-loss en pips para cada posición.
TrailingStopPips 10 Distancia del trailing stop en pips. Establecer en cero para deshabilitar el trailing.
TrailingStepPips 5 Movimiento favorable mínimo (en pips) antes de que se ajuste el trailing stop.
MaxPositions 3 Número máximo de pasos martingala simultáneamente abiertos. Un valor de 0 elimina el límite.
GapPips 7 Gap de precio, en pips, requerido antes de doblar en la dirección actual.
KPeriod 5 Número de velas usadas para construir la línea %K.
DPeriod 3 Período del promedio de suavizado %D.
Slowing 3 Suavizado adicional aplicado a %K.
ZoneBuy 30 Umbral de %D usado para validar entradas largas (zona de sobreventa).
ZoneSell 70 Umbral de %D usado para validar entradas cortas (zona de sobrecompra).
CandleType Marco temporal de 15 minutos Serie de velas empleada para los cálculos.

Notas de implementación

  • Las distancias en pips se convierten a precios con PriceStep. Para cotizaciones de 3 y 5 dígitos se usa un factor adicional de 10 para imitar la lógica de punto ajustado de MetaTrader.
  • Las verificaciones de stop-loss, take-profit y trailing stop dependen de los precios de cierre de las velas para mantener la lógica determinista dentro del backtester. La ejecución en tiempo real puede personalizarse si se requiere gestión intrabar.
  • La estrategia solo abre una escalera direccional a la vez; todas las posiciones deben cerrarse antes de cambiar de dirección.
  • La implementación en Python se omite intencionalmente según lo solicitado.
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>
/// Stochastic Oscillator-based strategy with zone filtering.
/// Goes long when %K crosses above %D in the oversold zone, short when %K crosses below %D in overbought zone.
/// </summary>
public class IStochasticTradingStrategy : Strategy
{
	private readonly StrategyParam<int> _kPeriod;
	private readonly StrategyParam<int> _dPeriod;
	private readonly StrategyParam<decimal> _zoneBuy;
	private readonly StrategyParam<decimal> _zoneSell;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevK;
	private decimal? _prevD;

	/// <summary>
	/// %K period.
	/// </summary>
	public int KPeriod
	{
		get => _kPeriod.Value;
		set => _kPeriod.Value = value;
	}

	/// <summary>
	/// %D smoothing period.
	/// </summary>
	public int DPeriod
	{
		get => _dPeriod.Value;
		set => _dPeriod.Value = value;
	}

	/// <summary>
	/// Buy zone threshold (oversold).
	/// </summary>
	public decimal ZoneBuy
	{
		get => _zoneBuy.Value;
		set => _zoneBuy.Value = value;
	}

	/// <summary>
	/// Sell zone threshold (overbought).
	/// </summary>
	public decimal ZoneSell
	{
		get => _zoneSell.Value;
		set => _zoneSell.Value = value;
	}

	/// <summary>
	/// Candle type used for analysis.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public IStochasticTradingStrategy()
	{
		_kPeriod = Param(nameof(KPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("K Period", "Number of bars for %K", "Indicators");

		_dPeriod = Param(nameof(DPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("D Period", "Smoothing period for %D", "Indicators");

		_zoneBuy = Param(nameof(ZoneBuy), 30m)
			.SetRange(0m, 100m)
			.SetDisplay("Buy Zone", "Upper boundary for bullish confirmation", "Signals");

		_zoneSell = Param(nameof(ZoneSell), 70m)
			.SetRange(0m, 100m)
			.SetDisplay("Sell Zone", "Lower boundary for bearish confirmation", "Signals");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevK = null;
		_prevD = null;
	}

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

		_prevK = null;
		_prevD = null;

		var stochastic = new StochasticOscillator();
		stochastic.K.Length = KPeriod;
		stochastic.D.Length = DPeriod;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(stochastic, ProcessCandle)
			.Start();

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

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

		if (!stochasticValue.IsFinal)
			return;

		var stoch = (StochasticOscillatorValue)stochasticValue;

		if (stoch.K is not decimal kValue || stoch.D is not decimal dValue)
			return;

		if (_prevK is decimal prevK && _prevD is decimal prevD)
		{
			var crossedUp = prevK <= prevD && kValue > dValue;
			var crossedDown = prevK >= prevD && kValue < dValue;

			if (crossedUp && dValue < ZoneBuy && Position <= 0)
			{
				BuyMarket();
			}
			else if (crossedDown && dValue > ZoneSell && Position >= 0)
			{
				SellMarket();
			}
		}

		_prevK = kValue;
		_prevD = dValue;
	}
}