Auf GitHub ansehen

Kauf WMA Cross Strategy

This strategy trades when the Kaufman Adaptive Moving Average (KAMA) crosses a Weighted Moving Average (WMA). An upward cross closes short positions and optionally opens a long if allowed. A downward cross closes long positions and optionally opens a short. Opening and closing for each side can be enabled or disabled through parameters.

Parameters

  • Candle type
  • AMA length
  • Fast EMA
  • Slow EMA
  • WMA length
  • Open long
  • Open short
  • Close long
  • Close short
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>
/// Trades when Kaufman Adaptive Moving Average crosses Weighted Moving Average.
/// </summary>
public class KaufWmaCrossStrategy : Strategy
{
	private readonly StrategyParam<int> _amaPeriod;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _wmaPeriod;
	private readonly StrategyParam<bool> _buyOpen;
	private readonly StrategyParam<bool> _sellOpen;
	private readonly StrategyParam<bool> _buyClose;
	private readonly StrategyParam<bool> _sellClose;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _minSpreadPercent;
	private readonly StrategyParam<int> _cooldownBars;

	private decimal _prevKama;
	private decimal _prevWma;
	private bool _isFirst = true;
	private int _cooldownRemaining;

	public int AmaPeriod { get => _amaPeriod.Value; set => _amaPeriod.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int WmaPeriod { get => _wmaPeriod.Value; set => _wmaPeriod.Value = value; }
	public bool BuyOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
	public bool SellOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
	public bool BuyClose { get => _buyClose.Value; set => _buyClose.Value = value; }
	public bool SellClose { get => _sellClose.Value; set => _sellClose.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public decimal MinSpreadPercent { get => _minSpreadPercent.Value; set => _minSpreadPercent.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }

	public KaufWmaCrossStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle type", "Type of candles", "General");

		_amaPeriod = Param(nameof(AmaPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("AMA length", "Kaufman AMA period", "Indicators");

		_fastPeriod = Param(nameof(FastPeriod), 2)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast smoothing period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow smoothing period", "Indicators");

		_wmaPeriod = Param(nameof(WmaPeriod), 13)
			.SetGreaterThanZero()
			.SetDisplay("WMA length", "Weighted MA period", "Indicators");

		_buyOpen = Param(nameof(BuyOpen), true)
			.SetDisplay("Open long", "Allow opening long position", "Signals");

		_sellOpen = Param(nameof(SellOpen), true)
			.SetDisplay("Open short", "Allow opening short position", "Signals");

		_buyClose = Param(nameof(BuyClose), true)
			.SetDisplay("Close long", "Allow closing long on sell signal", "Signals");

		_sellClose = Param(nameof(SellClose), true)
			.SetDisplay("Close short", "Allow closing short on buy signal", "Signals");

		_minSpreadPercent = Param(nameof(MinSpreadPercent), 0.0008m)
			.SetDisplay("Minimum Spread %", "Minimum normalized spread between AMA and WMA", "Filters");

		_cooldownBars = Param(nameof(CooldownBars), 4)
			.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevKama = 0m;
		_prevWma = 0m;
		_isFirst = true;
		_cooldownRemaining = 0;
	}

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

		var kama = new KaufmanAdaptiveMovingAverage
		{
			Length = AmaPeriod,
			FastSCPeriod = FastPeriod,
			SlowSCPeriod = SlowPeriod
		};

		var wma = new WeightedMovingAverage { Length = WmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(kama, wma, ProcessCandle).Start();

		StartProtection(null, null);

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

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

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		if (_isFirst)
		{
			_prevKama = kamaValue;
			_prevWma = wmaValue;
			_isFirst = false;
			return;
		}

		var spreadPercent = candle.ClosePrice != 0m ? Math.Abs(kamaValue - wmaValue) / candle.ClosePrice : 0m;
		var crossUp = _prevKama <= _prevWma && kamaValue > wmaValue && spreadPercent >= MinSpreadPercent;
		var crossDown = _prevKama >= _prevWma && kamaValue < wmaValue && spreadPercent >= MinSpreadPercent;

		if (_cooldownRemaining == 0)
		{
			if (crossUp)
			{
				if (SellClose && Position < 0)
					BuyMarket();

				if (BuyOpen && Position <= 0)
				{
					BuyMarket();
					_cooldownRemaining = CooldownBars;
				}
			}
			else if (crossDown)
			{
				if (BuyClose && Position > 0)
					SellMarket();

				if (SellOpen && Position >= 0)
				{
					SellMarket();
					_cooldownRemaining = CooldownBars;
				}
			}
		}

		_prevKama = kamaValue;
		_prevWma = wmaValue;
	}
}