Ver en GitHub

Estrategia Labouchere EA

Esta estrategia combina un cruce de Oscilador Estocástico con una secuencia de gestión monetaria Labouchere. El indicador Estocástico genera señales cuando %K cruza a %D. El sistema Labouchere ajusta el volumen de la operación después de cada posición cerrada: las pérdidas agregan un nuevo elemento igual a la suma del primer y último número de la secuencia, mientras que las ganancias eliminan estos elementos.

Las operaciones se toman solo en velas completadas. La secuencia puede reiniciarse opcionalmente cuando se eliminan todos los números. Un filtro de tiempo permite operar dentro de una ventana intradiaria específica, y las señales opuestas pueden cerrar posiciones existentes. Se admiten niveles fijos de stop-loss y take-profit (en pasos de precio).

Detalles

  • Criterios de entrada:
    • Largo: %K cruza por encima de %D.
    • Corto: %K cruza por debajo de %D.
  • Largo/Corto: Ambos lados.
  • Criterios de salida:
    • Salida opcional por señal opuesta.
    • Stop-loss y take-profit fijos (si se configuran).
  • Stops: Sí.
  • Gestión monetaria: Secuencia Labouchere.
  • Valores predeterminados:
    • LotSequence = "0.01,0.02,0.01,0.02,0.01,0.01,0.01,0.01"
    • NewRecycle = true
    • StopLoss = 40
    • TakeProfit = 50
    • IsReversed = false
    • UseOppositeExit = false
    • UseWorkTime = false
    • StartTime = 00:00
    • StopTime = 24:00
    • KPeriod = 10
    • DPeriod = 190
  • Filtros:
    • Categoría: Mixto
    • Dirección: Ambos
    • Indicadores: Stochastic Oscillator
    • Stops: Sí
    • Complejidad: Intermedio
    • Marco temporal: Intradía
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
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>
/// Labouchere betting system strategy using Stochastic oscillator for signals.
/// Adjusts position sizing based on the Labouchere sequence.
/// </summary>
public class LabouchereEaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _kPeriod;
	private readonly StrategyParam<int> _dPeriod;
	private readonly StrategyParam<decimal> _stopLossPct;
	private readonly StrategyParam<decimal> _takeProfitPct;

	private decimal? _prevK;
	private decimal? _prevD;
	private decimal _entryPrice;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int KPeriod { get => _kPeriod.Value; set => _kPeriod.Value = value; }
	public int DPeriod { get => _dPeriod.Value; set => _dPeriod.Value = value; }
	public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
	public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }

	public LabouchereEaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles used", "General");

		_kPeriod = Param(nameof(KPeriod), 10)
			.SetDisplay("K Period", "Stochastic %K period", "Indicator")
			.SetGreaterThanZero();

		_dPeriod = Param(nameof(DPeriod), 3)
			.SetDisplay("D Period", "Stochastic %D period", "Indicator")
			.SetGreaterThanZero();

		_stopLossPct = Param(nameof(StopLossPct), 1m)
			.SetDisplay("Stop Loss %", "Stop loss percent", "Risk")
			.SetGreaterThanZero();

		_takeProfitPct = Param(nameof(TakeProfitPct), 1.5m)
			.SetDisplay("Take Profit %", "Take profit percent", "Risk")
			.SetGreaterThanZero();
	}

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

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

		var stoch = new StochasticOscillator
		{
			K = { Length = KPeriod },
			D = { Length = DPeriod }
		};

		StartProtection(
			takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
			stopLoss: new Unit(StopLossPct, UnitTypes.Percent));

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

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

		if (!stochValue.IsFinal || !stochValue.IsFormed)
			return;

		var stoch = (IStochasticOscillatorValue)stochValue;

		if (stoch.K is not decimal k || stoch.D is not decimal d)
			return;

		var signal = 0;

		if (_prevK.HasValue && _prevD.HasValue)
		{
			if (_prevK <= _prevD && k > d)
				signal = 1;
			else if (_prevK >= _prevD && k < d)
				signal = -1;
		}

		_prevK = k;
		_prevD = d;

		if (signal == 0)
			return;

		if (signal > 0 && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
			_entryPrice = candle.ClosePrice;
		}
		else if (signal < 0 && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
			_entryPrice = candle.ClosePrice;
		}
	}

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