Boring EA2 Alert переносит логику уведомлений эксперта MetaTrader 4 boring-ea2. Стратегия обрабатывает закрытые свечи, рассчитывает три простые скользящие средние (SMA 3, SMA 20, SMA 150) и пишет детальные сообщения каждый раз, когда между ними происходит пересечение. Реализация намеренно не торгует — цель состоит в оперативных подсказках, которые можно сочетать с ручным исполнением или другими автоматизированными системами.
Логика стратегии
Отслеживание скользящих средних
Краткосрочный импульс – SMA с периодом 3 мгновенно реагирует на локальное движение цены.
Среднесрочный фильтр – SMA с периодом 20 сглаживает колебания внутри текущего свинга.
Долгосрочный фон – SMA с периодом 150 описывает господствующую тенденцию.
Фиксация пересечений
SMA3 против SMA20 – сообщает о "crossed up", когда SMA3 поднимается выше SMA20, и о "crossed down", когда опускается ниже. Внутренние флаги исключают повторные сообщения при неизменном соотношении.
SMA3 против SMA150 – повторяет те же правила относительно долгосрочной средней, что помогает распознавать импульсы и возможные развороты глобального тренда.
SMA20 против SMA150 – дополнительный слой подтверждения, который выделяет перестройку средне- и долгосрочной структуры.
Инициализационный барьер – первая закрытая свеча только инициализирует исходное состояние, а уведомления появляются со второй закрытой свечи, когда выявляется реальное изменение отношений.
Формат уведомлений
Формат сообщений совпадает с оригиналом: Alert!!! - SYMBOL - TF - description.
Таймфрейм вычисляется из выбранного типа свечей. Если интервал совпадает с популярными значениями, используется метатрейдеровское обозначение (M1, M5, H1 и т.п.); иначе применяется компактное представление, например M45 или D2.
Логи пишутся через AddInfoLog, поэтому их легко перенаправить в просмотрщик, файл или пользовательский интерфейс.
Параметры
Short SMA Length – период быстрой скользящей средней (по умолчанию 3).
Medium SMA Length – период средней скользящей (по умолчанию 20).
Long SMA Length – период медленной скользящей (по умолчанию 150).
Candle Type – таймфрейм, на основе которого рассчитываются средние. Значение по умолчанию — минутные свечи, что обеспечивает высокую чувствительность, сопоставимую с тиковой проверкой оригинального советника.
Дополнительные замечания
Стратегия не выставляет, не изменяет и не отменяет заявки — она служит исключительно для сигнализации.
Благодаря Bind расчёты выполняются по закрытым свечам, что избавляет от шумовых ложных пересечений, которые исходный эксперт сглаживал подсчётом тиков.
При необходимости сообщения можно обрабатывать в прикладном коде, подписавшись на события журнала стратегии.
В составе пакета присутствует только C# версия; Python-реализация отсутствует.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Boring EA2 strategy: Triple SMA crossover.
/// Buys when fast crosses above medium while medium above slow.
/// Sells when fast crosses below medium while medium below slow.
/// </summary>
public class BoringEa2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public BoringEa2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new SimpleMovingAverage { Length = 10 };
var med = new SimpleMovingAverage { Length = 20 };
var slow = new SimpleMovingAverage { Length = 40 };
decimal? prevFast = null;
decimal? prevMed = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, med, slow, (candle, fastVal, medVal, slowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevFast.HasValue && prevMed.HasValue)
{
var fastCrossUp = prevFast.Value <= prevMed.Value && fastVal > medVal;
var fastCrossDown = prevFast.Value >= prevMed.Value && fastVal < medVal;
if (fastCrossUp && medVal > slowVal && Position <= 0)
BuyMarket();
else if (fastCrossDown && medVal < slowVal && Position >= 0)
SellMarket();
}
prevFast = fastVal;
prevMed = medVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, med);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
}
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.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class boring_ea2_strategy(Strategy):
def __init__(self):
super(boring_ea2_strategy, self).__init__()
self._fast = None
self._med = None
self._slow = None
self._prev_fast = None
self._prev_med = None
def OnReseted(self):
super(boring_ea2_strategy, self).OnReseted()
self._fast = None
self._med = None
self._slow = None
self._prev_fast = None
self._prev_med = None
def OnStarted2(self, time):
super(boring_ea2_strategy, self).OnStarted2(time)
self._fast = SimpleMovingAverage()
self._fast.Length = 10
self._med = SimpleMovingAverage()
self._med.Length = 20
self._slow = SimpleMovingAverage()
self._slow.Length = 40
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(15)))
subscription.Bind(self._fast, self._med, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, med_value, slow_value):
if candle.State != CandleStates.Finished:
return
if not self._fast.IsFormed or not self._med.IsFormed or not self._slow.IsFormed:
return
fast_val = float(fast_value)
med_val = float(med_value)
slow_val = float(slow_value)
if self._prev_fast is not None and self._prev_med is not None:
fast_cross_up = self._prev_fast <= self._prev_med and fast_val > med_val
fast_cross_down = self._prev_fast >= self._prev_med and fast_val < med_val
if fast_cross_up and med_val > slow_val and self.Position <= 0:
self.BuyMarket()
elif fast_cross_down and med_val < slow_val and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_med = med_val
def CreateClone(self):
return boring_ea2_strategy()