X Bug — это стратегия пересечения скользящих средних, перенесённая с советника MQL4. Она вычисляет две простые скользящие средние по медианной цене свечи и открывает позицию, когда быстрая средняя пересекает медленную. Реализация сохраняет ключевые особенности оригинала: разворот сигналов, принудительное закрытие позиции при противоположном сигнале и управление защитными заявками в пипсах.
Логика работы
Подписка на выбранный тип свечей (по умолчанию минутные) и расчёт двух простых скользящих средних — быстрой и медленной. Параметры сдвига учитываются при получении значений индикаторов.
Бычий сигнал фиксируется, когда текущее значение быстрой средней находится выше медленной, а два бара назад — ниже. Медвежий сигнал определяется зеркально.
Параметр ReverseSignals позволяет инвертировать направление сделок и торговать против направления пересечения.
При активированном CloseOnSignal стратегия немедленно закрывает противоположную позицию перед открытием новой.
Входы совершаются рыночными ордерами: покупка при бычьем пересечении и продажа при медвежьем. Стратегия не накапливает однонаправленные позиции, торгует только при отсутствии открытых сделок или при совпадении направления.
Управление рисками
StopLossPips — фиксирует защитный стоп в пипсах. При котировках с 5 или 3 знаками после запятой величина пипса автоматически корректируется по шагу цены инструмента.
TakeProfitPips — задаёт расстояние до тейк-профита в пипсах.
TrailingStopPips — при включённом UseTrailingStop активирует трейлинг с заданным расстоянием. Шаг трейлинга равен самому расстоянию, что соответствует логике советника.
Все защитные ордера запускаются через StartProtection с рыночным исполнением для точного соответствия MQL-версии.
Параметры
Параметр
Описание
Значение по умолчанию
OrderVolume
Базовый объём сделки.
0.1
StopLossPips
Стоп-лосс в пипсах (0 отключает).
70
TakeProfitPips
Тейк-профит в пипсах (0 отключает).
5000
UseTrailingStop
Включает/выключает трейлинг.
true
TrailingStopPips
Дистанция трейлинга в пипсах.
90
FastPeriod
Период быстрой скользящей средней.
1
FastShift
Сдвиг быстрой средней в барах.
0
SlowPeriod
Период медленной скользящей средней.
14
SlowShift
Сдвиг медленной средней в барах.
10
CloseOnSignal
Закрывать противоположную позицию при новом сигнале.
true
ReverseSignals
Инвертировать направление сигналов.
false
AppliedPrice
Тип цены свечи для расчёта индикаторов.
Median
CandleType
Тип свечей для сигналов.
Таймфрейм 1 минута
Примечания
Значение пипса вычисляется как шаг цены, умноженный на 10 для инструментов с 5 или 3 десятичными знаками, что повторяет оригинальный советник.
Версия на Python не предоставляется; в каталоге находится только реализация на C#.
Чтобы отключить стопы или трейлинг, установите соответствующие параметры в 0.
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()