EMA Moving Away-Strategie
EMA Moving Away verfolgt, wie weit sich der Preis von einem exponentiellen gleitenden Durchschnitt entfernt. Wenn eine Reihe von Kerzen den Preis um einen festgelegten Prozentsatz vom EMA weg drückt, wettet die Strategie auf eine Rückkehr zum Mittelwert.
Das Setup konzentriert sich auf die Long-Seite: Nach einer ausgedehnten bärischen
Bewegung, die den Preis um MovingAwayPercent unter den EMA treibt, wird eine
Position eröffnet. Körpergröße- und Serienfilter können hinzugefügt werden, um
sicherzustellen, dass die Bewegung überdehnt ist und nicht nur Rauschen. Ein
prozentualer Stop-Loss schützt das Kapital, wenn die Umkehr ausbleibt.
Details
- Daten: Kurskerzen.
- Einstiegskriterien:
- Long: Schlusskurs unter EMA um
MovingAwayPercentmit erforderlichen Serien-/Größenfiltern. - Short: nicht verwendet.
- Long: Schlusskurs unter EMA um
- Ausstiegskriterien: Rückkehr zum EMA oder Stop-Loss ausgelöst.
- Stops: Prozentualer Stop basierend auf
StopLossPercent. - Standardwerte:
EmaLength= 55MovingAwayPercent= 2.0StopLossPercent= 2.0
- Filter:
- Kategorie: Mean Reversion
- Richtung: Nur Long
- Indikatoren: EMA
- Komplexität: Moderat
- Risikolevel: Mittel
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>
/// EMA Moving Away Strategy.
/// Buys when price moves too far below EMA (mean reversion).
/// Sells when price moves too far above EMA.
/// Exits when price returns to EMA.
/// </summary>
public class EmaMovingAwayStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<decimal> _movingAwayPercent;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _ema;
private int _cooldownRemaining;
public EmaMovingAwayStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_emaLength = Param(nameof(EmaLength), 55)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Moving Average");
_movingAwayPercent = Param(nameof(MovingAwayPercent), 1.5m)
.SetDisplay("Moving away (%)", "Required percentage that price moves away from EMA", "Strategy");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public decimal MovingAwayPercent
{
get => _movingAwayPercent.Value;
set => _movingAwayPercent.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
var close = candle.ClosePrice;
// Calculate entry zones
var longEntryLevel = emaValue * (1 - MovingAwayPercent / 100);
var shortEntryLevel = emaValue * (1 + MovingAwayPercent / 100);
// Exit conditions - price returns to EMA
if (Position > 0 && close >= emaValue)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
return;
}
else if (Position < 0 && close <= emaValue)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
return;
}
// Entry: price far below EMA - buy (mean reversion)
if (close <= longEntryLevel && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Entry: price far above EMA - sell (mean reversion)
else if (close >= shortEntryLevel && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ema_moving_away_strategy(Strategy):
"""EMA Moving Away Strategy - mean reversion from EMA."""
def __init__(self):
super(ema_moving_away_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._ema_length = self.Param("EmaLength", 55) \
.SetDisplay("EMA Length", "EMA period", "Moving Average")
self._moving_away_pct = self.Param("MovingAwayPercent", 1.5) \
.SetDisplay("Moving away (%)", "Required percentage that price moves away from EMA", "Strategy")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._ema = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ema_moving_away_strategy, self).OnReseted()
self._ema = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(ema_moving_away_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
close = float(candle.ClosePrice)
ev = float(ema_val)
pct = float(self._moving_away_pct.Value)
cooldown = int(self._cooldown_bars.Value)
long_entry = ev * (1.0 - pct / 100.0)
short_entry = ev * (1.0 + pct / 100.0)
if self.Position > 0 and close >= ev:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
return
elif self.Position < 0 and close <= ev:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
return
if close <= long_entry and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif close >= short_entry and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
def CreateClone(self):
return ema_moving_away_strategy()