Boring EA2 Alert recria a lógica de notificação do expert advisor boring-ea2 do MetaTrader 4. A estratégia escuta candles concluídos, calcula três médias móveis simples (SMA 3, SMA 20, SMA 150) e emite logs informativos sempre que ocorre um cruzamento entre as médias móveis. A implementação evita intencionalmente colocar ordens: o objetivo é fornecer alertas oportunos que traders possam combinar com execução discricionária ou outras estratégias automatizadas.
Lógica da estratégia
Acompanhamento de médias móveis
Viés de curto prazo: uma SMA de 3 períodos reage a mudanças imediatas de preço.
Tendência média: uma SMA de 20 períodos suaviza o preço no horizonte de swing de curto prazo.
Tendência longa: uma SMA de 150 períodos representa o pano de fundo dominante.
Detecção de cruzamentos
SMA3 vs SMA20: informa "crossed up" quando a SMA3 sobe acima da SMA20 e "crossed down" quando cai abaixo. Flags internas garantem que cada transição seja relatada uma vez.
SMA3 vs SMA150: espelha a mesma lógica contra a média de longo prazo para detectar surtos de momentum ou reversões contra a tendência predominante.
SMA20 vs SMA150: adiciona uma camada de confirmação de médio/longo prazo para que mudanças na estrutura de timeframe superior disparem seus próprios alertas.
Guarda de inicialização: o primeiro candle concluído apenas semeia o estado inicial. Alertas começam no segundo candle concluído quando uma mudança real de relação é observada.
Formato de notificação
Alertas espelham a mensagem original do EA: Alert!!! - SYMBOL - TF - description.
O código de timeframe é derivado do tipo de candle configurado. Rótulos padrão no estilo MetaTrader (M1, M5, H1 etc.) são usados quando disponíveis; outros timeframes usam notação compacta (por exemplo, M45 ou D2).
Mensagens são escritas com AddInfoLog, permitindo roteamento para visualizadores de log, scripts ou dashboards GUI.
Parâmetros
Short SMA Length: número de períodos da média móvel rápida (padrão 3).
Medium SMA Length: número de períodos da média móvel intermediária (padrão 20).
Long SMA Length: número de períodos da média móvel lenta (padrão 150).
Candle Type: timeframe usado para calcular as médias móveis. O padrão é candles de 1 minuto, correspondendo às checagens baseadas em ticks do EA com alta reatividade.
Notas adicionais
A estratégia não envia, modifica nem cancela ordens. Ela é puramente informativa.
Como Bind alimenta valores finalizados, cada cruzamento é avaliado em candles concluídos. Isso evita as viradas intrabar ruidosas que o EA original mitigava contando ticks.
Notificações baseadas em logging podem ser integradas a handlers personalizados assinando eventos de log da estratégia dentro de uma aplicação hospedeira.
Nenhuma tradução Python é fornecida neste momento; apenas a versão C# está incluída no pacote API.
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()