GitHub で見る

XMA Candles戦略

説明

XMA Candles戦略は、始値と終値のXMA(指数移動平均)から計算された平滑化足の方向を監視します。平滑化始値が平滑化終値より低い場合、足は強気とみなされ、平滑化始値が平滑化終値より高い場合は弱気とみなされます。この戦略は、これらの平滑化足の色変化に反応します。

  • 非強気の足の後に新しい強気の足が現れると、戦略はショートポジションをクローズしてロングポジションをオープンします。
  • 非弱気の足の後に新しい弱気の足が現れると、戦略はロングポジションをクローズしてショートポジションをオープンします。

パラメーター

  • Length – 始値と終値を平滑化する期間数。
  • CandleType – 計算に使用する足の時間軸。
  • BuyPosOpen – ロングポジションのオープンを許可する。
  • SellPosOpen – ショートポジションのオープンを許可する。
  • BuyPosClose – 弱気シグナル出現時にロングポジションのクローズを許可する。
  • SellPosClose – 強気シグナル出現時にショートポジションのクローズを許可する。
  • StopLoss – 保護ストップ(パーセント単位)。
  • TakeProfit – 利益目標(パーセント単位)。

取引ルール

  1. 選択した時間軸の各足が確定するのを待つ。
  2. 始値と終値の指数移動平均を計算する。
  3. 足の色を判定する:
    • 平滑化始値 < 平滑化終値の場合、緑(強気)。
    • 平滑化始値 > 平滑化終値の場合、赤(弱気)。
  4. 色が強気に変わった場合、ショートをクローズし、任意でロングポジションをオープンする。
  5. 色が弱気に変わった場合、ロングをクローズし、任意でショートポジションをオープンする。
  6. 保護ストップと目標は組み込みのリスク管理によって管理される。

この戦略は、MQL5の元のエキスパート「Exp_XMACandles」を変換したものです。

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 XMA Candles indicator.
/// Opens a long position when smoothed candles turn bullish and
/// opens a short position when smoothed candles turn bearish.
/// </summary>
public class XmaCandlesStrategy : Strategy
{
	private readonly StrategyParam<int> _length;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<bool> _buyPosOpen;
	private readonly StrategyParam<bool> _sellPosOpen;
	private readonly StrategyParam<bool> _buyPosClose;
	private readonly StrategyParam<bool> _sellPosClose;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;

	private ExponentialMovingAverage _openMa;
	private ExponentialMovingAverage _closeMa;
	private int _prevColor = -1;

	/// <summary>
	/// Length of smoothing for moving averages.
	/// </summary>
	public int Length
	{
		get => _length.Value;
		set => _length.Value = value;
	}

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

	/// <summary>
	/// Allows opening long positions.
	/// </summary>
	public bool BuyPosOpen
	{
		get => _buyPosOpen.Value;
		set => _buyPosOpen.Value = value;
	}

	/// <summary>
	/// Allows opening short positions.
	/// </summary>
	public bool SellPosOpen
	{
		get => _sellPosOpen.Value;
		set => _sellPosOpen.Value = value;
	}

	/// <summary>
	/// Allows closing long positions.
	/// </summary>
	public bool BuyPosClose
	{
		get => _buyPosClose.Value;
		set => _buyPosClose.Value = value;
	}

	/// <summary>
	/// Allows closing short positions.
	/// </summary>
	public bool SellPosClose
	{
		get => _sellPosClose.Value;
		set => _sellPosClose.Value = value;
	}

	/// <summary>
	/// Stop loss in percent.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Take profit in percent.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

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

	/// <summary>
	/// Initialize <see cref="XmaCandlesStrategy"/>.
	/// </summary>
	public XmaCandlesStrategy()
	{
		_length = Param(nameof(Length), 12)
			.SetDisplay("Length", "Smoothing length", "Parameters")
			.SetGreaterThanZero();

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

		_buyPosOpen = Param(nameof(BuyPosOpen), true)
			.SetDisplay("Buy Open", "Allow opening long positions", "Parameters");

		_sellPosOpen = Param(nameof(SellPosOpen), true)
			.SetDisplay("Sell Open", "Allow opening short positions", "Parameters");

		_buyPosClose = Param(nameof(BuyPosClose), true)
			.SetDisplay("Buy Close", "Allow closing long positions", "Parameters");

		_sellPosClose = Param(nameof(SellPosClose), true)
			.SetDisplay("Sell Close", "Allow closing short positions", "Parameters");

		_stopLoss = Param(nameof(StopLoss), 2m)
			.SetDisplay("Stop Loss %", "Stop loss in percent", "Protection");

		_takeProfit = Param(nameof(TakeProfit), 4m)
			.SetDisplay("Take Profit %", "Take profit in percent", "Protection");
	}

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

		_prevColor = -1;
	}

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

		_prevColor = -1;

		_openMa = new EMA { Length = Length };
		_closeMa = new EMA { Length = Length };

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

		StartProtection(
			stopLoss: new Unit(StopLoss, UnitTypes.Percent),
			takeProfit: new Unit(TakeProfit, UnitTypes.Percent)
		);
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		// process only finished candles
		if (candle.State != CandleStates.Finished)
			return;

		var t = candle.ServerTime;
		var openValue = _openMa.Process(candle.OpenPrice, t, true);
		var closeValue = _closeMa.Process(candle.ClosePrice, t, true);

		if (!_openMa.IsFormed || !_closeMa.IsFormed)
			return;

		var openMa = openValue.GetValue<decimal>();
		var closeMa = closeValue.GetValue<decimal>();

		// determine candle color based on smoothed values
		var currentColor = openMa < closeMa ? 2 : openMa > closeMa ? 0 : 1;

		if (currentColor == 2 && _prevColor != 2)
		{
			// bullish change
			if (Position <= 0)
				BuyMarket();
		}
		else if (currentColor == 0 && _prevColor != 0)
		{
			// bearish change
			if (Position >= 0)
				SellMarket();
		}

		_prevColor = currentColor;
	}
}