Ver en GitHub

Estrategia de Media Móvil Adaptativa de Kaufman

Esta estrategia utiliza la Media Móvil Adaptativa de Kaufman (KAMA). Se abre una posición larga cuando la KAMA sube durante un número específico de barras y se cierra cualquier posición corta. Se abre una posición corta cuando la KAMA cae durante el período requerido y se cierra cualquier posición larga.

Parámetros

  • Tipo de vela
  • Longitud
  • Período rápido
  • Período lento
  • Período de subida
  • Período de caída
  • Dirección de la orden
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>
/// Kaufman Adaptive Moving Average (KAMA) strategy.
/// </summary>
public class KaufmanAdaptiveMovingAverageStrategy : Strategy
{
	public enum TradeSides
	{
		Long,
		Short,
		Both
	}

	private readonly StrategyParam<int> _length;
	private readonly StrategyParam<int> _fast;
	private readonly StrategyParam<int> _slow;
	private readonly StrategyParam<int> _risingPeriod;
	private readonly StrategyParam<int> _fallingPeriod;
	private readonly StrategyParam<TradeSides> _orderDirection;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevKama;
	private int _risingCount;
	private int _fallingCount;
	private bool _isFirst = true;
	private bool _wasRising;
	private bool _wasFalling;

	public int Length { get => _length.Value; set => _length.Value = value; }
	public int Fast { get => _fast.Value; set => _fast.Value = value; }
	public int Slow { get => _slow.Value; set => _slow.Value = value; }
	public int RisingPeriod { get => _risingPeriod.Value; set => _risingPeriod.Value = value; }
	public int FallingPeriod { get => _fallingPeriod.Value; set => _fallingPeriod.Value = value; }
	public TradeSides OrderDirection { get => _orderDirection.Value; set => _orderDirection.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public KaufmanAdaptiveMovingAverageStrategy()
	{
		_length = Param(nameof(Length), 10)
			.SetGreaterThanZero()
			.SetDisplay("Length", "KAMA lookback period", "KAMA")
			
			.SetOptimize(5, 20, 5);

		_fast = Param(nameof(Fast), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast period", "Fast EMA length for KAMA", "KAMA")
			
			.SetOptimize(2, 10, 1);

		_slow = Param(nameof(Slow), 50)
			.SetGreaterThanZero()
			.SetDisplay("Slow period", "Slow EMA length for KAMA", "KAMA")
			
			.SetOptimize(20, 100, 10);

		_risingPeriod = Param(nameof(RisingPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Rising period", "Bars for KAMA rising condition", "Strategy")
			
			.SetOptimize(5, 20, 5);

		_fallingPeriod = Param(nameof(FallingPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Falling period", "Bars for KAMA falling condition", "Strategy")
			
			.SetOptimize(5, 20, 5);

		_orderDirection = Param(nameof(OrderDirection), TradeSides.Long)
			.SetDisplay("Order direction", "Allowed trade direction", "Strategy");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevKama = default;
		_risingCount = 0;
		_fallingCount = 0;
		_isFirst = true;
		_wasRising = false;
		_wasFalling = false;
	}

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

		var kama = new KaufmanAdaptiveMovingAverage
		{
			Length = Length,
			FastSCPeriod = Fast,
			SlowSCPeriod = Slow
		};

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

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

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

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

		if (kamaValue > _prevKama)
		{
			_risingCount++;
			_fallingCount = 0;
		}
		else if (kamaValue < _prevKama)
		{
			_fallingCount++;
			_risingCount = 0;
		}
		else
		{
			_risingCount = 0;
			_fallingCount = 0;
		}

		var isRising = _risingCount >= RisingPeriod;
		var isFalling = _fallingCount >= FallingPeriod;
		var risingEdge = isRising && !_wasRising;
		var fallingEdge = isFalling && !_wasFalling;

		if (risingEdge)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));

			var allowLong = OrderDirection == TradeSides.Long || OrderDirection == TradeSides.Both;
			if (allowLong && Position == 0)
				BuyMarket(Volume);
		}

		if (fallingEdge)
		{
			if (Position > 0)
				SellMarket(Math.Abs(Position));

			var allowShort = OrderDirection == TradeSides.Short || OrderDirection == TradeSides.Both;
			if (allowShort && Position == 0)
				SellMarket(Volume);
		}

		_wasRising = isRising;
		_wasFalling = isFalling;
		_prevKama = kamaValue;
	}
}