Ver no GitHub

3MA Bunny Cross Strategy

Overview

The ThreeMaBunnyCrossStrategy is a conversion of the MetaTrader 4 expert advisor "3MA Bunny Cross". It trades trend reversals based on the crossover between two linear weighted moving averages (LWMAs) calculated on the closing prices of the selected timeframe. The StockSharp version keeps the original idea of reversing the position immediately after a crossover and adds high-level API conveniences such as indicator binding and built-in risk protection.

Original MQL Description

The source expert advisor uses two LWMAs with periods 5 and 20. When the fast LWMA crosses the slow LWMA, the advisor closes the opposite position if it exists and immediately opens a new trade in the direction of the crossover. Only one position is allowed at any moment. The original script also checks for a minimum number of bars and free margin before trading.

StockSharp Implementation Details

  • The strategy subscribes to candles defined by the CandleType parameter (15-minute timeframe by default) and binds them to two LinearWeightedMovingAverage indicators.
  • Indicator values are provided directly to the processing method through Bind, removing the need for manual buffer handling.
  • The previous fast and slow values are cached to detect crossovers using the same logic as the MQL version (fast crossing above or below slow).
  • When a bullish crossover occurs and the current position is flat or short, the strategy sends a market buy order sized to both close any short exposure and open a new long (Volume + |Position|). The bearish crossover behaves symmetrically for sells.
  • StartProtection() is called once at start to enable built-in position protection routines.
  • Chart visualization draws the source candles along with the two moving averages and the strategy's own trades.

Parameters

  • CandleType – data type describing the candle series to subscribe to (defaults to 15-minute time frame).
  • FastPeriod – period of the fast LWMA. Default: 5. Optimizable.
  • SlowPeriod – period of the slow LWMA. Default: 20. Optimizable.

Indicators

  • LinearWeightedMovingAverage (fast, period 5 by default).
  • LinearWeightedMovingAverage (slow, period 20 by default).

Trading Rules

  1. Wait for a finished candle and verify that the strategy is formed, online, and allowed to trade.
  2. Detect a bullish crossover when the fast LWMA was below or equal to the slow LWMA on the previous candle and is above or equal to it on the current candle. In this case, close any existing short position and open a long.
  3. Detect a bearish crossover when the fast LWMA was above or equal to the slow LWMA on the previous candle and is below or equal to it on the current candle. In this case, close any existing long position and open a short.
  4. Each new order size is calculated as Volume + |Position| to fully reverse any outstanding exposure, ensuring that only one directional position exists at a time.
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>
/// 3MA Bunny Cross strategy using weighted moving average crossover.
/// Goes long on fast WMA crossing above slow WMA, short on opposite.
/// </summary>
public class ThreeMaBunnyCrossStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ThreeMaBunnyCrossStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetDisplay("Fast WMA", "Fast weighted MA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 20)
			.SetDisplay("Slow WMA", "Slow weighted MA period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0m;
		_prevSlow = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var fast = new WeightedMovingAverage { Length = FastPeriod };
		var slow = new WeightedMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		var crossUp = _prevFast <= _prevSlow && fast > slow;
		var crossDown = _prevFast >= _prevSlow && fast < slow;

		if (crossUp && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (crossDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}