Elder Impulse 戦略
エルダーのインパルスシステムに基づく戦略
テストでは年平均リターン約106%が示されています。株式市場で最もパフォーマンスが高くなります。
Elder ImpulseはEMAの方向とMACDヒストグラムの色を組み合わせています。EMAを上回る緑のバーはロングを促し、EMAを下回る赤のバーはショートを促し、ニュートラルなバーはエグジットを示します。
トレンドの方向とモメンタムを融合させることで、このアプローチはトレーダーを強い動きの正しい側に保ちます。エグジットはシンプルで、ヒストグラムの色の変化またはEMAの傾きの反転に依存します。
詳細
- エントリー条件: MACDに基づくシグナル。
- ロング/ショート: 両方。
- エグジット条件: 逆シグナルまたはストップ。
- ストップ: はい。
- デフォルト値:
EmaPeriod= 13MacdFastPeriod= 12MacdSlowPeriod= 26MacdSignalPeriod= 9StopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: MACD
- ストップ: はい
- 複雑さ: 基本
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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()