Estratégia Elder Impulse
Estratégia baseada no Sistema de Impulso de Elder
Os testes indicam um retorno anual médio de aproximadamente 106%. Funciona melhor no mercado de ações.
Elder Impulse combina a direção da EMA com a cor do histograma do MACD. Barras verdes acima da EMA impulsionam posições compradas, barras vermelhas abaixo impulsionam vendidas, e barras neutras sinalizam saídas.
Ao combinar direção de tendência e momentum, essa abordagem mantém os traders no lado certo dos movimentos fortes. As saídas são diretas, dependendo da mudança de cor do histograma ou da inversão da inclinação da EMA.
Detalhes
- Critérios de entrada: Sinais baseados em MACD.
- Comprado/Vendido: Ambos os direções.
- Critérios de saída: Sinal oposto ou stop.
- Stops: Sim.
- Valores padrão:
EmaPeriod= 13MacdFastPeriod= 12MacdSlowPeriod= 26MacdSignalPeriod= 9StopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoria: Tendência
- Direção: Ambos
- Indicadores: MACD
- Stops: Sim
- Complexidade: Básico
- Período: Intradiário (5m)
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on Elder's Impulse System.
/// Uses EMA direction and MACD histogram to determine impulse.
/// Green (bullish): EMA rising + MACD histogram rising -> buy
/// Red (bearish): EMA falling + MACD histogram falling -> sell
/// </summary>
public class ElderImpulseStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _macdFastPeriod;
private readonly StrategyParam<int> _macdSlowPeriod;
private readonly StrategyParam<int> _macdSignalPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private decimal _prevEma;
private decimal _prevHistogram;
private bool _hasPrevValues;
private Order _pendingOrder;
/// <summary>
/// EMA period.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MacdFastPeriod { get => _macdFastPeriod.Value; set => _macdFastPeriod.Value = value; }
public int MacdSlowPeriod { get => _macdSlowPeriod.Value; set => _macdSlowPeriod.Value = value; }
public int MacdSignalPeriod { get => _macdSignalPeriod.Value; set => _macdSignalPeriod.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
/// <summary>
/// Initializes a new instance of the <see cref="ElderImpulseStrategy"/>.
/// </summary>
public ElderImpulseStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 13).SetGreaterThanZero()
.SetDisplay("EMA Period", "Period for EMA calculation", "Indicators")
.SetOptimize(8, 21, 3);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_macdFastPeriod = Param(nameof(MacdFastPeriod), 12).SetGreaterThanZero()
.SetDisplay("MACD Fast Period", "Fast EMA period.", "Indicators");
_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26).SetGreaterThanZero()
.SetDisplay("MACD Slow Period", "Slow EMA period.", "Indicators");
_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9).SetGreaterThanZero()
.SetDisplay("MACD Signal Period", "Signal EMA period.", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 2m).SetNotNegative()
.SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it.", "Protection");
OrderRegistering += order => _pendingOrder = order;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType), (Security, DataType.Level1)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevEma = default;
_prevHistogram = default;
_hasPrevValues = default;
_pendingOrder = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var macdSignal = new MovingAverageConvergenceDivergenceSignal();
macdSignal.Macd.ShortMa.Length = MacdFastPeriod;
macdSignal.Macd.LongMa.Length = MacdSlowPeriod;
macdSignal.SignalMa.Length = MacdSignalPeriod;
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ema, macdSignal, ProcessCandle, false)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawIndicator(area, macdSignal);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// Native protection runs before the callback, including between finished candles.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (emaValue.IsEmpty)
return;
var emaDec = emaValue.GetValue<decimal>();
if (emaDec == 0)
return;
var macdTyped = (IMovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
return;
var histogram = macd - signal;
if (!_hasPrevValues)
{
_hasPrevValues = true;
_prevEma = emaDec;
_prevHistogram = histogram;
return;
}
// Both slopes must be strict; a flat EMA or histogram is neutral.
var impulse = emaDec > _prevEma && histogram > _prevHistogram ? 1
: emaDec < _prevEma && histogram < _prevHistogram ? -1 : 0;
if (_pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed))
{
_prevEma = emaDec;
_prevHistogram = histogram;
return;
}
// A qualified opposite entry reverses fully; otherwise loss of the held color exits only.
if (impulse == 1 && candle.ClosePrice > emaDec && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
}
else if (impulse == -1 && candle.ClosePrice < emaDec && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
}
else if (Position > 0m && impulse != 1)
SellMarket(Position);
else if (Position < 0m && impulse != -1)
BuyMarket(Math.Abs(Position));
_prevEma = emaDec;
_prevHistogram = histogram;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, OrderStates, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import ExponentialMovingAverage, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class elder_impulse_strategy(Strategy):
"""
Strategy based on Elder's Impulse System.
Uses EMA direction and MACD histogram to determine impulse.
Green (bullish): EMA rising + MACD histogram rising -> buy
Red (bearish): EMA falling + MACD histogram falling -> sell
"""
def __init__(self):
super(elder_impulse_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 13).SetGreaterThanZero() \
.SetDisplay("EMA Period", "Period for EMA calculation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._macd_fast_period = self.Param("MacdFastPeriod", 12).SetGreaterThanZero() \
.SetDisplay("MACD Fast Period", "Fast EMA period.", "Indicators")
self._macd_slow_period = self.Param("MacdSlowPeriod", 26).SetGreaterThanZero() \
.SetDisplay("MACD Slow Period", "Slow EMA period.", "Indicators")
self._macd_signal_period = self.Param("MacdSignalPeriod", 9).SetGreaterThanZero() \
.SetDisplay("MACD Signal Period", "Signal EMA period.", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative() \
.SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it.", "Protection")
self._prev_ema = Decimal.Zero
self._prev_histogram = Decimal.Zero
self._has_prev_values = False
self._pending_order = None
self.OrderRegistering += self._track_pending
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type), (self.Security, DataType.Level1)]
def _track_pending(self, order):
self._pending_order = order
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(elder_impulse_strategy, self).OnReseted()
self._prev_ema = Decimal.Zero
self._prev_histogram = Decimal.Zero
self._has_prev_values = False
self._pending_order = None
def OnStarted2(self, time):
super(elder_impulse_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
macd_signal = MovingAverageConvergenceDivergenceSignal()
macd_signal.Macd.ShortMa.Length = self._macd_fast_period.Value
macd_signal.Macd.LongMa.Length = self._macd_slow_period.Value
macd_signal.SignalMa.Length = self._macd_signal_period.Value
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ema, macd_signal, self._process_candle, False).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawIndicator(area, macd_signal)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# Native protection runs before this callback, including between signal bars.
pass
def _process_candle(self, candle, ema_value, macd_value):
if candle.State != CandleStates.Finished or not self.IsFormedAndOnlineAndAllowTrading():
return
if ema_value.IsEmpty:
return
ema_dec = ema_value.GetValue[Decimal](None)
if ema_dec == Decimal.Zero:
return
macd_line = macd_value.Macd
signal_line = macd_value.Signal
if macd_line is None or signal_line is None:
return
histogram = macd_line - signal_line
if not self._has_prev_values:
self._has_prev_values = True
self._prev_ema = ema_dec
self._prev_histogram = histogram
return
if ema_dec > self._prev_ema and histogram > self._prev_histogram:
impulse = 1
elif ema_dec < self._prev_ema and histogram < self._prev_histogram:
impulse = -1
else:
impulse = 0
if self._pending_order is not None and self._pending_order.State not in (OrderStates.Done, OrderStates.Failed):
self._prev_ema = ema_dec
self._prev_histogram = histogram
return
if impulse == 1 and candle.ClosePrice > ema_dec and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif impulse == -1 and candle.ClosePrice < ema_dec and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
elif self.Position > 0 and impulse != 1:
self.SellMarket(self.Position)
elif self.Position < 0 and impulse != -1:
self.BuyMarket(Math.Abs(self.Position))
self._prev_ema = ema_dec
self._prev_histogram = histogram
def CreateClone(self):
return elder_impulse_strategy()