GitHub で見る

Fast2 Crossover Strategy

Strategy based on the Fast2 histogram. The histogram combines the body of the last three candles with square‑root weights and applies two weighted moving averages. A long position is opened when the fast average crosses below the slow one, and a short position when it crosses above.

Details

  • Entry Criteria:
    • Long: fast WMA crosses below slow WMA
    • Short: fast WMA crosses above slow WMA
  • Long/Short: Both
  • Exit Criteria:
    • Opposite crossover
  • Stops: None
  • Default Values:
    • FastLength = 3
    • SlowLength = 9
    • CandleType = TimeSpan.FromHours(8).TimeFrame()
  • Filters:
    • Category: Crossover
    • Direction: Both
    • Indicators: WeightedMovingAverage
    • Stops: No
    • Complexity: Basic
    • Timeframe: Mid-term
    • Seasonality: No
    • Neural Networks: No
    • Divergence: No
    • Risk Level: Medium
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>
/// Trading strategy based on Fast2 histogram moving average crossover.
/// Uses weighted candle body differences with WMA smoothing.
/// </summary>
public class Fast2CrossoverStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrevAverage;
	private decimal _prevDiff1;
	private decimal _prevDiff2;
	private bool _hasPrevDiff1;
	private bool _hasPrevDiff2;

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

	/// <summary>Fast WMA length.</summary>
	public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }

	/// <summary>Slow WMA length.</summary>
	public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }

	public Fast2CrossoverStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame());
		_fastLength = Param(nameof(FastLength), 5).SetDisplay("Fast length", "Fast length", "General");
		_slowLength = Param(nameof(SlowLength), 13).SetDisplay("Slow length", "Slow length", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = default;
		_prevSlow = default;
		_hasPrevAverage = default;
		_prevDiff1 = default;
		_prevDiff2 = default;
		_hasPrevDiff1 = default;
		_hasPrevDiff2 = default;
	}

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

		var fast = new WeightedMovingAverage { Length = FastLength };
		var slow = new WeightedMovingAverage { Length = SlowLength };
		Indicators.Add(fast);
		Indicators.Add(slow);

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(candle =>
		{
			if (candle.State != CandleStates.Finished)
				return;

			var diff = candle.ClosePrice - candle.OpenPrice;
			var hist = diff;
			if (_hasPrevDiff1)
				hist += _prevDiff1 / (decimal)Math.Sqrt(2);
			if (_hasPrevDiff2)
				hist += _prevDiff2 / (decimal)Math.Sqrt(3);

			var fastValue = fast.Process(hist, candle.OpenTime, true);
			var slowValue = slow.Process(hist, candle.OpenTime, true);

			_prevDiff2 = _prevDiff1;
			_prevDiff1 = diff;
			_hasPrevDiff2 = _hasPrevDiff1;
			_hasPrevDiff1 = true;

			if (fastValue.IsEmpty || slowValue.IsEmpty || !fast.IsFormed || !slow.IsFormed)
				return;

			if (!IsFormedAndOnlineAndAllowTrading())
				return;

			var f = fastValue.ToDecimal();
			var s = slowValue.ToDecimal();

			if (_hasPrevAverage)
			{
				if (_prevFast > _prevSlow && f < s && Position <= 0)
					BuyMarket();

				if (_prevFast < _prevSlow && f > s && Position >= 0)
					SellMarket();
			}

			_prevFast = f;
			_prevSlow = s;
			_hasPrevAverage = true;
		}).Start();
	}
}