Ver en GitHub

Estrategia DecEMA

Estrategia que utiliza el indicador DecEMA para seguir la dirección de la tendencia. El indicador aplica diez suavizaciones exponenciales consecutivas y las combina para crear una media móvil de bajo retardo. La estrategia compara los últimos tres valores de DecEMA. Si la línea gira hacia arriba y el valor más reciente supera al anterior, compra y cierra cualquier posición corta. Si la línea gira hacia abajo y el valor más reciente está por debajo del anterior, vende y cierra cualquier posición larga.

Detalles

  • Criterios de entrada:
    • Largo: la pendiente de DecEMA gira hacia arriba y el valor actual > valor anterior
    • Corto: la pendiente de DecEMA gira hacia abajo y el valor actual < valor anterior
  • Largo/Corto: Ambos
  • Criterios de salida:
    • Largo: la pendiente gira hacia abajo
    • Corto: la pendiente gira hacia arriba
  • Stops: Ninguno
  • Valores predeterminados:
    • EmaPeriod = 3
    • Length = 15
    • BuyPosOpen = true
    • SellPosOpen = true
    • BuyPosClose = true
    • SellPosClose = true
    • CandleType = TimeSpan.FromHours(8).TimeFrame()
  • Filtros:
    • Categoría: Seguimiento de tendencia
    • Dirección: Ambos
    • Indicadores: DecEMA
    • Stops: No
    • Complejidad: Intermedio
    • Marco temporal: Medio plazo
    • 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>
/// Strategy based on DecEMA indicator slope.
/// Buys when the indicator turns upward and sells when it turns downward.
/// </summary>
public class DecEmaStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _length;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prev;
	private decimal _prevPrev;
	private int _count;
	private readonly StrategyParam<bool> _buyOpen, _sellOpen, _buyClose, _sellClose;
	private Order _pending;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int Length { get => _length.Value; set => _length.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public bool BuyPosOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
	public bool SellPosOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
	public bool BuyPosClose { get => _buyClose.Value; set => _buyClose.Value = value; }
	public bool SellPosClose { get => _sellClose.Value; set => _sellClose.Value = value; }
	public DecEmaStrategy()
	{
		_buyOpen = Param(nameof(BuyPosOpen), true);
		_sellOpen = Param(nameof(SellPosOpen), true);
		_buyClose = Param(nameof(BuyPosClose), true);
		_sellClose = Param(nameof(SellPosClose), true);
		_emaPeriod = Param(nameof(EmaPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("Base EMA Period", "Length for initial EMA", "Parameters");

		_length = Param(nameof(Length), 15)
			.SetGreaterThanZero()
			.SetDisplay("Smoothing Length", "Smoothing length for DecEMA", "Parameters");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prev = default;
		_prevPrev = default;
		_count = default;
		_pending = null;
	}

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

		_count = 0;
		_pending = null;

		var decema = new DecemaIndicator
		{
			EmaPeriod = EmaPeriod,
			Length = Length
		};

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

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

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

		_count++;

		if (_count <= 2)
		{
			_prevPrev = _prev;
			_prev = decema;
			return;
		}

		var buy = _prev < _prevPrev && decema > _prev;
		var sell = _prev > _prevPrev && decema < _prev;
		if (IsFormedAndOnlineAndAllowTrading() && (_pending is null || _pending.State is OrderStates.Done or OrderStates.Failed))
		{
			if (buy)
			{
				if (Position < 0m && SellPosClose) _pending = BuyMarket(-Position + (BuyPosOpen ? Volume : 0m));
				else if (Position == 0m && BuyPosOpen) _pending = BuyMarket(Volume);
			}
			else if (sell)
			{
				if (Position > 0m && BuyPosClose) _pending = SellMarket(Position + (SellPosOpen ? Volume : 0m));
				else if (Position == 0m && SellPosOpen) _pending = SellMarket(Volume);
			}
		}

		_prevPrev = _prev;
		_prev = decema;
	}

	private class DecemaIndicator : DecimalLengthIndicator
	{
		public int EmaPeriod { get; set; } = 3;

		private readonly ExponentialMovingAverage _baseEma = new();
		private decimal _ema1, _ema2, _ema3, _ema4, _ema5;
		private decimal _ema6, _ema7, _ema8, _ema9, _ema10;

		public override void Reset()
		{
			base.Reset();
			_baseEma.Length = EmaPeriod;
			_baseEma.Reset();
			_ema1 = _ema2 = _ema3 = _ema4 = _ema5 = 0m;
			_ema6 = _ema7 = _ema8 = _ema9 = _ema10 = 0m;
		}

		protected override IIndicatorValue OnProcess(IIndicatorValue input)
		{
			var ema0 = _baseEma.Process(input).ToDecimal();
			var alpha = 2m / (1m + Length);

			_ema1 = alpha * ema0 + (1 - alpha) * _ema1;
			_ema2 = alpha * _ema1 + (1 - alpha) * _ema2;
			_ema3 = alpha * _ema2 + (1 - alpha) * _ema3;
			_ema4 = alpha * _ema3 + (1 - alpha) * _ema4;
			_ema5 = alpha * _ema4 + (1 - alpha) * _ema5;
			_ema6 = alpha * _ema5 + (1 - alpha) * _ema6;
			_ema7 = alpha * _ema6 + (1 - alpha) * _ema7;
			_ema8 = alpha * _ema7 + (1 - alpha) * _ema8;
			_ema9 = alpha * _ema8 + (1 - alpha) * _ema9;
			_ema10 = alpha * _ema9 + (1 - alpha) * _ema10;

			var value = 10m * _ema1 - 45m * _ema2 + 120m * _ema3 - 210m * _ema4 + 252m * _ema5
				- 210m * _ema6 + 120m * _ema7 - 45m * _ema8 + 10m * _ema9 - _ema10;

			IsFormed = _baseEma.IsFormed;

			return new DecimalIndicatorValue(this, value, input.Time);
		}
	}
}