Открыть на GitHub

Стратегия JMA Candle Sign

Стратегия использует две скользящие средние Jurik (JMA), рассчитанные по ценам открытия и закрытия свечи. Когда JMA по цене открытия пересекает JMA по цене закрытия снизу вверх, формируется сигнал на покупку. Если JMA по цене открытия пересекает JMA по цене закрытия сверху вниз, появляется сигнал на продажу.

По умолчанию используется таймфрейм H4 и период JMA равный 7. Уровни стоп-лосса и тейк-профита задаются в пунктах и применяются встроенной системой управления рисками. Стратегия работает только с завершёнными свечами и удерживает не более одной позиции.

Параметры

  • JMA Length – период обеих JMA.
  • Candle Type – таймфрейм обрабатываемых свечей.
  • Take Profit – цель прибыли в пунктах.
  • Stop Loss – максимально допустимый убыток в пунктах.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy using Jurik moving averages of open and close prices.
/// Goes long when JMA(close) crosses above JMA(open).
/// Goes short when JMA(close) crosses below JMA(open).
/// </summary>
public class JmaCandleSignStrategy : Strategy
{
	private readonly StrategyParam<int> _jmaLength;
	private readonly StrategyParam<DataType> _candleType;

	private JurikMovingAverage _jmaOpen;
	private JurikMovingAverage _jmaClose;
	private decimal _prevOpenJma;
	private decimal _prevCloseJma;
	private bool _hasPrev;

	public int JmaLength
	{
		get => _jmaLength.Value;
		set => _jmaLength.Value = value;
	}

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

	public JmaCandleSignStrategy()
	{
		_jmaLength = Param(nameof(JmaLength), 7)
			.SetDisplay("JMA Length", "Period for Jurik moving averages", "Parameters");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for strategy", "Parameters");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_jmaOpen = default;
		_jmaClose = default;
		_prevOpenJma = 0;
		_prevCloseJma = 0;
		_hasPrev = false;
	}

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

		_jmaOpen = new JurikMovingAverage { Length = JmaLength };
		_jmaClose = new JurikMovingAverage { Length = JmaLength };
		_prevOpenJma = 0;
		_prevCloseJma = 0;
		_hasPrev = false;

		Indicators.Add(_jmaOpen);
		Indicators.Add(_jmaClose);

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

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

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

		var openResult = _jmaOpen.Process(new DecimalIndicatorValue(_jmaOpen, candle.OpenPrice, candle.OpenTime) { IsFinal = true });
		var closeResult = _jmaClose.Process(new DecimalIndicatorValue(_jmaClose, candle.ClosePrice, candle.OpenTime) { IsFinal = true });

		if (!openResult.IsFormed || !closeResult.IsFormed)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var openJma = openResult.ToDecimal();
		var closeJma = closeResult.ToDecimal();

		if (!_hasPrev)
		{
			_prevOpenJma = openJma;
			_prevCloseJma = closeJma;
			_hasPrev = true;
			return;
		}

		// JMA(close) crosses above JMA(open) - bullish
		if (_prevCloseJma <= _prevOpenJma && closeJma > openJma && Position <= 0)
		{
			BuyMarket();
		}
		// JMA(close) crosses below JMA(open) - bearish
		else if (_prevCloseJma >= _prevOpenJma && closeJma < openJma && Position >= 0)
		{
			SellMarket();
		}

		_prevOpenJma = openJma;
		_prevCloseJma = closeJma;
	}
}