Ver en GitHub

XMA Candles Strategy

Description

The XMA Candles strategy monitors the direction of smoothed candles calculated from the XMA (Exponential Moving Average) of open and close prices. A candle is considered bullish when the smoothed open price is below the smoothed close price, and bearish when the smoothed open price is above the smoothed close price. The strategy reacts to color changes of these smoothed candles.

  • When a new bullish candle appears after a non-bullish one, the strategy closes any short position and opens a long position.
  • When a new bearish candle appears after a non-bearish one, the strategy closes any long position and opens a short position.

Parameters

  • Length – number of periods for smoothing open and close prices.
  • CandleType – timeframe of candles used for calculations.
  • BuyPosOpen – allow opening long positions.
  • SellPosOpen – allow opening short positions.
  • BuyPosClose – allow closing long positions when bearish signal appears.
  • SellPosClose – allow closing short positions when bullish signal appears.
  • StopLoss – protective stop in percent.
  • TakeProfit – profit target in percent.

Trading Rules

  1. Wait for each candle of the selected timeframe to finish.
  2. Calculate exponential moving averages for open and close prices.
  3. Determine candle color:
    • Green (bullish) if smoothed open < smoothed close.
    • Red (bearish) if smoothed open > smoothed close.
  4. If color changes to bullish, close shorts and optionally open a long position.
  5. If color changes to bearish, close longs and optionally open a short position.
  6. Protective stops and targets are managed by built‑in risk controls.

This strategy is a conversion of the original MQL5 expert "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;
	}
}