Die E-News Lucky Strategie ist ein StockSharp-Port des MetaTrader Expert Advisors e-News-Lucky. Das System automatisiert den klassischen News-Breakout-Ansatz:
Zu einer konfigurierbaren PlacementTime sendet es sowohl Buy-Stop- als auch Sell-Stop-Orders um den aktuellen Preis, versetzt um DistancePips.
Wenn eine der ausstehenden Orders ausgeführt wird, wird die entgegengesetzte Order sofort storniert. Initiale Schutz-Stop-Loss- und Take-Profit-Niveaus werden gemäß den konfigurierten Pip-Offsets angehängt.
Ein Trailing Stop kann über TrailingStopPips und TrailingStepPips aktiviert werden, um Gewinne zu sichern, wenn sich der Trade in die günstige Richtung bewegt.
Zur konfigurierten CancelTime werden alle verbleibenden ausstehenden Orders entfernt und offene Positionen geschlossen, um Risiken außerhalb des Handelsfensters zu vermeiden.
Die Strategie verwendet Kerzendaten (CandleType, standardmäßig 1 Minute) nur zur Verfolgung der geplanten Zeiten und zur Aktualisierung des Trailing Stops. Sie stützt sich nicht auf Indikatorberechnungen.
Parameter
Name
Beschreibung
Volume
Ordervolumen für jeden ausstehenden Einstieg. Die Strategie sendet symmetrische Buy-Stop- und Sell-Stop-Orders mit diesem Volumen.
StopLossPips
Abstand zwischen dem Einstiegspreis und dem Schutz-Stop-Loss, ausgedrückt in Pips. Auf null setzen zum Deaktivieren des Stops.
TakeProfitPips
Abstand zwischen dem Einstiegspreis und dem Gewinnziel in Pips. Auf null setzen zum Deaktivieren des Ziels.
TrailingStopPips
Trailing-Stop-Distanz in Pips. Die Trailing-Engine wird nur aktiv, wenn dieser Wert größer als null ist.
TrailingStepPips
Minimaler Pip-Gewinn erforderlich, bevor der Trailing Stop wieder verschoben wird. Verhindert übermäßige Stop-Aktualisierungen in seitwärts tendierenden Märkten.
DistancePips
Versatz (in Pips) vom aktuellen Preis zur Platzierung der Stop-Orders.
PlacementTime
Tageszeit (Broker-/Server-Zeit), zu der die ausstehenden Orders platziert werden. Standard: 10:30.
CancelTime
Tageszeit, zu der ausstehende Orders storniert und offene Positionen geschlossen werden. Standard: 22:30.
CandleType
Kerzenserie für Terminierung und Trailing. Standard: 1-Minuten-Zeitrahmen.
Implementierungshinweise
Die Pip-Größe folgt der MetaTrader-Logik: Wenn das Symbol 3 oder 5 Stellen hat, multipliziert die Strategie den Preisschritt mit 10, um in Pip-Einheiten zu arbeiten.
Alle Preise werden auf den Instrument-Preisschritt normalisiert, bevor Orders gesendet werden.
Trailing Stops vergleichen den letzten Schlusskurs mit PositionPrice und verschieben den Schutz-Stop nur, wenn der Gewinn sowohl TrailingStopPips als auch TrailingStepPips übersteigt.
Ausstehende Orders werden jeden Handelstag neu erstellt, wenn die Platzierungszeit erreicht wird. Überprüfungen der Stornierungszeit stellen sicher, dass alle Positionen am Ende des Fensters flach sind.
Verwendungstipps
Hängen Sie die Strategie an ein liquides Instrument mit engen Spreads; die Breakout-Abstände setzen nachrichtenartiges Preisverhalten voraus.
Legen Sie PlacementTime und CancelTime gemäß dem relevanten Wirtschaftskalender fest.
Passen Sie die Pip-Abstände an die Instrumentvolatilität an. Größere Werte reduzieren die Wahrscheinlichkeit von falschen Signalen, während kleinere Werte frühere Bewegungen erfassen können, aber das Whipsaw-Risiko erhöhen.
Deaktivieren Sie Trailing, indem Sie TrailingStopPips bei null lassen, wenn feste Stops bevorzugt werden.
Überwachen Sie Slippage und Spread bei high-impact-Nachrichten, um sicherzustellen, dass ausstehende Orders wie erwartet gefüllt 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>
/// Scheduled breakout strategy that monitors price around a reference level and enters on breakout.
/// Converted from the original pending-order version to use market orders.
/// </summary>
public class ENewsLuckyStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<decimal> _trailingStepPips;
private readonly StrategyParam<decimal> _distancePips;
private readonly StrategyParam<int> _placementHour;
private readonly StrategyParam<int> _cancelHour;
private readonly StrategyParam<DataType> _candleType;
private decimal _pipSize;
private decimal? _buyLevel;
private decimal? _sellLevel;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private bool _pendingActive;
private bool _lastWasPlacementDay;
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
public decimal TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
public decimal DistancePips
{
get => _distancePips.Value;
set => _distancePips.Value = value;
}
public int PlacementHour
{
get => _placementHour.Value;
set => _placementHour.Value = value;
}
public int CancelHour
{
get => _cancelHour.Value;
set => _cancelHour.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ENewsLuckyStrategy()
{
_stopLossPips = Param(nameof(StopLossPips), 50m)
.SetDisplay("Stop Loss", "Stop loss in pips", "Trading");
_takeProfitPips = Param(nameof(TakeProfitPips), 150m)
.SetDisplay("Take Profit", "Take profit in pips", "Trading");
_trailingStopPips = Param(nameof(TrailingStopPips), 5m)
.SetDisplay("Trailing Stop", "Trailing distance in pips", "Risk");
_trailingStepPips = Param(nameof(TrailingStepPips), 5m)
.SetDisplay("Trailing Step", "Minimum trailing step in pips", "Risk");
_distancePips = Param(nameof(DistancePips), 20m)
.SetGreaterThanZero()
.SetDisplay("Entry Distance", "Distance from market in pips", "Trading");
_placementHour = Param(nameof(PlacementHour), 2)
.SetDisplay("Placement Hour", "Hour to set breakout levels", "General");
_cancelHour = Param(nameof(CancelHour), 22)
.SetDisplay("Cancel Hour", "Hour to cancel and close", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Working candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_pipSize = 0m;
_buyLevel = null;
_sellLevel = null;
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
_pendingActive = false;
_lastWasPlacementDay = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var step = Security?.PriceStep ?? 0m;
_pipSize = step > 0 ? step : 1m;
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.CloseTime.Hour;
var price = candle.ClosePrice;
// Set breakout levels at placement hour
if (hour == PlacementHour && !_lastWasPlacementDay && Position == 0)
{
var distance = DistancePips * _pipSize;
_buyLevel = price + distance;
_sellLevel = price - distance;
_pendingActive = true;
_lastWasPlacementDay = true;
}
if (hour != PlacementHour)
_lastWasPlacementDay = false;
// Cancel at cancel hour
if (hour == CancelHour && _pendingActive)
{
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
return;
}
// Check breakout triggers
if (_pendingActive && Position == 0)
{
if (_buyLevel.HasValue && candle.HighPrice >= _buyLevel.Value)
{
var buyLevel = _buyLevel.Value;
BuyMarket(Volume);
_entryPrice = buyLevel;
_stopPrice = StopLossPips > 0 ? _entryPrice - StopLossPips * _pipSize : null;
_takePrice = TakeProfitPips > 0 ? _entryPrice + TakeProfitPips * _pipSize : null;
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
}
else if (_sellLevel.HasValue && candle.LowPrice <= _sellLevel.Value)
{
var sellLevel = _sellLevel.Value;
SellMarket(Volume);
_entryPrice = sellLevel;
_stopPrice = StopLossPips > 0 ? _entryPrice + StopLossPips * _pipSize : null;
_takePrice = TakeProfitPips > 0 ? _entryPrice - TakeProfitPips * _pipSize : null;
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
}
}
// Manage open position
if (Position > 0)
{
// Trailing stop
if (TrailingStopPips > 0 && _entryPrice > 0)
{
var trailDist = TrailingStopPips * _pipSize;
var stepDist = TrailingStepPips * _pipSize;
if (price - _entryPrice > trailDist + stepDist)
{
var newStop = price - trailDist;
if (!_stopPrice.HasValue || newStop > _stopPrice.Value)
_stopPrice = newStop;
}
}
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
{
SellMarket(Position);
ResetPosition();
return;
}
if (_takePrice.HasValue && candle.HighPrice >= _takePrice.Value)
{
SellMarket(Position);
ResetPosition();
}
}
else if (Position < 0)
{
// Trailing stop
if (TrailingStopPips > 0 && _entryPrice > 0)
{
var trailDist = TrailingStopPips * _pipSize;
var stepDist = TrailingStepPips * _pipSize;
if (_entryPrice - price > trailDist + stepDist)
{
var newStop = price + trailDist;
if (!_stopPrice.HasValue || newStop < _stopPrice.Value)
_stopPrice = newStop;
}
}
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
{
BuyMarket(-Position);
ResetPosition();
return;
}
if (_takePrice.HasValue && candle.LowPrice <= _takePrice.Value)
{
BuyMarket(-Position);
ResetPosition();
}
}
}
private void ResetPosition()
{
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Messages import DataType, CandleStates
from System import TimeSpan, Math
class e_news_lucky_strategy(Strategy):
def __init__(self):
super(e_news_lucky_strategy, self).__init__()
self._stop_loss_pips = self.Param("StopLossPips", 50.0)
self._take_profit_pips = self.Param("TakeProfitPips", 150.0)
self._trailing_stop_pips = self.Param("TrailingStopPips", 5.0)
self._trailing_step_pips = self.Param("TrailingStepPips", 5.0)
self._distance_pips = self.Param("DistancePips", 20.0)
self._placement_hour = self.Param("PlacementHour", 2)
self._cancel_hour = self.Param("CancelHour", 22)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._pip_size = 0.0
self._buy_level = None
self._sell_level = None
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._pending_active = False
self._last_was_placement_day = False
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(e_news_lucky_strategy, self).OnStarted2(time)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0
self._pip_size = step if step > 0 else 1.0
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
hour = candle.CloseTime.Hour
price = float(candle.ClosePrice)
# Set breakout levels at placement hour
if hour == self._placement_hour.Value and not self._last_was_placement_day and self.Position == 0:
distance = self._distance_pips.Value * self._pip_size
self._buy_level = price + distance
self._sell_level = price - distance
self._pending_active = True
self._last_was_placement_day = True
if hour != self._placement_hour.Value:
self._last_was_placement_day = False
# Cancel at cancel hour
if hour == self._cancel_hour.Value and self._pending_active:
self._pending_active = False
self._buy_level = None
self._sell_level = None
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(abs(self.Position))
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
return
# Check breakout triggers
if self._pending_active and self.Position == 0:
if self._buy_level is not None and float(candle.HighPrice) >= self._buy_level:
buy_level = self._buy_level
self.BuyMarket(self.Volume)
self._entry_price = buy_level
self._stop_price = self._entry_price - self._stop_loss_pips.Value * self._pip_size if self._stop_loss_pips.Value > 0 else None
self._take_price = self._entry_price + self._take_profit_pips.Value * self._pip_size if self._take_profit_pips.Value > 0 else None
self._pending_active = False
self._buy_level = None
self._sell_level = None
elif self._sell_level is not None and float(candle.LowPrice) <= self._sell_level:
sell_level = self._sell_level
self.SellMarket(self.Volume)
self._entry_price = sell_level
self._stop_price = self._entry_price + self._stop_loss_pips.Value * self._pip_size if self._stop_loss_pips.Value > 0 else None
self._take_price = self._entry_price - self._take_profit_pips.Value * self._pip_size if self._take_profit_pips.Value > 0 else None
self._pending_active = False
self._buy_level = None
self._sell_level = None
# Manage open position
if self.Position > 0:
# Trailing stop for long
if self._trailing_stop_pips.Value > 0 and self._entry_price > 0:
trail_dist = self._trailing_stop_pips.Value * self._pip_size
step_dist = self._trailing_step_pips.Value * self._pip_size
if price - self._entry_price > trail_dist + step_dist:
new_stop = price - trail_dist
if self._stop_price is None or new_stop > self._stop_price:
self._stop_price = new_stop
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket(self.Position)
self._reset_position()
return
if self._take_price is not None and float(candle.HighPrice) >= self._take_price:
self.SellMarket(self.Position)
self._reset_position()
elif self.Position < 0:
# Trailing stop for short
if self._trailing_stop_pips.Value > 0 and self._entry_price > 0:
trail_dist = self._trailing_stop_pips.Value * self._pip_size
step_dist = self._trailing_step_pips.Value * self._pip_size
if self._entry_price - price > trail_dist + step_dist:
new_stop = price + trail_dist
if self._stop_price is None or new_stop < self._stop_price:
self._stop_price = new_stop
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket(abs(self.Position))
self._reset_position()
return
if self._take_price is not None and float(candle.LowPrice) <= self._take_price:
self.BuyMarket(abs(self.Position))
self._reset_position()
def _reset_position(self):
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(e_news_lucky_strategy, self).OnReseted()
self._pip_size = 0.0
self._buy_level = None
self._sell_level = None
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._pending_active = False
self._last_was_placement_day = False
def CreateClone(self):
return e_news_lucky_strategy()