The ENewsLuckyw Strategy is a time-based breakout system converted from the MetaTrader expert advisor e-News-Lucky$. At a scheduled time it submits buy-stop and sell-stop orders around the current price, continuously recenters them while both orders are active, and performs position management that mimics the original MQL logic. Protective exits, optional trailing, and an end-of-day cleanup complete the workflow.
Trading Logic
Scheduled straddle placement. At SetOrdersTime the strategy cancels any remaining pending orders, measures the current candle close, and places symmetric stop orders at DistancePips from the market price.
Continuous order refresh. When both pending orders are active they are realigned on every finished candle, keeping the straddle centred on price like the original expert did on each new bar.
Entry preparation. Stop-loss and optional take-profit levels are pre-calculated so they can be attached immediately when a position opens. Opposite pending orders are removed as soon as a position appears.
Trailing protection. If UseTrailing is enabled, the stop order moves by TrailingStopPips whenever the position has advanced by at least TrailingStepPips. With ProfitTrailing turned on the trailing starts only after the profit exceeds the trailing distance, replicating the MQL "ProfitTrailing" switch.
Session cleanup. At DeleteOrdersTime all pending orders are cancelled and any open position is closed to avoid holding risk overnight.
Parameters
Name
Description
Volume
Order volume in lots used for both stop orders.
StopLossPips
Protective stop distance. Zero disables the stop.
TakeProfitPips
Optional take-profit distance. Zero disables the target.
DistancePips
Offset from the current price for the breakout stop orders.
UseTrailing
Enables stop trailing once the position is open.
ProfitTrailing
Requires unrealized profit to exceed the trailing distance before moving the stop.
TrailingStopPips
Distance between price and the trailing stop.
TrailingStepPips
Minimal improvement needed before the trailing stop is updated again.
SetOrdersTime
Time of day when the straddle is placed.
DeleteOrdersTime
Time of day for cancelling orders and closing positions.
CandleType
Candle subscription used for time tracking and order maintenance.
Usage Notes
Attach the strategy to the desired instrument and configure CandleType to match the bar size you want to use for maintenance (the default is 1-minute candles).
Set the schedule parameters to align with your news event or trading session.
Adjust distances and risk controls according to instrument volatility. For Forex symbols make sure the price step is configured correctly so that StopLossPips, TakeProfitPips, and DistancePips translate into the expected price offsets.
The trailing system uses stop and limit orders for exits. If your venue does not support these order types, replace them with market exits or simulated orders before going live.
The strategy performs a daily reset by date. If you run it across midnight in the exchange time zone, ensure the trading session spans within a single trading day.
Conversion Notes
The strategy mirrors the MQL expert's workflow: scheduled placement (SetOrders), hourly maintenance (ModifyOrders), removal of conflicting pending orders (DeleteOppositeOrders), trailing logic (TrailingPositions), and end-of-day cleanup.
Spread-aware price calculations from the MQL code are approximated using the last candle close because StockSharp normalises prices to the instrument's PriceStep.
All sound, account-number, and colour settings from the original script were omitted because they have no equivalent in StockSharp's high-level API.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// ENews Lucky strategy - momentum breakout with ATR filter.
/// Buys when close breaks above recent high with strong momentum.
/// Sells when close breaks below recent low with strong momentum.
/// </summary>
public class ENewsLuckywStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMid;
private bool _hasPrev;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ENewsLuckywStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 15)
.SetDisplay("Channel Period", "Highest/Lowest period", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 10)
.SetDisplay("Momentum Period", "Momentum period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevClose = 0m; _prevMid = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var momentum = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, momentum, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest, decimal momentum)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev)
{
_prevClose = close;
_prevMid = mid;
_hasPrev = true;
return;
}
// Breakout above midpoint with positive momentum
if (_prevClose <= _prevMid && close > mid && momentum > 0 && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Breakout below midpoint with negative momentum
else if (_prevClose >= _prevMid && close < mid && momentum < 0 && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevClose = close;
_prevMid = mid;
}
}
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.Indicators import Highest, Lowest, Momentum
from StockSharp.Algo.Strategies import Strategy
class e_news_luckyw_strategy(Strategy):
def __init__(self):
super(e_news_luckyw_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 15) \
.SetDisplay("Channel Period", "Highest/Lowest period", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 10) \
.SetDisplay("Momentum Period", "Momentum period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
@property
def channel_period(self):
return self._channel_period.Value
@property
def momentum_period(self):
return self._momentum_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(e_news_luckyw_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(e_news_luckyw_strategy, self).OnStarted2(time)
self._has_prev = False
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
momentum = Momentum()
momentum.Length = self.momentum_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, momentum, self.process_candle).Start()
def process_candle(self, candle, highest, lowest, momentum):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
high_val = float(highest)
low_val = float(lowest)
mom_val = float(momentum)
mid = (high_val + low_val) / 2.0
if not self._has_prev:
self._prev_close = close
self._prev_mid = mid
self._has_prev = True
return
if self._prev_close <= self._prev_mid and close > mid and mom_val > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_close >= self._prev_mid and close < mid and mom_val < 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return e_news_luckyw_strategy()