Ver en GitHub

Estrategia de Cruce de Nube TSI

La Estrategia de Cruce de Nube TSI compara el True Strength Index (TSI) con una copia retardada de sí mismo para formar una nube. Se abre una posición larga cuando el TSI cruza por encima de la línea desplazada, indicando impulso alcista. Se abre una posición corta cuando el TSI cruza por debajo de la línea desplazada. Las señales pueden invertirse y las posiciones opuestas pueden cerrarse opcionalmente.

Detalles

  • Criterios de entrada:
    • TSI cruza por encima de su valor desplazado (largo).
    • TSI cruza por debajo de su valor desplazado (corto).
  • Largo/Corto: Ambos.
  • Criterios de salida:
    • Cierre opcional con señal opuesta.
  • Stops: Ninguno.
  • Valores predeterminados:
    • LongLength = 25
    • ShortLength = 13
    • TriggerShift = 1
    • Invert = false
  • Filtros:
    • Categoría: Oscilador de momentum
    • Dirección: Largo/Corto
    • Indicadores: True Strength Index
    • Stops: No
    • Complejidad: Bajo
    • Marco temporal: Cualquiera
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Bajo
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>
/// True Strength Index cross with shifted TSI line.
/// Opens long when TSI crosses above its shifted value and short on opposite cross.
/// </summary>
public class TsiCloudCrossStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _firstLength;
	private readonly StrategyParam<int> _secondLength;
	private readonly StrategyParam<int> _triggerShift;

	private TrueStrengthIndex _tsi;
	private readonly Queue<decimal> _tsiValues = new();
	private decimal _prevTsi;
	private decimal _prevTrigger;
	private bool _isInitialized;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FirstLength { get => _firstLength.Value; set => _firstLength.Value = value; }
	public int SecondLength { get => _secondLength.Value; set => _secondLength.Value = value; }
	public int TriggerShift { get => _triggerShift.Value; set => _triggerShift.Value = value; }

	public TsiCloudCrossStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
		_firstLength = Param(nameof(FirstLength), 25)
			.SetDisplay("First Length", "First smoothing period for TSI", "TSI")
			.SetGreaterThanZero();
		_secondLength = Param(nameof(SecondLength), 13)
			.SetDisplay("Second Length", "Second smoothing period for TSI", "TSI")
			.SetGreaterThanZero();
		_triggerShift = Param(nameof(TriggerShift), 1)
			.SetDisplay("Trigger Shift", "Bars to shift TSI for trigger", "TSI")
			.SetGreaterThanZero();
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_tsi = null;
		_tsiValues.Clear();
		_prevTsi = 0;
		_prevTrigger = 0;
		_isInitialized = false;
	}

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

		_tsiValues.Clear();
		_isInitialized = false;

		_tsi = new TrueStrengthIndex
		{
			FirstLength = FirstLength,
			SecondLength = SecondLength,
		};

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

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

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

		if (value is not ITrueStrengthIndexValue tsiVal || tsiVal.Tsi is not decimal tsiValue)
			return;

		if (!_tsi.IsFormed)
			return;

		_tsiValues.Enqueue(tsiValue);
		if (_tsiValues.Count > TriggerShift + 1)
			_tsiValues.Dequeue();

		if (_tsiValues.Count < TriggerShift + 1)
		{
			_prevTsi = tsiValue;
			_prevTrigger = tsiValue;
			return;
		}

		var trigger = _tsiValues.Peek();

		if (!_isInitialized)
		{
			_prevTsi = tsiValue;
			_prevTrigger = trigger;
			_isInitialized = true;
			return;
		}

		var crossUp = _prevTsi <= _prevTrigger && tsiValue > trigger;
		var crossDown = _prevTsi >= _prevTrigger && tsiValue < trigger;

		_prevTsi = tsiValue;
		_prevTrigger = trigger;

		if (crossUp && Position <= 0)
			BuyMarket();
		else if (crossDown && Position >= 0)
			SellMarket();
	}
}