The X Bug Strategy is a moving average crossover system converted from the MQL4 expert advisor with the same name. It compares two simple moving averages calculated on the median candle price. When the fast average crosses above or below the slow average, the strategy opens a position in the direction of the crossover. The implementation reproduces the original Expert Advisor features including optional signal reversal, automatic position closing on opposite signals, and pip-based protective orders.
Trading Logic
Subscribe to the configured candle type (one-minute candles by default) and calculate two simple moving averages: a fast line and a slow line. The averages use the median price and respect the configured indicator shifts.
Detect a bullish crossover when the current fast value is above the slow value while the fast value two bars earlier was below the slow value. Detect a bearish crossover using the opposite condition.
Optionally invert the crossover signal when ReverseSignals is enabled to trade the opposite direction.
When CloseOnSignal is enabled, immediately close any opposing position before entering a new one on the fresh signal.
Enter long positions on bullish signals and short positions on bearish signals. The strategy avoids stacking positions in the same direction; it only trades when the current position is flat or aligned with the signal.
Risk Management
StopLossPips – sets an absolute protective stop in pips. The stop is expressed in whole pips; fractional pricing (5-digit or 3-digit quotes) is automatically handled by converting the pip value using the security price step.
TakeProfitPips – configures the profit target distance in pips.
TrailingStopPips – when UseTrailingStop is enabled, activates a trailing stop that starts at the configured pip distance once the position moves into profit. The trailing step matches the trailing distance, replicating the original MetaTrader logic.
All protective orders are managed through StartProtection with market exits to maintain parity with the MQL4 expert.
Parameters
Parameter
Description
Default
OrderVolume
Base trade volume used for market entries.
0.1
StopLossPips
Stop-loss distance measured in pips; set to 0 to disable.
70
TakeProfitPips
Take-profit distance measured in pips; set to 0 to disable.
5000
UseTrailingStop
Enables or disables trailing stop management.
true
TrailingStopPips
Trailing distance in pips.
90
FastPeriod
Period of the fast moving average.
1
FastShift
Bars to shift the fast moving average before evaluating signals.
0
SlowPeriod
Period of the slow moving average.
14
SlowShift
Bars to shift the slow moving average before evaluating signals.
10
CloseOnSignal
Close an opposing position immediately when a new signal appears.
true
ReverseSignals
Invert signal direction to trade counter to the crossover.
false
AppliedPrice
Candle price source supplied to the moving averages.
Median
CandleType
Candle data type for signal generation.
1 minute time frame
Notes
The pip conversion multiplies the price step by 10 for symbols quoted with 5 or 3 decimal places, matching the original Expert Advisor behaviour.
No Python port is provided; only the C# strategy is included in this directory.
Trailing stops, stops, and targets are optional. Set the corresponding pip values to zero to disable them.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Moving average crossover strategy converted from the MQL4 expert "X bug".
/// Uses fast and slow SMA crossover for signal generation.
/// </summary>
public class XBugStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<bool> _reverseSignals;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _slowMa;
private decimal? _prevFast;
private decimal? _prevSlow;
private int _cooldown;
private int _candleCount;
public XBugStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast MA period", "Length of the fast moving average.", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Slow MA period", "Length of the slow moving average.", "Indicators");
_reverseSignals = Param(nameof(ReverseSignals), false)
.SetDisplay("Reverse signals", "Invert buy and sell directions.", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle type", "Primary timeframe used for signals.", "General");
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public bool ReverseSignals
{
get => _reverseSignals.Value;
set => _reverseSignals.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_slowMa = default;
_prevFast = null;
_prevSlow = null;
_cooldown = 0;
_candleCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = 0.001m;
_prevFast = null;
_prevSlow = null;
_cooldown = 0;
_candleCount = 0;
_slowMa = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_slowMa, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_slowMa.IsFormed)
return;
_candleCount++;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Use close price as the "fast" value (period=1 effectively)
var fastValue = candle.ClosePrice;
if (_prevFast is null || _prevSlow is null)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var signal = 0;
if (fastValue > slowValue && _prevFast.Value <= _prevSlow.Value)
signal = 1;
else if (fastValue < slowValue && _prevFast.Value >= _prevSlow.Value)
signal = -1;
_prevFast = fastValue;
_prevSlow = slowValue;
if (signal == 0)
return;
if (ReverseSignals)
signal = -signal;
if (signal > 0 && Position <= 0)
{
BuyMarket();
_cooldown = 100;
}
else if (signal < 0 && Position >= 0)
{
SellMarket();
_cooldown = 100;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import SimpleMovingAverage
class x_bug_strategy(Strategy):
def __init__(self):
super(x_bug_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle type", "Primary timeframe used for signals.", "General")
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast MA period", "Length of the fast moving average.", "Indicators")
self._slow_period = self.Param("SlowPeriod", 14) \
.SetDisplay("Slow MA period", "Length of the slow moving average.", "Indicators")
self._reverse_signals = self.Param("ReverseSignals", False) \
.SetDisplay("Reverse signals", "Invert buy and sell directions.", "Trading")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def ReverseSignals(self):
return self._reverse_signals.Value
def OnStarted2(self, time):
super(x_bug_strategy, self).OnStarted2(time)
from System import Decimal
self.Volume = Decimal(0.001)
self._prev_fast = None
self._prev_slow = None
self._slow_ma = SimpleMovingAverage()
self._slow_ma.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._slow_ma, self.ProcessCandle).Start()
def ProcessCandle(self, candle, slow_val):
if candle.State != CandleStates.Finished:
return
if not self._slow_ma.IsFormed:
return
from System import Decimal
sv = Decimal(float(slow_val))
fast_value = candle.ClosePrice
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fast_value
self._prev_slow = sv
return
signal = 0
if fast_value > sv and self._prev_fast <= self._prev_slow:
signal = 1
elif fast_value < sv and self._prev_fast >= self._prev_slow:
signal = -1
self._prev_fast = fast_value
self._prev_slow = sv
if signal == 0:
return
if self.ReverseSignals:
signal = -signal
if signal > 0 and self.Position <= 0:
self.BuyMarket()
elif signal < 0 and self.Position >= 0:
self.SellMarket()
def OnReseted(self):
super(x_bug_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def CreateClone(self):
return x_bug_strategy()