Ver no GitHub

Color Schaff WPR Trend Cycle Strategy

This strategy implements the Color Schaff WPR Trend Cycle expert from MetaTrader. It uses the Schaff Trend Cycle calculated from fast and slow Williams %R values to detect market turns.

The algorithm works on finished candles only. When the indicator value crosses above the upper level, the strategy opens a long position and closes any existing short position. When the value crosses below the lower level, it opens a short position and closes any existing long position.

Parameters

  • Fast WPR – period for the fast Williams %R calculation.
  • Slow WPR – period for the slow Williams %R calculation.
  • Cycle – cycle length used in the Schaff Trend calculation.
  • High Level – upper trigger level for long entries.
  • Low Level – lower trigger level for short entries.
  • Candle Type – timeframe of candles for indicator evaluation.
  • Original MQL source: MQL/13489/mql5/Experts/exp_colorschaffwprtrendcycle.mq5
  • Indicator: MQL/13489/mql5/Indicators/colorschaffwprtrendcycle.mq5
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>
/// Strategy based on the Schaff Trend Cycle calculated from WPR.
/// </summary>
public class ColorSchaffWprTrendCycleStrategy : Strategy
{
	private readonly StrategyParam<int> _fastWpr;
	private readonly StrategyParam<int> _slowWpr;
	private readonly StrategyParam<int> _cycle;
	private readonly StrategyParam<int> _highLevel;
	private readonly StrategyParam<int> _lowLevel;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevStc;

	public int FastWpr { get => _fastWpr.Value; set => _fastWpr.Value = value; }
	public int SlowWpr { get => _slowWpr.Value; set => _slowWpr.Value = value; }
	public int Cycle { get => _cycle.Value; set => _cycle.Value = value; }
	public int HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
	public int LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ColorSchaffWprTrendCycleStrategy()
	{
		_fastWpr = Param(nameof(FastWpr), 23)
			.SetDisplay("Fast WPR", "Fast Williams %R period", "Indicator");

		_slowWpr = Param(nameof(SlowWpr), 50)
			.SetDisplay("Slow WPR", "Slow Williams %R period", "Indicator");

		_cycle = Param(nameof(Cycle), 10)
			.SetDisplay("Cycle", "Cycle length", "Indicator");

		_highLevel = Param(nameof(HighLevel), 60)
			.SetDisplay("High Level", "Upper trigger level", "Indicator");

		_lowLevel = Param(nameof(LowLevel), -60)
			.SetDisplay("Low Level", "Lower trigger level", "Indicator");

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

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevStc = 0m;
	}

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

		// Schaff trend cycle indicator using WPR values
		var stc = new SchaffTrendCycle
		{
			Length = Cycle
		};
		stc.Macd.Macd.ShortMa.Length = FastWpr;
		stc.Macd.Macd.LongMa.Length = SlowWpr;

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

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

		var prev = _prevStc;
		_prevStc = stcValue;

		// Generate buy signal when STC crosses above HighLevel
		var crossUp = prev <= HighLevel && stcValue > HighLevel;
		// Generate sell signal when STC crosses below LowLevel
		var crossDown = prev >= LowLevel && stcValue < LowLevel;

		if (crossUp)
		{
			if (Position <= 0)
				BuyMarket(Volume + Math.Abs(Position));
		}
		else if (crossDown)
		{
			if (Position >= 0)
				SellMarket(Volume + Math.Abs(Position));
		}
	}
}