Die Expert NEWS-Strategie ist eine direkte Portierung des MQL5-Roboters "Expert_NEWS". Die Strategie platziert kontinuierlich symmetrische Stop-Orders ober- und unterhalb des aktuellen Marktpreises und verwaltet die resultierenden Positionen mit Break-Even-Schutz, Trailing Stops und geplanten Aktualisierungen ausstehender Orders. Die Implementierung basiert auf Level1-Quotes und hält das Standard-Handelsvolumen bei 0.1 Lots.
Handelslogik
Quote-Abonnement – die Strategie lauscht auf beste Bid/Ask-Updates und berechnet Order-Preise aus den neuesten Werten.
Initiale Stop-Orders – wenn keine Long-Position oder kein Buy Stop vorhanden ist, wird ein neuer Buy Stop bei ask + EntryOffsetTicks * PriceStep platziert. Wenn keine Short-Position oder kein Sell Stop vorhanden ist, wird ein Sell Stop bei bid - EntryOffsetTicks * PriceStep platziert.
Order-Aktualisierung – alle OrderRefreshSeconds storniert die Strategie einen ausstehenden Stop und erstellt ihn neu, wenn der erforderliche Preis um mehr als TrailingStepTicks Ticks abweicht.
Positionsschutz – nach einer Ausführung öffnet die Strategie schützende Stop- und Take-Profit-Orders, wenn die angeforderten Abstände die MinimumStopTicks-Bedingung erfüllen.
Break-Even-Kontrolle – wenn UseBreakEven aktiviert ist, wird der Stop auf Einstieg ± BreakEvenProfitTicks gezogen, sobald sich der Markt weit genug bewegt und der neue Stop den Mindestabstand zum aktuellen Quote respektiert.
Trailing Stop – sobald der Preis um TrailingStartTicks vorschreitet, folgt der Stop mit TrailingStopTicks als Abstand und TrailingStepTicks als minimaler Verbesserungsschritt.
Bereinigung – das Schließen der Position storniert alle verbleibenden Schutz-Orders.
Parameter
Parameter
Beschreibung
StopLossTicks
Initialer Schutz-Stop-Abstand (Ticks). Auf null setzen, um die initiale Stop-Order zu deaktivieren.
TakeProfitTicks
Initialer Take-Profit-Abstand (Ticks). Auf null setzen, um die Ziel-Order zu deaktivieren.
TrailingStopTicks
Abstand des Trailing Stops (Ticks).
TrailingStartTicks
Gewinn in Ticks, der erforderlich ist, bevor die Trailing-Logik aktiviert wird.
TrailingStepTicks
Mindestverbesserung beim Aktualisieren des Trailing Stops oder der ausstehenden Einstiegs-Orders.
UseBreakEven
Aktiviert die Break-Even-Verschiebung des Stops, sobald genügend Gewinn vorhanden ist.
BreakEvenProfitTicks
Zusätzliches Gewinnpolster beim Verschieben des Stops auf Break-Even.
EntryOffsetTicks
Abstand zwischen aktuellem Quote und jeder neuen Stop-Einstiegs-Order.
OrderRefreshSeconds
Zeitintervall zwischen automatischen Aktualisierungsversuchen für ausstehende Stop-Orders.
MinimumStopTicks
Manueller Fallback für die Stop-Level-Anforderung des Brokers. Stops, die näher als dieser Abstand sind, werden nicht übermittelt.
Positionsverwaltung
Schutz-Orders entsprechen immer dem Netto-Positionsvolumen. Teilausführungen skalieren die Stop- und Take-Profit-Orders automatisch.
Break-Even- und Trailing-Logik funktionieren auch wenn der initiale Stop deaktiviert ist; der Stop wird dynamisch erstellt, sobald die Regeln erfüllt sind.
Die Strategie speichert den letzten Stop-Preis im Speicher, sodass Trailing-Updates ein monotones Verhalten gewährleisten.
Verwendungshinweise
Stellen Sie sicher, dass Security.PriceStep konfiguriert ist; jeder Tick-Abstandsparameter wird mit diesem Wert multipliziert.
Das Standard-Volumen beträgt 0.1, um den ursprünglichen Roboter zu spiegeln. Passen Sie die Volume-Eigenschaft an, wenn eine andere Größe erforderlich ist.
MinimumStopTicks sollte auf die Stop-Level-Anforderung des Handelsplatzes gesetzt werden, wenn dieser eine vorschreibt. Lassen Sie es auf null, um die engstmöglichen Stops zu erlauben.
Der Algorithmus ist nicht auf historische Bars angewiesen und kann nur mit Streaming-Quotes betrieben werden.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy converted from the MQL Expert NEWS robot.
/// Detects breakouts above/below a reference price range and enters positions.
/// Uses stop loss and take profit for position management.
/// </summary>
public class ExpertNewsStrategy : Strategy
{
private readonly StrategyParam<decimal> _entryOffset;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private decimal _entryPrice;
private int _lastSignal;
/// <summary>
/// Entry offset from the high/low range.
/// </summary>
public decimal EntryOffset
{
get => _entryOffset.Value;
set => _entryOffset.Value = value;
}
/// <summary>
/// Stop loss distance in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Number of bars to determine high/low range.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ExpertNewsStrategy()
{
_entryOffset = Param(nameof(EntryOffset), 200m)
.SetGreaterThanZero()
.SetDisplay("Entry Offset", "Offset from range high/low for entry", "Parameters");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take profit in price units", "Risk");
_lookbackPeriod = Param(nameof(LookbackPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Lookback Period", "Bars for range calculation", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_entryPrice = 0m;
_lastSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
StartProtection(
new Unit(TakeProfit, UnitTypes.Absolute),
new Unit(StopLoss, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_highs.Add(candle.HighPrice);
_lows.Add(candle.LowPrice);
if (_highs.Count > LookbackPeriod + 1)
_highs.RemoveAt(0);
if (_lows.Count > LookbackPeriod + 1)
_lows.RemoveAt(0);
if (_highs.Count <= LookbackPeriod)
return;
// Compute range from prior bars (excluding current)
var rangeHigh = decimal.MinValue;
var rangeLow = decimal.MaxValue;
for (int i = 0; i < _highs.Count - 1; i++)
{
if (_highs[i] > rangeHigh) rangeHigh = _highs[i];
if (_lows[i] < rangeLow) rangeLow = _lows[i];
}
var close = candle.ClosePrice;
var breakoutUp = close > rangeHigh + EntryOffset;
var breakoutDown = close < rangeLow - EntryOffset;
if (breakoutUp && _lastSignal != 1 && Position <= 0)
{
BuyMarket();
_entryPrice = close;
_lastSignal = 1;
}
else if (breakoutDown && _lastSignal != -1 && Position >= 0)
{
SellMarket();
_entryPrice = close;
_lastSignal = -1;
}
else if (!breakoutUp && !breakoutDown)
_lastSignal = 0;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class expert_news_strategy(Strategy):
def __init__(self):
super(expert_news_strategy, self).__init__()
self._entry_offset = self.Param("EntryOffset", 200.0)
self._stop_loss = self.Param("StopLoss", 1000.0)
self._take_profit = self.Param("TakeProfit", 2000.0)
self._lookback_period = self.Param("LookbackPeriod", 20)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._highs = []
self._lows = []
self._entry_price = 0.0
self._last_signal = 0
@property
def EntryOffset(self):
return self._entry_offset.Value
@EntryOffset.setter
def EntryOffset(self, value):
self._entry_offset.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def LookbackPeriod(self):
return self._lookback_period.Value
@LookbackPeriod.setter
def LookbackPeriod(self, value):
self._lookback_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(expert_news_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._entry_price = 0.0
self._last_signal = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
Unit(self.TakeProfit, UnitTypes.Absolute),
Unit(self.StopLoss, UnitTypes.Absolute))
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
h = float(candle.HighPrice)
l = float(candle.LowPrice)
close = float(candle.ClosePrice)
self._highs.append(h)
self._lows.append(l)
period = int(self.LookbackPeriod)
if len(self._highs) > period + 1:
self._highs.pop(0)
if len(self._lows) > period + 1:
self._lows.pop(0)
if len(self._highs) <= period:
return
range_high = -1e18
range_low = 1e18
for i in range(len(self._highs) - 1):
if self._highs[i] > range_high:
range_high = self._highs[i]
if self._lows[i] < range_low:
range_low = self._lows[i]
offset = float(self.EntryOffset)
breakout_up = close > range_high + offset
breakout_down = close < range_low - offset
if breakout_up and self._last_signal != 1 and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._last_signal = 1
elif breakout_down and self._last_signal != -1 and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._last_signal = -1
elif not breakout_up and not breakout_down:
self._last_signal = 0
def OnReseted(self):
super(expert_news_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._entry_price = 0.0
self._last_signal = 0
def CreateClone(self):
return expert_news_strategy()