Die neue FSCEA-Strategie ist ein auf MACD basierendes Trendfolgesystem, das vom ursprünglichen MetaTrader 4 Expert Advisor new_fscea.mq4 portiert wurde. Die Strategie kombiniert eine klassische MACD-Crossover-Bestätigung mit einem EMA-Steigungsfilter, statischen Take-Profit-Zielen und einem Trailing-Stop zur Verwaltung offener Positionen. Es handelt jeweils ein einzelnes Symbol und eröffnet nur eine Position auf dem Markt.
Handelslogik
Langer Eintrag
Die Hauptlinie von MACD liegt unter Null, kreuzt jedoch über der Signallinie der aktuell geschlossenen Kerze.
Die vorherige Kerze hatte immer noch die MACD-Linie unterhalb der Signallinie (bestätigt den Crossover).
Der absolute Wert der Zeile MACD überschreitet den Schwellenwert OpenLevelPoints (skaliert nach Preisschritt).
Die verschobene EMA-Steigung ist positiv (EMA_shifted_now > EMA_shifted_previous).
Derzeit ist keine Position offen.
Kurzer Eintrag
Die Hauptlinie von MACD liegt über Null, kreuzt jedoch unterhalb der Signallinie der aktuell geschlossenen Kerze.
Die vorherige Kerze hatte immer noch die MACD-Linie über der Signallinie.
Die Hauptlinie MACD überschreitet den Schwellenwert OpenLevelPoints (skaliert nach Preisschritt).
Die verschobene EMA-Steigung ist negativ (EMA_shifted_now < EMA_shifted_previous).
Derzeit ist keine Position offen.
Langer Ausgang
Wird ausgelöst, wenn MACD die Signallinie unterschreitet, aber über Null bleibt und der MACD-Wert den Schwellenwert CloseLevelPoints überschreitet.
Oder wenn das Kerzenhoch das virtuelle Take-Profit-Niveau (entry + TakeProfitPoints * priceStep) berührt.
Oder wenn das Kerzentief das Trailing-Stop-Niveau erreicht (wird dynamisch aktualisiert, wenn sich der Preis zu seinen Gunsten bewegt).
Kurzer Ausgang
Wird ausgelöst, wenn MACD die Signallinie überschreitet, dabei jedoch unter Null bleibt und der absolute MACD-Wert den Schwellenwert CloseLevelPoints überschreitet.
Oder wenn das Kerzentief das virtuelle Take-Profit-Niveau (entry - TakeProfitPoints * priceStep) berührt.
Oder wenn das Kerzenhoch das Trailing-Stop-Niveau erreicht (wird dynamisch aktualisiert, wenn sich der Preis zu seinen Gunsten bewegt).
Risikomanagement
Der Take-Profit wird in Instrumentenpunkten ausgedrückt und durch Multiplikation mit Security.PriceStep in einen Preis umgewandelt.
Der Trailing-Stop funktioniert in Punkten und verschärft sich, sobald der variable Gewinn größer als die Trailing-Distanz ist.
Es kann immer nur eine Position offen sein, was dem Verhalten des MT4-Expertenberaters entspricht.
Der Positionsschutz wird durch den integrierten StartProtection()-Helfer aktiviert.
Indikatoren
MACD (12, 26, 9) – der wichtigste Crossover-Motor. Die Histogrammgröße liefert die Eintritts- und Austrittsschwellenwerte.
EMA (TrendPeriod) – wird auf Schlusskurse angewendet. Der Steigungsvergleich verwendet eine konfigurierbare Verschiebung (TrendShift), um den MT4-Parameter ma_shift zu emulieren.
Parameter
Parameter
Standard
Beschreibung
TakeProfitPoints
300
Abstand zum Gewinnziel in Punkten. Mithilfe des Symbols „Preisschritt“ in einen Preis umgerechnet.
TrailingStopPoints
20
Trailing-Stop-Größe in Punkten. Wird erst aktiviert, wenn sich der Handel um mehr als diesen Abstand zu seinen Gunsten bewegt.
OpenLevelPoints
3
Mindestens MACD Betrag (Punkte) erforderlich, bevor ein neuer Handel zulässig ist.
CloseLevelPoints
2
MACD Betrag (Punkte), der erforderlich ist, um einen Trade über einen MACD Crossover abzuschließen.
TrendPeriod
10
Länge des Trendfilters EMA.
TrendShift
2
Horizontale Verschiebung (in Balken), die bei der Auswertung seiner Steigung auf EMA angewendet wird. Höhere Werte verzögern die Trendbestätigung.
TradeVolume
0,1
Standard-Auftragsvolumen, das mit Marktaufträgen gesendet wird.
CandleType
1-stündiger Zeitrahmen
Kerzentyp, der für Indikatorberechnungen verwendet wird; kann an den gewünschten Zeitrahmen angepasst werden.
Implementierungshinweise
Die Strategie verarbeitet nur fertige Kerzen, um die Logik nahe an der MT4-Version zu halten.
Die EMA-Verschiebung wird durch die Pufferung der Indikatorausgaben und den Vergleich von Werten im Abstand von TrendShift Balken emuliert.
Trailing-Stop und Take-Profit werden virtuell implementiert (keine tatsächlichen Stop-/Limit-Orders), um die hohen API-Anforderungen einzuhalten.
Der Code basiert ausschließlich auf dem High-Level-Kerzenabonnement API (SubscribeCandles().BindEx(...)), um den Repository-Richtlinien zu entsprechen.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// NewFSCEA: EMA trend with RSI momentum and ATR stops.
/// </summary>
public class NewFsceaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevClose;
private decimal _entryPrice;
public NewFsceaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_entryPrice = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, rsi, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_prevClose == 0 || atrVal <= 0)
{
_prevClose = close;
return;
}
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 75)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 25)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > emaVal && _prevClose <= emaVal && rsiVal > 50)
{
_entryPrice = close;
BuyMarket();
}
else if (close < emaVal && _prevClose >= emaVal && rsiVal < 50)
{
_entryPrice = close;
SellMarket();
}
}
_prevClose = close;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, AverageTrueRange
class new_fscea_strategy(Strategy):
def __init__(self):
super(new_fscea_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_close = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(new_fscea_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._entry_price = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
rv = float(rsi_val)
av = float(atr_val)
close = float(candle.ClosePrice)
if self._prev_close == 0 or av <= 0:
self._prev_close = close
return
if self.Position > 0:
if close >= self._entry_price + av * 2.5 or close <= self._entry_price - av * 1.5 or rv > 75:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 2.5 or close >= self._entry_price + av * 1.5 or rv < 25:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = close
return
if self.Position == 0:
if close > ev and self._prev_close <= ev and rv > 50:
self._entry_price = close
self.BuyMarket()
elif close < ev and self._prev_close >= ev and rv < 50:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def OnReseted(self):
super(new_fscea_strategy, self).OnReseted()
self._prev_close = 0.0
self._entry_price = 0.0
def CreateClone(self):
return new_fscea_strategy()