Ver no GitHub

Exp Sinewave2 X2 Strategy

Overview

Exp Sinewave2 X2 is a multi-timeframe trend-following strategy inspired by John Ehlers' Sinewave analysis. The higher timeframe filter defines the dominant direction, while the lower timeframe provides precise entry and exit triggers. All calculations use the reconstructed Sinewave2 indicator, which internally relies on the adaptive CyclePeriod module.

Indicators

  • Higher timeframe Sinewave2 (lead vs. sine line) – detects bullish or bearish bias using the lead sine cross over the main sine component.
  • Lower timeframe Sinewave2 – monitors the most recent crossover events to trigger trades aligned with the higher timeframe direction.

Trading Logic

  1. Trend filter
    • Compute Sinewave2 on the higher timeframe.
    • Evaluate the lead and main lines SignalBarHigh bars back.
    • Trend is bullish if Lead > Sine, bearish if Lead < Sine, otherwise neutral.
  2. Entry signals
    • Wait for a finished candle on the lower timeframe.
    • Retrieve lead and sine values at offsets defined by SignalBarLow (current) and SignalBarLow + 1 (previous).
    • Long entry: previous crossover was downward (Lead > Sine previously, Lead <= Sine now) while the higher timeframe trend is bullish and EnableBuyOpen is enabled.
    • Short entry: previous crossover was upward (Lead < Sine previously, Lead >= Sine now) while the higher timeframe trend is bearish and EnableSellOpen is enabled.
  3. Exit rules
    • Lower timeframe exit booleans EnableBuyCloseLower and EnableSellCloseLower close positions on opposing crossovers.
    • Higher timeframe exit booleans EnableBuyCloseTrend and EnableSellCloseTrend close positions immediately whenever the major trend flips against the open direction.
    • Protective stop loss and take profit are evaluated each candle using intrabar highs/lows and the StopLossPoints / TakeProfitPoints distances expressed in price steps.
  4. Risk management
    • Position reversals size new orders as Volume + |Position| to flatten the existing position before establishing the new one.
    • After each entry SetRiskLevels recalculates absolute stop/target prices using Security.PriceStep (fallback 1 when unavailable).

Parameters

Name Description
AlphaHigh Alpha factor for the higher timeframe Sinewave2 filter.
AlphaLow Alpha factor for the lower timeframe Sinewave2 trigger.
SignalBarHigh Number of bars back on the higher timeframe used to read the trend state.
SignalBarLow Number of bars back on the lower timeframe used to read crossover states.
EnableBuyOpen / EnableSellOpen Allow long/short entries from lower timeframe signals.
EnableBuyCloseTrend / EnableSellCloseTrend Force exits when the higher timeframe flips against the position.
EnableBuyCloseLower / EnableSellCloseLower Close positions on lower timeframe opposite crossovers.
StopLossPoints Stop-loss distance expressed in instrument price steps.
TakeProfitPoints Take-profit distance expressed in instrument price steps.
HigherCandleType / LowerCandleType Candle data types (timeframes) for the filter and trigger streams.

Notes

  • The strategy processes only finished candles and ignores partial updates.
  • The adaptive Sinewave2 implementation uses the original CyclePeriod algorithm to remain faithful to the MQL version.
  • When the higher and lower candle types are identical, both indicators share a single candle subscription to avoid redundant data requests.
  • Adjust Volume in the base Strategy to control trade size before deployment.
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>
/// Ehlers Sinewave X2 strategy (simplified). Uses CCI oscillator for entries.
/// </summary>
public class ExpSinewave2X2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciLength;
	private readonly StrategyParam<int> _emaLength;

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

	public int CciLength
	{
		get => _cciLength.Value;
		set => _cciLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public ExpSinewave2X2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_cciLength = Param(nameof(CciLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Length", "CCI period", "Indicators");

		_emaLength = Param(nameof(EmaLength), 30)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "Trend filter EMA", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var cci = new CommodityChannelIndex { Length = CciLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };

		decimal prevCci = 0;
		bool hasPrev = false;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(cci, ema, (ICandleMessage candle, decimal cciValue, decimal emaValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!hasPrev)
				{
					prevCci = cciValue;
					hasPrev = true;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevCci = cciValue;
					return;
				}

				// CCI crosses above -100 with price above EMA
				if (prevCci < -100 && cciValue >= -100 && candle.ClosePrice > emaValue && Position <= 0)
				{
					BuyMarket();
				}
				// CCI crosses below 100 with price below EMA
				else if (prevCci > 100 && cciValue <= 100 && candle.ClosePrice < emaValue && Position >= 0)
				{
					SellMarket();
				}

				prevCci = cciValue;
			})
			.Start();

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