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
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>
/// EMA Moving Away strategy (long only).
/// Buys when the close is at least MovingAwayPercent below the EMA, optionally requiring a streak of bearish candles
/// (BearishStreak) and a minimum candle body (MinBodyPercent). The long closes when price returns to the EMA, and a
/// percent stop-loss protects it if the reversion fails.
/// </summary>
public class EmaMovingAwayStrategy : Strategy
{
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<decimal> _movingAwayPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<int> _bearishStreak;
private readonly StrategyParam<decimal> _minBodyPercent;
private readonly StrategyParam<DataType> _candleType;
private int _bearishCount;
/// <summary>
/// EMA period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Required distance of the close below the EMA, in percent.
/// </summary>
public decimal MovingAwayPercent
{
get => _movingAwayPercent.Value;
set => _movingAwayPercent.Value = value;
}
/// <summary>
/// Stop-loss percentage. 0 disables it.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Consecutive bearish candles required before an entry. 0 disables the filter.
/// </summary>
public int BearishStreak
{
get => _bearishStreak.Value;
set => _bearishStreak.Value = value;
}
/// <summary>
/// Minimum body of the signal candle in percent of its open. 0 disables the filter.
/// </summary>
public decimal MinBodyPercent
{
get => _minBodyPercent.Value;
set => _minBodyPercent.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public EmaMovingAwayStrategy()
{
_emaLength = Param(nameof(EmaLength), 55)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Moving Average");
_movingAwayPercent = Param(nameof(MovingAwayPercent), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Moving away (%)", "Required distance of the close below the EMA", "Strategy");
_stopLossPercent = Param(nameof(StopLossPercent), 2.0m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk");
_bearishStreak = Param(nameof(BearishStreak), 0)
.SetNotNegative()
.SetDisplay("Bearish Streak", "Consecutive bearish candles required, 0 disables", "Filters");
_minBodyPercent = Param(nameof(MinBodyPercent), 0m)
.SetNotNegative()
.SetDisplay("Min Body %", "Minimum body of the signal candle in percent, 0 disables", "Filters");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bearishCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bearishCount = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ema, ProcessCandle)
.Start();
if (StopLossPercent > 0)
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
_bearishCount = candle.ClosePrice < candle.OpenPrice ? _bearishCount + 1 : 0;
if (!emaValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ema = emaValue.ToDecimal();
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= ema)
SellMarket(Position);
return;
}
var stretched = close <= ema * (1m - MovingAwayPercent / 100m);
var streakOk = BearishStreak <= 0 || _bearishCount >= BearishStreak;
var bodyOk = MinBodyPercent <= 0 || candle.OpenPrice > 0 && Math.Abs(candle.OpenPrice - close) / candle.OpenPrice * 100m >= MinBodyPercent;
if (stretched && streakOk && bodyOk)
BuyMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ema_moving_away_strategy(Strategy):
"""
EMA Moving Away strategy (long only).
Buys when the close is at least MovingAwayPercent below the EMA, optionally requiring a streak of bearish candles
(BearishStreak) and a minimum candle body (MinBodyPercent). The long closes when price returns to the EMA, and a
percent stop-loss protects it if the reversion fails.
"""
def __init__(self):
super(ema_moving_away_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 55) \
.SetGreaterThanZero() \
.SetDisplay("EMA Length", "EMA period", "Moving Average")
self._moving_away_percent = self.Param("MovingAwayPercent", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Moving away (%)", "Required distance of the close below the EMA", "Strategy")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetNotNegative() \
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk")
self._bearish_streak = self.Param("BearishStreak", 0) \
.SetNotNegative() \
.SetDisplay("Bearish Streak", "Consecutive bearish candles required, 0 disables", "Filters")
self._min_body_percent = self.Param("MinBodyPercent", 0.0) \
.SetNotNegative() \
.SetDisplay("Min Body %", "Minimum body of the signal candle in percent, 0 disables", "Filters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
self._bearish_count = 0
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(ema_moving_away_strategy, self).OnReseted()
self._bearish_count = 0
def OnStarted2(self, time):
super(ema_moving_away_strategy, self).OnStarted2(time)
self._bearish_count = 0
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(ema, self._process_candle).Start()
stop_loss = float(self._stop_loss_percent.Value)
if stop_loss > 0:
self.StartProtection(Unit(), Unit(Decimal(stop_loss), UnitTypes.Percent), useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
self._bearish_count = self._bearish_count + 1 if candle.ClosePrice < candle.OpenPrice else 0
if not ema_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ema = float(ema_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
if self.Position > 0:
if close >= ema:
self.SellMarket(self.Position)
return
streak = int(self._bearish_streak.Value)
min_body = float(self._min_body_percent.Value)
stretched = close <= ema * (1.0 - float(self._moving_away_percent.Value) / 100.0)
streak_ok = streak <= 0 or self._bearish_count >= streak
body_ok = min_body <= 0 or (open_price > 0 and abs(open_price - close) / open_price * 100.0 >= min_body)
if stretched and streak_ok and body_ok:
self.BuyMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return ema_moving_away_strategy()