Estratégia diária de quadro de entrada STP (StockSharp)
Visão geral
A Estratégia de quadro de entrada de STP diário replica o comportamento do consultor especialista MetaTrader original "Quadro de entrada de STP diário" usando o StockSharp API de alto nível. O sistema prepara ordens de breakout stop no início de cada novo dia de negociação. Os preços de entrada são derivados dos máximos e mínimos do dia anterior, com filtros adicionais para garantir que o mercado esteja posicionado próximo desses extremos antes de armar as ordens. A lógica é adaptada para instrumentos do tipo Forex, onde os “pontos base” correspondem a um décimo de pip para cotações de cinco dígitos.
Fluxo de trabalho principal
Acompanhamento de intervalo diário – a estratégia assina velas diárias para lembrar as máximas e mínimas da sessão anterior.
Monitoramento em tempo real – Os dados do Nível 1 fornecem os preços atuais de compra, venda e última negociação para gerenciamento intradiário.
Armação de ordem – no início de um novo dia, se o último preço estiver a pelo menos ThresholdPoints de distância da máxima/mínima de ontem e a abertura do dia atual estiver no lado correto desse extremo, uma ordem stop será enviada:
Stop de compra em High + SpreadPoints / 2 (convertido em unidades de preço).
Parada de venda em Low - SpreadPoints / 2.
Validação de risco – novas ordens são bloqueadas sempre que a redução do patrimônio excede MaximumDrawdownPercent ou os filtros de tempo não permitem a negociação (fins de semana, filtro de hora ou filtro de dia).
Gerenciamento de posição – uma vez que uma negociação está ativa, a estratégia impõe:
Distâncias estáticas de stop-loss e take-profit.
Saída opcional baseada em tempo após CloseAfterSeconds.
Trailing stop opcional emulando o parâmetro original "SL Slope".
Higiene de final de dia – os pedidos pendentes são cancelados após NoNewOrdersHour (ou o horário limite de sexta-feira) e sempre que o dia do calendário mudar.
Regras de negociação
Entradas longas
Permitido quando SideFilter é 0 (ambos) ou 1 (apenas longo).
Máxima do dia anterior menos preço atual ≥ ThresholdPoints.
O preço de abertura de hoje está abaixo da máxima de ontem.
O preço de entrada calculado deve respeitar a distância mínima do pedido atual.
Entradas curtas
Permitido quando SideFilter é 0 (ambos) ou -1 (apenas abreviado).
Preço atual menos a mínima do dia anterior ≥ ThresholdPoints.
O preço de abertura de hoje está acima do mínimo de ontem.
O preço de entrada calculado deverá respeitar a distância mínima da oferta atual.
Gerenciamento de dinheiro
O dimensionamento dinâmico do volume usa uma porcentagem do lucro acumulado (PercentOfProfit).
O tamanho resultante é limitado por MinVolume e MaxVolume e alinhado com o VolumeStep do instrumento.
A negociação é pausada automaticamente quando o rebaixamento medido ultrapassa MaximumDrawdownPercent.
Lógica de proteção
Os níveis de stop-loss e take-profit são expressos em pontos base e convertidos em compensações de preço usando o tamanho do pip do instrumento.
O stop móvel está ativo somente quando TrailingSlope < 1. Aproxima o limiar de protecção do preço à medida que o lucro não realizado aumenta.
As saídas vitalícias fecham qualquer posição aberta depois de decorrido o número configurado de segundos.
Parâmetros
Nome
Descrição
CandleType
Período de tempo utilizado para buscar as velas de referência (diariamente por padrão).
StopLossPoints
Distância de stop-loss em pontos base.
TakeProfitPoints
Distância de lucro em pontos base.
TrailingSlope
Parcela do lucro retida durante o trailing; ≥ 1 desativa o recurso.
SideFilter
-1 apenas curto, 0 em ambas as direções, 1 apenas longo.
ThresholdPoints
Diferença mínima entre o preço atual e o extremo anterior necessário para armar um stop.
SpreadPoints
Deslocamento adicional (metade usado acima/abaixo do extremo) para compensar o spread.
SlippagePoints
Buffer de segurança adicionado à verificação da distância mínima de parada.
NoNewOrdersHour
Hora (horário da plataforma) para cancelar pedidos pendentes em dias normais.
NoNewOrdersHourFriday
Horário de cancelamento específico para sexta-feira.
EarliestOrderHour
Primeira hora do dia em que novos pedidos podem ser criados.
DayFilter
6 para todos os dias ou 0-5 para negociar apenas de domingo a sexta-feira.
CloseAfterSeconds
Saída opcional baseada em tempo (0 desabilita).
PercentOfProfit
Fração do lucro acumulado usada para dimensionar o tamanho da posição.
MinVolume / MaxVolume
Limites rígidos para o volume calculado.
MaximumDrawdownPercent
Limite de rebaixamento que bloqueia novos pedidos.
Notas de conversão
A conversão de pip reflete a implementação MetaTrader: se o título expõe 3 ou 5 casas decimais, o ponto base se torna PriceStep * 10.
A janela de cancelamento da ordem stop reproduz a limpeza noturna do especialista, incluindo o corte separado de sexta-feira.
A lógica final segue a fórmula de inclinação original (newStop = Bid - StopLoss - Slope * (Bid - Entry) para longos).
As notificações de patrimônio da versão MQL são substituídas por mensagens de registro de estratégia.
A implementação StockSharp mantém as ordens pendentes ativas mesmo quando uma posição está aberta, correspondendo ao comportamento da origem.
Dicas de uso
Atribua um instrumento Forex com valores PriceStep, StepPrice e VolumeStep configurados corretamente para garantir um dimensionamento preciso.
Combine a estratégia com StockSharp controles de risco (limites de portfólio, proteções em nível de conector) durante a execução ao vivo.
Otimize ThresholdPoints, TrailingSlope e PercentOfProfit usando Designer ou Runner para adaptar a sensibilidade de rompimento 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()