Ver en GitHub

Estrategia de Compra en Caída con Múltiples Posiciones

La estrategia Buy Dip Multiple Positions añade posiciones largas cuando se produce una caída de precio junto con un alto volumen y una condición de impulso de precio. Cada operación arriesga el 2% del capital y comparte niveles comunes de stop dinámico y objetivo. Solo se abre una nueva posición si la operación anterior cerrada fue rentable.

Detalles

  • Criterios de entrada:
    • Cierre por debajo del mínimo anterior en un 0,2%.
    • Volumen superior al 120% de la media de las dos últimas barras.
    • Cierre por debajo del precio de cierre N barras atrás multiplicado por PriceSurgePercent / 100.
  • Largo/Corto: Solo largos.
  • Criterios de salida:
    • Stop inicial como porcentaje del mínimo de la barra de entrada.
    • Stop dinámico que aumenta cada barra después del setup.
    • Precio objetivo por encima del mínimo de la barra de entrada.
  • Stops: Sí.
  • Valores predeterminados:
    • MaxPositions = 20
    • TrailRatePercent = 1
    • InitialStopPercent = 85
    • TargetPricePercent = 60
    • PriceSurgePercent = 89
    • SurgeLookbackBars = 14
  • Filtros:
    • Categoría: Reversión a la media
    • Dirección: Largo
    • Indicadores: Volumen, Acción del precio
    • Stops: Sí
    • Complejidad: Moderado
    • Marco temporal: Cualquiera
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
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>
/// Buy dip multiple positions strategy using EMA crossover for trend timing.
/// Enters long on golden cross, short on death cross.
/// </summary>
public class BuyDipMultiplePositionsStrategy : Strategy
{
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFastEma;
	private decimal _prevSlowEma;

	public int FastEmaPeriod { get => _fastEmaPeriod.Value; set => _fastEmaPeriod.Value = value; }
	public int SlowEmaPeriod { get => _slowEmaPeriod.Value; set => _slowEmaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public BuyDipMultiplePositionsStrategy()
	{
		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 120)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 450)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFastEma = 0m;
		_prevSlowEma = 0m;
	}

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

		var fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };

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

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

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

		if (_prevFastEma == 0m || _prevSlowEma == 0m)
		{
			_prevFastEma = fastEmaValue;
			_prevSlowEma = slowEmaValue;
			return;
		}

		if (_prevFastEma <= _prevSlowEma && fastEmaValue > slowEmaValue && Position <= 0)
		{
			BuyMarket();
		}
		else if (_prevFastEma >= _prevSlowEma && fastEmaValue < slowEmaValue && Position >= 0)
		{
			SellMarket();
		}

		_prevFastEma = fastEmaValue;
		_prevSlowEma = slowEmaValue;
	}
}