Ver no GitHub

Close Cross MA Strategy

This strategy monitors a simple moving average (MA) and automatically closes any open position when the candle close crosses the MA line. It is designed for traders who manage entries manually or with other systems but want an automated exit once the trend reverses.

The logic tracks the relationship between the close price and the MA. When a new finished candle crosses from one side of the MA to the other, the strategy sends a market order to flatten the position. No new positions are opened.

Details

  • Entry Criteria: None. Positions must be opened externally.
  • Exit Criteria:
    • Long: Previous close above MA and current close below MA triggers a sell to close.
    • Short: Previous close below MA and current close above MA triggers a buy to close.
  • Long/Short: Both directions are supported.
  • Stops: Not used. The MA cross acts as the exit signal.
  • Default Values:
    • MA Period = 50.
    • Candle Type = Time frame of 1 minute.
  • Filters:
    • Category: Trend following
    • Direction: Both
    • Indicators: Single
    • Stops: No
    • Complexity: Simple
    • Timeframe: Any
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk level: Moderate
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>
/// Price cross MA strategy.
/// </summary>
public class CloseCrossMaStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevDiff;
	private bool _hasPrev;

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

	public CloseCrossMaStrategy()
	{
		_maPeriod = Param(nameof(MaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "EMA period", "Parameters");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle type", "General");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevDiff = 0;
		_hasPrev = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = MaPeriod };

		SubscribeCandles(CandleType)
			.Bind(ema, ProcessCandle)
			.Start();
	}

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

		var diff = candle.ClosePrice - emaVal;

		if (!_hasPrev)
		{
			_prevDiff = diff;
			_hasPrev = true;
			return;
		}

		// Price crosses above EMA
		if (_prevDiff <= 0 && diff > 0 && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		// Price crosses below EMA
		else if (_prevDiff >= 0 && diff < 0 && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}

		_prevDiff = diff;
	}
}