Ver no GitHub

Altcoin Index Correlation Strategy

The strategy compares EMA trends on the traded instrument and a reference index. It opens long when both fast EMAs are above their slow EMAs, and short when both are below. Optional inverse logic allows trading against the index trend or skipping the index completely.

Details

  • Entry Criteria:
    • Fast EMA above slow EMA on both instruments (or opposite if inverse).
  • Long/Short: Both.
  • Exit Criteria:
    • Opposite crossover condition.
  • Stops: None.
  • Default Values:
    • FastEmaLength = 47
    • SlowEmaLength = 50
    • IndexFastEmaLength = 47
    • IndexSlowEmaLength = 50
    • SkipIndexReference = false
    • InverseSignal = false
  • Filters:
    • Category: Trend following
    • Direction: Both
    • Indicators: EMA
    • Stops: No
    • Complexity: Low
    • Timeframe: Any
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk level: Medium
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Altcoin Index Correlation Strategy - trades when EMA trends on the symbol and reference index align.
/// </summary>
public class AltcoinIndexCorrelationStrategy : Strategy
{
	private readonly StrategyParam<int> _fastEmaLen;
	private readonly StrategyParam<int> _slowEmaLen;
	private readonly StrategyParam<int> _indexFastEmaLen;
	private readonly StrategyParam<int> _indexSlowEmaLen;
	private readonly StrategyParam<bool> _skipIndex;
	private readonly StrategyParam<bool> _inverseSignal;
	private readonly StrategyParam<Security> _indexSecurity;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _indexFast;
	private decimal _indexSlow;
	private bool _indexReady;
	private decimal _prevFast;
	private decimal _prevSlow;
	private int _barIndex;
	private int _lastTradeBar;

	/// <summary>
	/// Length of fast EMA for main security.
	/// </summary>
	public int FastEmaLength
	{
		get => _fastEmaLen.Value;
		set => _fastEmaLen.Value = value;
	}

	/// <summary>
	/// Length of slow EMA for main security.
	/// </summary>
	public int SlowEmaLength
	{
		get => _slowEmaLen.Value;
		set => _slowEmaLen.Value = value;
	}

	/// <summary>
	/// Length of fast EMA for reference index.
	/// </summary>
	public int IndexFastEmaLength
	{
		get => _indexFastEmaLen.Value;
		set => _indexFastEmaLen.Value = value;
	}

	/// <summary>
	/// Length of slow EMA for reference index.
	/// </summary>
	public int IndexSlowEmaLength
	{
		get => _indexSlowEmaLen.Value;
		set => _indexSlowEmaLen.Value = value;
	}

	/// <summary>
	/// Skip using reference index in calculations.
	/// </summary>
	public bool SkipIndexReference
	{
		get => _skipIndex.Value;
		set => _skipIndex.Value = value;
	}

	/// <summary>
	/// Inverse correlation logic.
	/// </summary>
	public bool InverseSignal
	{
		get => _inverseSignal.Value;
		set => _inverseSignal.Value = value;
	}

	/// <summary>
	/// Reference index security.
	/// </summary>
	public Security IndexSecurity
	{
		get => _indexSecurity.Value;
		set => _indexSecurity.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes parameters.
	/// </summary>
	public AltcoinIndexCorrelationStrategy()
	{
		_fastEmaLen = Param(nameof(FastEmaLength), 7)
			.SetDisplay("Fast EMA", "Fast EMA length", "EMA Settings")
			.SetOptimize(5, 50, 5);

		_slowEmaLen = Param(nameof(SlowEmaLength), 18)
			.SetDisplay("Slow EMA", "Slow EMA length", "EMA Settings")
			.SetOptimize(10, 100, 5);

		_indexFastEmaLen = Param(nameof(IndexFastEmaLength), 47)
			.SetDisplay("Index Fast EMA", "Fast EMA length for index", "Index Reference")
			
			.SetOptimize(10, 100, 5);

		_indexSlowEmaLen = Param(nameof(IndexSlowEmaLength), 50)
			.SetDisplay("Index Slow EMA", "Slow EMA length for index", "Index Reference")
			
			.SetOptimize(10, 100, 5);

		_skipIndex = Param(nameof(SkipIndexReference), false)
			.SetDisplay("Skip Index", "Ignore index correlation", "Index Reference");

		_inverseSignal = Param(nameof(InverseSignal), false)
			.SetDisplay("Inverse Signal", "Use inverse correlation logic", "Index Reference");

		_indexSecurity = Param<Security>(nameof(IndexSecurity))
			.SetDisplay("Index Security", "Reference index security", "Data");

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

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, CandleType);
		if (IndexSecurity != null)
			yield return (IndexSecurity, CandleType);
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_indexFast = 0m;
		_indexSlow = 0m;
		_indexReady = false;
		_prevFast = 0m;
		_prevSlow = 0m;
		_barIndex = 0;
		_lastTradeBar = 0;
	}

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

		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };

		var mainSub = SubscribeCandles(CandleType);
		mainSub
			.Bind(fastEma, slowEma, ProcessMainCandle)
			.Start();

		if (IndexSecurity != null)
		{
			var indexFastEma = new ExponentialMovingAverage { Length = IndexFastEmaLength };
			var indexSlowEma = new ExponentialMovingAverage { Length = IndexSlowEmaLength };

			var indexSub = SubscribeCandles(CandleType, security: IndexSecurity);
			indexSub
				.Bind(indexFastEma, indexSlowEma, ProcessIndexCandle)
				.Start();
		}

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

	private void ProcessIndexCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_indexFast = fast;
		_indexSlow = slow;
		_indexReady = true;
	}

	private void ProcessMainCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_barIndex++;

		if (_prevFast == 0 || _prevSlow == 0)
		{
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		var cooldownOk = _barIndex - _lastTradeBar > 5;

		// Cross-over detection
		var crossOver = _prevFast <= _prevSlow && fast > slow;
		var crossUnder = _prevFast >= _prevSlow && fast < slow;

		bool goLong;
		bool goShort;

		if (SkipIndexReference || !_indexReady)
		{
			goLong = crossOver;
			goShort = crossUnder;
		}
		else
		{
			goLong = crossOver && _indexFast > _indexSlow;
			goShort = crossUnder && _indexFast < _indexSlow;

			if (InverseSignal)
			{
				goLong = crossOver && _indexFast < _indexSlow;
				goShort = crossUnder && _indexFast > _indexSlow;
			}
		}

		if (goLong && Position <= 0 && cooldownOk)
		{
			BuyMarket();
			_lastTradeBar = _barIndex;
		}
		else if (goShort && Position >= 0 && cooldownOk)
		{
			SellMarket();
			_lastTradeBar = _barIndex;
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}