Estrategia de marco de entrada STP diaria (StockSharp)
Descripción general
La Estrategia de marco de entrada STP diario replica el comportamiento del asesor experto original MetaTrader "Marco de entrada STP diario" utilizando el StockSharp nivel alto API. El sistema prepara órdenes stop de ruptura al comienzo de cada nuevo día de negociación. Los precios de entrada se derivan de los máximos y mínimos del día anterior, con filtros adicionales para garantizar que el mercado se posicione cerca de estos extremos antes de armar las órdenes. La lógica está diseñada para instrumentos de estilo Forex donde los "puntos base" corresponden a una décima parte de un pip para cotizaciones de cinco dígitos.
Flujo de trabajo principal
Seguimiento de rango diario: la estrategia se suscribe a velas diarias para recordar los máximos y mínimos de la sesión anterior.
Monitoreo en tiempo real: los datos de Level1 proporcionan los precios de oferta y demanda actuales y de la última operación para la gestión intradiaria.
Activación de órdenes: al comienzo de un nuevo día, si el último precio se ubica al menos a ThresholdPoints del máximo/mínimo de ayer y la apertura del día actual está en el lado correcto de ese extremo, se envía una orden de detención:
Parada de compra en High + SpreadPoints / 2 (convertida a unidades de precio).
Parada de venta en Low - SpreadPoints / 2.
Validación de riesgos: las nuevas órdenes se bloquean cada vez que la reducción del capital excede MaximumDrawdownPercent o los filtros de tiempo no permiten la negociación (fines de semana, filtro de hora o filtro de día).
Gestión de posiciones: una vez que una operación está activa, la estrategia aplica:
Distancias estáticas de stop-loss y take-profit.
Salida opcional basada en tiempo después de CloseAfterSeconds.
Trailing stop opcional que emula el parámetro original "Pendiente SL".
Higiene de final de día: los pedidos pendientes se cancelan después del NoNewOrdersHour (o el límite exclusivo del viernes) y siempre que cambie el día calendario.
Reglas de trading
Entradas largas
Permitido cuando SideFilter es 0 (ambos) o 1 (solo largo).
Máximo del día anterior menos precio actual ≥ ThresholdPoints.
El precio de apertura de hoy está por debajo del máximo de ayer.
El precio de entrada calculado debe respetar la distancia mínima con respecto al precio de venta actual.
Entradas cortas
Permitido cuando SideFilter es 0 (ambos) o -1 (solo corto).
Precio actual menos el mínimo del día anterior ≥ ThresholdPoints.
El precio de apertura de hoy está por encima del mínimo de ayer.
El precio de entrada calculado debe respetar la distancia mínima de la oferta actual.
Gestión del dinero
El tamaño del volumen dinámico utiliza un porcentaje de las ganancias acumuladas (PercentOfProfit).
El tamaño resultante está limitado por MinVolume y MaxVolume y alineado con el VolumeStep del instrumento.
El comercio se detiene automáticamente una vez que la reducción medida supera MaximumDrawdownPercent.
Lógica de protección
Los niveles de stop-loss y take-profit se expresan en puntos base y se convierten en compensaciones de precios utilizando el tamaño del pip del instrumento.
El trailing stop está activo sólo cuando TrailingSlope < 1. Acerca el umbral de protección al precio a medida que aumentan las ganancias no realizadas.
Las salidas de por vida cierran cualquier posición abierta una vez transcurrido el número de segundos configurado.
Parámetros
Nombre
Descripción
CandleType
Marco de tiempo utilizado para recuperar las velas de referencia (diariamente por defecto).
StopLossPoints
Distancia de stop-loss en puntos base.
TakeProfitPoints
Distancia de toma de ganancias en puntos base.
TrailingSlope
Parte de las ganancias retenida durante el seguimiento; ≥ 1 desactiva la función.
SideFilter
-1 solo corto, 0 en ambas direcciones, 1 solo largo.
ThresholdPoints
Brecha mínima entre el precio actual y el extremo anterior requerida para armar un stop.
SpreadPoints
Compensación adicional (la mitad se utiliza por encima o por debajo del extremo) para compensar el diferencial.
SlippagePoints
Se agregó un amortiguador de seguridad al control de la distancia mínima de parada.
NoNewOrdersHour
Hora (tiempo de plataforma) para cancelar órdenes pendientes en días regulares.
NoNewOrdersHourFriday
Hora de cancelación específica del viernes.
EarliestOrderHour
Primera hora del día en la que se pueden crear nuevos pedidos.
DayFilter
6 para todos los días o 0-5 para operar de domingo a viernes únicamente.
CloseAfterSeconds
Salida opcional basada en tiempo (0 inhabilitaciones).
PercentOfProfit
Fracción del beneficio acumulado utilizada para escalar el tamaño de la posición.
MinVolume / MaxVolume
Límites estrictos para el volumen calculado.
MaximumDrawdownPercent
Umbral de reducción que bloquea nuevas órdenes.
Notas de conversión
La conversión de pips refleja la implementación de MetaTrader: si el valor expone 3 o 5 decimales, el punto base se convierte en PriceStep * 10.
La ventana de cancelación de la orden de suspensión reproduce la limpieza nocturna del experto, incluido el corte separado del viernes.
La lógica final sigue la fórmula de pendiente original (newStop = Bid - StopLoss - Slope * (Bid - Entry) para largos).
Las notificaciones de acciones de la versión MQL se reemplazan con mensajes de registro de estrategias.
La implementación StockSharp mantiene activas las órdenes pendientes incluso cuando una posición está abierta, coincidiendo con el comportamiento de origen.
Consejos de uso
Asigne un instrumento Forex con valores PriceStep, StepPrice y VolumeStep configurados correctamente para garantizar un tamaño preciso.
Combine la estrategia con StockSharp controles de riesgo (límites de cartera, protecciones a nivel de conector) cuando se ejecute en vivo.
Optimice ThresholdPoints, TrailingSlope y PercentOfProfit usando Designer o Runner para adaptar la sensibilidad de ruptura a símbolos específicos.
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()