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
Wait for a finished candle and verify that the strategy is formed, online, and allowed to trade.
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.
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.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class three_ma_bunny_cross_strategy(Strategy):
"""3MA Bunny Cross strategy using weighted moving average crossover.
Goes long on fast WMA crossing above slow WMA, short on opposite."""
def __init__(self):
super(three_ma_bunny_cross_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast WMA", "Fast weighted MA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 20) \
.SetDisplay("Slow WMA", "Slow weighted MA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(three_ma_bunny_cross_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(three_ma_bunny_cross_strategy, self).OnStarted2(time)
self._has_prev = False
fast = WeightedMovingAverage()
fast.Length = self.FastPeriod
slow = WeightedMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast)
slow_val = float(slow)
if not self._has_prev:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
return
cross_up = self._prev_fast <= self._prev_slow and fast_val > slow_val
cross_down = self._prev_fast >= self._prev_slow and fast_val < slow_val
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return three_ma_bunny_cross_strategy()