The Daily STP Entry Frame Strategy replicates the behaviour of the original MetaTrader expert advisor "Daily STP Entry Frame" using the StockSharp high level API. The system prepares breakout stop orders at the start of each new trading day. Entry prices are derived from the previous day's high and low, with additional filters to ensure the market is positioned near these extremes before arming orders. The logic is tailored for Forex-style instruments where "base points" correspond to one tenth of a pip for five-digit quotes.
Core Workflow
Daily range tracking – the strategy subscribes to daily candles to remember the previous session's high and low.
Real-time monitoring – Level1 data supplies the current bid, ask, and last trade prices for intraday management.
Order arming – at the beginning of a new day, if the last price sits at least ThresholdPoints away from yesterday's high/low and the current day's open is on the correct side of that extreme, a stop order is submitted:
Buy stop at High + SpreadPoints / 2 (converted to price units).
Sell stop at Low - SpreadPoints / 2.
Risk validation – new orders are blocked whenever equity drawdown exceeds MaximumDrawdownPercent or the time filters disallow trading (weekends, hour filter, or day filter).
Position management – once a trade is active the strategy enforces:
Static stop-loss and take-profit distances.
Optional time-based exit after CloseAfterSeconds.
Optional trailing stop emulating the original "SL slope" parameter.
End-of-day hygiene – pending orders are cancelled after NoNewOrdersHour (or the dedicated Friday cutoff) and whenever the calendar day changes.
Trading Rules
Long entries
Allowed when SideFilter is 0 (both) or 1 (long only).
Previous day's high minus current price ≥ ThresholdPoints.
Today's opening price is below yesterday's high.
Calculated entry price must respect the minimum distance from the current ask.
Short entries
Allowed when SideFilter is 0 (both) or -1 (short only).
Current price minus previous day's low ≥ ThresholdPoints.
Today's opening price is above yesterday's low.
Calculated entry price must respect the minimum distance from the current bid.
Money management
Dynamic volume sizing uses a percentage of accumulated profit (PercentOfProfit).
Resulting size is bounded by MinVolume and MaxVolume and aligned with the instrument's VolumeStep.
Trading pauses automatically once the measured drawdown breaches MaximumDrawdownPercent.
Protection logic
Stop-loss and take-profit levels are expressed in base points and converted to price offsets using the instrument's pip size.
Trailing stop is active only when TrailingSlope < 1. It shifts the protective threshold closer to price as unrealised profit grows.
Lifetime exits close any open position once the configured number of seconds elapses.
Parameters
Name
Description
CandleType
Time-frame used to fetch the reference candles (daily by default).
StopLossPoints
Stop-loss distance in base points.
TakeProfitPoints
Take-profit distance in base points.
TrailingSlope
Portion of profit retained during trailing; ≥ 1 disables the feature.
SideFilter
-1 short only, 0 both directions, 1 long only.
ThresholdPoints
Minimum gap between current price and the previous extreme required to arm a stop.
SpreadPoints
Additional offset (half used above/below the extreme) to compensate for spread.
SlippagePoints
Safety buffer added to the minimum stop distance check.
NoNewOrdersHour
Hour (platform time) to cancel pending orders on regular days.
NoNewOrdersHourFriday
Friday-specific cancellation hour.
EarliestOrderHour
Earliest hour of the day when new orders can be created.
DayFilter
6 for all days or 0-5 to trade Sunday through Friday only.
CloseAfterSeconds
Optional time-based exit (0 disables).
PercentOfProfit
Fraction of accumulated profit used to scale the position size.
MinVolume / MaxVolume
Hard bounds for the calculated volume.
MaximumDrawdownPercent
Drawdown threshold that blocks new orders.
Conversion Notes
Pip conversion mirrors the MetaTrader implementation: if the security exposes 3 or 5 decimal places the base point becomes PriceStep * 10.
The stop-order cancellation window reproduces the expert's evening cleanup, including the separate Friday cutoff.
Trailing logic follows the original slope formula (newStop = Bid - StopLoss - Slope * (Bid - Entry) for longs).
Equity notifications from the MQL version are replaced with strategy log messages.
The StockSharp implementation keeps pending orders active even when a position is open, matching the source behaviour.
Usage Tips
Assign a Forex instrument with properly configured PriceStep, StepPrice, and VolumeStep values to ensure accurate sizing.
Combine the strategy with StockSharp risk controls (portfolio limits, connector-level protections) when running live.
Optimise ThresholdPoints, TrailingSlope, and PercentOfProfit using Designer or Runner to adapt the breakout sensitivity to specific symbols.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Daily breakout strategy that enters on price crossing previous session extremes.
/// Converted from "Daily STP Entry Frame" MetaTrader expert.
/// Uses market orders when price breaks above previous high or below previous low.
/// </summary>
public class DailyStpEntryFrameStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private decimal _pipSize;
private decimal _stopLossOffset;
private decimal _takeProfitOffset;
private decimal? _previousDayHigh;
private decimal? _previousDayLow;
private decimal _currentDayHigh;
private decimal _currentDayLow;
private DateTime? _currentTradingDay;
private bool _tradedToday;
private decimal _entryPrice;
private decimal? _longStop;
private decimal? _longTake;
private decimal? _shortStop;
private decimal? _shortTake;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
public DailyStpEntryFrameStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Time-frame for monitoring", "General");
_stopLossPoints = Param(nameof(StopLossPoints), 80m)
.SetNotNegative()
.SetDisplay("Stop-Loss (points)", "Stop-loss distance", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 200m)
.SetNotNegative()
.SetDisplay("Take-Profit (points)", "Take-profit distance", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_pipSize = 0m;
_stopLossOffset = 0m;
_takeProfitOffset = 0m;
_previousDayHigh = null;
_previousDayLow = null;
_currentDayHigh = 0m;
_currentDayLow = 0m;
_currentTradingDay = null;
_tradedToday = false;
_entryPrice = 0m;
_longStop = null;
_longTake = null;
_shortStop = null;
_shortTake = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pipSize = Security?.PriceStep ?? 0.01m;
if (_pipSize <= 0) _pipSize = 0.01m;
_stopLossOffset = StopLossPoints * _pipSize;
_takeProfitOffset = TakeProfitPoints * _pipSize;
_previousDayHigh = null;
_previousDayLow = null;
_currentTradingDay = null;
_tradedToday = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var date = candle.OpenTime.Date;
// Track daily high/low
if (_currentTradingDay != date)
{
// Save previous day's range
if (_currentTradingDay != null)
{
_previousDayHigh = _currentDayHigh;
_previousDayLow = _currentDayLow;
}
_currentTradingDay = date;
_currentDayHigh = candle.HighPrice;
_currentDayLow = candle.LowPrice;
_tradedToday = false;
}
else
{
_currentDayHigh = Math.Max(_currentDayHigh, candle.HighPrice);
_currentDayLow = Math.Min(_currentDayLow, candle.LowPrice);
}
// Manage existing position
ManagePosition(candle);
// Check for breakout entries
if (_previousDayHigh is null || _previousDayLow is null)
return;
if (_tradedToday || Position != 0)
return;
var close = candle.ClosePrice;
// Breakout above previous day high => buy
if (close > _previousDayHigh.Value)
{
_entryPrice = close;
_longStop = _stopLossOffset > 0 ? close - _stopLossOffset : null;
_longTake = _takeProfitOffset > 0 ? close + _takeProfitOffset : null;
_shortStop = null;
_shortTake = null;
BuyMarket();
_tradedToday = true;
}
// Breakout below previous day low => sell
else if (close < _previousDayLow.Value)
{
_entryPrice = close;
_shortStop = _stopLossOffset > 0 ? close + _stopLossOffset : null;
_shortTake = _takeProfitOffset > 0 ? close - _takeProfitOffset : null;
_longStop = null;
_longTake = null;
SellMarket();
_tradedToday = true;
}
}
private void ManagePosition(ICandleMessage candle)
{
if (Position > 0)
{
if (_longStop is decimal stop && candle.LowPrice <= stop)
{
SellMarket();
_longStop = null;
_longTake = null;
return;
}
if (_longTake is decimal take && candle.HighPrice >= take)
{
SellMarket();
_longStop = null;
_longTake = null;
}
}
else if (Position < 0)
{
if (_shortStop is decimal stop && candle.HighPrice >= stop)
{
BuyMarket();
_shortStop = null;
_shortTake = null;
return;
}
if (_shortTake is decimal take && candle.LowPrice <= take)
{
BuyMarket();
_shortStop = null;
_shortTake = null;
}
}
}
}
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
class daily_stp_entry_frame_strategy(Strategy):
def __init__(self):
super(daily_stp_entry_frame_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Time-frame for monitoring", "General")
self._stop_loss_points = self.Param("StopLossPoints", 80.0) \
.SetDisplay("Stop-Loss (points)", "Stop-loss distance", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 200.0) \
.SetDisplay("Take-Profit (points)", "Take-profit distance", "Risk")
self._prev_day_high = None
self._prev_day_low = None
self._cur_day_high = 0.0
self._cur_day_low = 0.0
self._current_day = None
self._traded_today = False
self._entry_price = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def StopLossPoints(self):
return float(self._stop_loss_points.Value)
@property
def TakeProfitPoints(self):
return float(self._take_profit_points.Value)
def OnStarted2(self, time):
super(daily_stp_entry_frame_strategy, self).OnStarted2(time)
self._pip_size = 0.01
sec = self.Security
if sec is not None:
ps = sec.PriceStep
if ps is not None and float(ps) > 0:
self._pip_size = float(ps)
self._sl_offset = self.StopLossPoints * self._pip_size
self._tp_offset = self.TakeProfitPoints * self._pip_size
self._prev_day_high = None
self._prev_day_low = None
self._current_day = None
self._traded_today = False
self._entry_price = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
dt = candle.OpenTime
day = dt.Date
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._current_day is None or self._current_day != day:
if self._current_day is not None:
self._prev_day_high = self._cur_day_high
self._prev_day_low = self._cur_day_low
self._current_day = day
self._cur_day_high = high
self._cur_day_low = low
self._traded_today = False
else:
if high > self._cur_day_high:
self._cur_day_high = high
if low < self._cur_day_low:
self._cur_day_low = low
# manage position
if self.Position > 0:
if self._long_stop is not None and low <= self._long_stop:
self.SellMarket()
self._long_stop = None
self._long_take = None
return
if self._long_take is not None and high >= self._long_take:
self.SellMarket()
self._long_stop = None
self._long_take = None
return
elif self.Position < 0:
if self._short_stop is not None and high >= self._short_stop:
self.BuyMarket()
self._short_stop = None
self._short_take = None
return
if self._short_take is not None and low <= self._short_take:
self.BuyMarket()
self._short_stop = None
self._short_take = None
return
# entries
if self._prev_day_high is None or self._prev_day_low is None:
return
if self._traded_today or self.Position != 0:
return
if close > self._prev_day_high:
self._entry_price = close
self._long_stop = close - self._sl_offset if self._sl_offset > 0 else None
self._long_take = close + self._tp_offset if self._tp_offset > 0 else None
self._short_stop = None
self._short_take = None
self.BuyMarket()
self._traded_today = True
elif close < self._prev_day_low:
self._entry_price = close
self._short_stop = close + self._sl_offset if self._sl_offset > 0 else None
self._short_take = close - self._tp_offset if self._tp_offset > 0 else None
self._long_stop = None
self._long_take = None
self.SellMarket()
self._traded_today = True
def OnReseted(self):
super(daily_stp_entry_frame_strategy, self).OnReseted()
self._prev_day_high = None
self._prev_day_low = None
self._cur_day_high = 0.0
self._cur_day_low = 0.0
self._current_day = None
self._traded_today = False
self._entry_price = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
def CreateClone(self):
return daily_stp_entry_frame_strategy()