Ver en GitHub

Estrategia Color Zerolag Momentum OSMA

Esta estrategia construye un oscilador OSMA de Momentum de cero rezago personalizado utilizando cinco cálculos de Momentum. Cuando el valor del oscilador de hace dos barras está por debajo del valor de hace tres barras, la tendencia se considera alcista. En este caso, las posiciones cortas se cierran y se puede abrir una nueva posición larga si el valor más reciente está por encima del valor de hace dos barras. Cuando el valor de hace dos barras está por encima del valor de hace tres barras, la tendencia es bajista, las posiciones largas se cierran y se puede abrir una corta si el último valor está por debajo del valor de hace dos barras.

Parámetros

  • Smoothing1 – primer factor de suavizado para la tendencia lenta.
  • Smoothing2 – segundo factor de suavizado para la línea OSMA.
  • Factor1-5 – pesos aplicados a cada componente de Momentum.
  • MomentumPeriod1-5 – períodos para los indicadores de Momentum.
  • CandleType – marco temporal de velas para los cálculos.
  • BuyOpen – permitir abrir posiciones largas.
  • SellOpen – permitir abrir posiciones cortas.
  • BuyClose – permitir cerrar posiciones largas.
  • SellClose – permitir cerrar posiciones cortas.
using System;
using System.Collections.Generic;
using System.Linq;

using Ecng.Common;

using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>Five weighted Momentum indicators, two OSMA smoothing stages and the documented t-2/t-3 signal.</summary>
public class ColorZerolagMomentumOsmaStrategy : Strategy
{
	private readonly StrategyParam<int>[] _periods = new StrategyParam<int>[5];
	private readonly StrategyParam<decimal>[] _factors = new StrategyParam<decimal>[5];
	private readonly StrategyParam<int> _smoothing1, _smoothing2;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<bool> _buyOpen, _sellOpen, _buyClose, _sellClose;

	private readonly Queue<decimal> _closes = new();
	private decimal? _slow, _smoothed, _last, _prev2, _prev3;
	private Order _pending;

	public int MomentumPeriod1 { get => _periods[0].Value; set => _periods[0].Value = value; }
	public int MomentumPeriod2 { get => _periods[1].Value; set => _periods[1].Value = value; }
	public int MomentumPeriod3 { get => _periods[2].Value; set => _periods[2].Value = value; }
	public int MomentumPeriod4 { get => _periods[3].Value; set => _periods[3].Value = value; }
	public int MomentumPeriod5 { get => _periods[4].Value; set => _periods[4].Value = value; }
	public decimal Factor1 { get => _factors[0].Value; set => _factors[0].Value = value; }
	public decimal Factor2 { get => _factors[1].Value; set => _factors[1].Value = value; }
	public decimal Factor3 { get => _factors[2].Value; set => _factors[2].Value = value; }
	public decimal Factor4 { get => _factors[3].Value; set => _factors[3].Value = value; }
	public decimal Factor5 { get => _factors[4].Value; set => _factors[4].Value = value; }
	public int Smoothing1 { get => _smoothing1.Value; set => _smoothing1.Value = value; }
	public int Smoothing2 { get => _smoothing2.Value; set => _smoothing2.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public bool BuyOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
	public bool SellOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
	public bool BuyClose { get => _buyClose.Value; set => _buyClose.Value = value; }
	public bool SellClose { get => _sellClose.Value; set => _sellClose.Value = value; }

	public ColorZerolagMomentumOsmaStrategy()
	{
		var periods = new[] { 5, 8, 13, 21, 34 };
		for (var i = 0; i < 5; i++)
		{
			_periods[i] = Param("MomentumPeriod" + (i + 1), periods[i]).SetGreaterThanZero()
				.SetDisplay("Momentum period " + (i + 1), "Price difference lookback", "Indicator");
			_factors[i] = Param("Factor" + (i + 1), 1m)
				.SetDisplay("Factor " + (i + 1), "Component weight", "Indicator");
		}
		_smoothing1 = Param(nameof(Smoothing1), 9).SetGreaterThanZero()
			.SetDisplay("Smoothing 1", "Slow trend EMA", "Indicator");
		_smoothing2 = Param(nameof(Smoothing2), 5).SetGreaterThanZero()
			.SetDisplay("Smoothing 2", "OSMA EMA", "Indicator");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Signal candles", "General");
		_buyOpen = Param(nameof(BuyOpen), true).SetDisplay("Buy Open", "Enable long entries", "Trading");
		_sellOpen = Param(nameof(SellOpen), true).SetDisplay("Sell Open", "Enable short entries", "Trading");
		_buyClose = Param(nameof(BuyClose), true).SetDisplay("Buy Close", "Enable long exits", "Trading");
		_sellClose = Param(nameof(SellClose), true).SetDisplay("Sell Close", "Enable short exits", "Trading");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	private void ResetHistory()
	{
		_closes.Clear();
		_slow = _smoothed = _last = _prev2 = _prev3 = null;
		_pending = null;
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		ResetHistory();
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		ResetHistory();
		SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;
		var max = _periods.Max(p => p.Value);
		_closes.Enqueue(candle.ClosePrice);
		if (_closes.Count > max + 1)
			_closes.Dequeue();
		if (_closes.Count < max + 1)
			return;

		var closes = _closes.ToArray();
		var fast = 0m;
		for (var i = 0; i < 5; i++)
		{
			// Matches StockSharp Momentum: close[t] - close[t - period].
			var period = _periods[i].Value;
			fast += (candle.ClosePrice - closes[closes.Length - period - 1]) * _factors[i].Value;
		}
		var slow = _slow is decimal oldSlow
			? oldSlow + 2m / (Smoothing1 + 1m) * (fast - oldSlow)
			: fast;
		var rawOsma = fast - slow;
		var osma = _smoothed is decimal oldOsma
			? oldOsma + 2m / (Smoothing2 + 1m) * (rawOsma - oldOsma)
			: rawOsma;

		// The README refers to t-2 against t-3, then t against t-2.
		var buy = _prev3 is decimal third && _prev2 is decimal second
			&& second < third && osma > second;
		var sell = _prev3 is decimal thirdDown && _prev2 is decimal secondDown
			&& secondDown > thirdDown && osma < secondDown;
		_prev3 = _prev2;
		_prev2 = _last;
		_last = _smoothed = osma;
		_slow = slow;

		if (!IsFormedAndOnlineAndAllowTrading()
			|| (_pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed)))
			return;
		if (buy)
		{
			if (Position < 0m && SellClose)
				_pending = BuyMarket(-Position + (BuyOpen ? Volume : 0m));
			else if (Position == 0m && BuyOpen)
				_pending = BuyMarket(Volume);
		}
		else if (sell)
		{
			if (Position > 0m && BuyClose)
				_pending = SellMarket(Position + (SellOpen ? Volume : 0m));
			else if (Position == 0m && SellOpen)
				_pending = SellMarket(Volume);
		}
	}
}