Diese Strategie reproduziert das Verhalten des ursprünglichen NewsTrader.mq4-Skripts, indem beide Seiten des Marktes kurz vor einer geplanten makroökonomischen Veröffentlichung bewaffnet werden. Zehn Minuten vor dem konfigurierten Nachrichtenzeitstempel sendet der Bot ein Paar Breakout-Stop-Orders und fügt sofort Schutzausgänge hinzu, wenn eine Seite ausgelöst wird.
Kernlogik
Verwendet ein 1-Minuten-Kerzenabonnement (konfigurierbar) ausschließlich als Timing-Quelle.
Berechnet den Aktivierungszeitpunkt als news time - LeadMinutes und wartet bis zur ersten fertigen Kerze, deren Öffnungszeit an diesem Punkt oder darüber hinaus liegt.
Platziert einen Verkaufsstopp unter dem aktuellen Preis und einen Kaufstopp darüber, versetzt um BiasPips, umgewandelt durch Security.PriceStep (spiegelt die bias * Point-Logik in MQL4 wider).
Sobald eine ausstehende Order ausgeführt wurde, wird die entgegengesetzte ausstehende Order storniert; Spezielle Stop-Loss- und Take-Profit-Orders werden anhand der konfigurierten Pip-Abstände platziert.
Stop-Loss- oder Take-Profit-Ausfüllungen heben die verbleibende Schutzorder auf und verflachen die Strategie.
Ruft StartProtection() beim Start auf, damit die Strategie mit höherstufigen StockSharp-Sicherheitsmaßnahmen zusammenarbeitet.
Parameter
Name
Beschreibung
Standard
TradeVolume
Mit jeder ausstehenden Bestellung eingereichte Verträge.
1
StopLossPips
Stop-Loss-Distanz in Pips (0 deaktiviert die Stop-Order).
10
TakeProfitPips
Take-Profit-Distanz in Pips (0 deaktiviert die Zielorder).
10
BiasPips
Abstand vom Referenzpreis zu den Breakout-Stop-Orders.
20
LeadMinutes
Minuten vor dem Nachrichtenzeitstempel, wenn die Breakout-Befehle aktiviert werden.
Bestandteile der geplanten Nachrichtenzeit (Bahnsteiguhr).
2010, 3, 8, 1, 30
CandleType
Kerzendatentyp, der zur Verfolgung des Zeitverlaufs verwendet wird.
1 Minute
Implementierungshinweise
Die Strategie setzt Volume während OnStarted auf TradeVolume und stellt so sicher, dass Hilfsmethoden wie BuyStop und SellStop die erwartete Größe verwenden.
Security.PriceStep muss definiert sein; Andernfalls löst die Logik eine Ausnahme aus, da Pip-basierte Entfernungen nicht in Preise umgewandelt werden können.
Candle-Close-Preise werden bei der Berechnung der Stop-Levels als Proxy für den letzten Geld-/Briefkurs verwendet – entsprechend der ursprünglichen MQL4-Logik, die sich auf den aktuellsten Kurs zum Auslösezeitpunkt stützte.
Ausstehende Bestellungen werden nur einmal aufgegeben; Der Algorithmus schaltet sich nicht neu ein, nachdem das konfigurierte Nachrichtenereignis vorüber ist.
Schutzbefehle werden übersprungen, wenn ihr jeweiliger Pip-Abstand Null ist, wodurch das Verhalten für manuelle Eingriffe konfigurierbar bleibt.
Dateien
CS/NewsTraderStrategy.cs – C#-Implementierung der Strategie.
Die Python-Version wurde wie gewünscht absichtlich weggelassen.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// News Trader: RSI breakout with EMA filter and ATR stops.
/// </summary>
public class NewsTraderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevRsi;
private decimal _entryPrice;
public NewsTraderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 30)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0; _entryPrice = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevRsi == 0 || atrVal <= 0) { _prevRsi = rsiVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 70) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 30) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevRsi = rsiVal;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
class news_trader_strategy(Strategy):
def __init__(self):
super(news_trader_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._ema_length = self.Param("EmaLength", 30) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_rsi = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(news_trader_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._entry_price = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
ev = float(ema_val)
av = float(atr_val)
if self._prev_rsi == 0 or av <= 0:
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + av * 2.5 or close <= self._entry_price - av * 1.5 or rv > 70:
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 < 30:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if rv > 55 and self._prev_rsi <= 55 and close > ev:
self._entry_price = close
self.BuyMarket()
elif rv < 45 and self._prev_rsi >= 45 and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_rsi = rv
def OnReseted(self):
super(news_trader_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._entry_price = 0.0
def CreateClone(self):
return news_trader_strategy()