Ver en GitHub

Estrategia de Cruce Q2MA

La Estrategia de Cruce Q2MA opera basándose en el cruce de medias móviles suavizadas construidas sobre los precios de cierre y apertura de las velas. Se abre una posición larga cuando la media del cierre cae por debajo de la media de la apertura tras haber estado por encima, mientras que se abre una posición corta en el cruce opuesto. Las posiciones se cierran cuando aparece una tendencia contraria. La estrategia también aplica niveles de stop loss y take profit medidos en ticks.

Detalles

  • Criterios de entrada: cruce entre medias móviles de precios de cierre y apertura
  • Largo/Corto: ambas direcciones
  • Criterios de salida: cruce opuesto o stop loss/take profit
  • Stops: sí
  • Valores predeterminados:
    • Length = 8
    • StopLoss = 1000
    • TakeProfit = 2000
    • CandleType = TimeSpan.FromHours(4).TimeFrame()
    • Volume = 1
    • BuyPosOpen = true
    • SellPosOpen = true
    • BuyPosClose = true
    • SellPosClose = true
    • Invert = false
  • Filtros:
    • Categoría: Tendencia
    • Dirección: Ambos
    • Indicadores: Moving Average
    • Stops: Sí
    • Complejidad: Intermedio
    • Marco temporal: H4
    • 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>
/// Q2MA cross strategy based on open and close moving averages.
/// Buys when close MA crosses above open MA, sells on opposite.
/// </summary>
public class Q2maCrossStrategy : Strategy
{
	private readonly StrategyParam<int> _length;
	private readonly StrategyParam<DataType> _candleType;

	private ExponentialMovingAverage _closeMa;
	private ExponentialMovingAverage _openMa;
	private decimal? _prevUp;
	private decimal? _prevDn;

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

	public Q2maCrossStrategy()
	{
		_length = Param(nameof(Length), 8)
			.SetDisplay("Length", "Moving average length", "Indicator");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Indicator timeframe", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_closeMa = null;
		_openMa = null;
		_prevUp = null;
		_prevDn = null;
	}

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

		_prevUp = null;
		_prevDn = null;

		_closeMa = new ExponentialMovingAverage { Length = Length };
		_openMa = new ExponentialMovingAverage { Length = Length };

		Indicators.Add(_closeMa);
		Indicators.Add(_openMa);

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

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

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

		var t = candle.ServerTime;

		var upResult = _closeMa.Process(new DecimalIndicatorValue(_closeMa, candle.ClosePrice, t) { IsFinal = true });
		var dnResult = _openMa.Process(new DecimalIndicatorValue(_openMa, candle.OpenPrice, t) { IsFinal = true });

		if (!_closeMa.IsFormed || !_openMa.IsFormed)
			return;

		var up = upResult.GetValue<decimal>();
		var dn = dnResult.GetValue<decimal>();

		if (_prevUp is null || _prevDn is null)
		{
			_prevUp = up;
			_prevDn = dn;
			return;
		}

		// Close MA crosses above Open MA -> buy signal
		if (_prevUp <= _prevDn && up > dn && Position <= 0)
			BuyMarket();
		// Close MA crosses below Open MA -> sell signal
		else if (_prevUp >= _prevDn && up < dn && Position >= 0)
			SellMarket();

		_prevUp = up;
		_prevDn = dn;
	}
}