A estratégia OpenPendingorderAfterPositionGetStopLoss transporta o MetaTrader 5 consultor especialista de mesmo nome para o StockSharp API de alto nível. Ele avalia continuamente a inclinação da linha Stochastic %K no período de tempo selecionado. Quando %K cai, ele coloca uma ordem de stop de venda abaixo do mercado, e quando %K sobe, ele coloca uma ordem de stop de compra acima do mercado. Cada entrada preenchida recebe imediatamente uma ordem protetora de stop-loss e take-profit. Se um stop-loss fechar a posição, a estratégia reinstala automaticamente a ordem pendente correspondente para que a grade de negociações de breakout seja restaurada sem esperar pela próxima vela.
Regras de negociação
Assine velas finalizadas do período configurado e calcule um oscilador Stochastic clássico (KPeriod, DPeriod, Slowing).
Compare o valor atual de %K com o valor de duas barras atrás:
%K(current) < %K(two bars ago) → envie um stop de venda abaixo do melhor lance.
%K(current) > %K(two bars ago) → envie um stop de compra acima da melhor venda.
As ordens pendentes são compensadas do mercado pelo spread atual mais o buffer MinStopDistancePoints definido pelo usuário, correspondendo à lógica MQL original.
Assim que uma ordem pendente é preenchida, a estratégia envia um stop-loss de proteção (ordem stop) e um take-profit opcional (ordem limite).
Quando o stop-loss de proteção é acionado, a ordem pendente correspondente é recriada imediatamente usando os preços de mercado mais recentes.
As ordens protetoras são canceladas automaticamente quando a posição é fechada pelo take-profit ou quando a estratégia é interrompida.
Parâmetros
Nome
Descrição
OrderVolume
Volume de negociação em lotes para cada ordem pendente.
StopLossPoints
Distância de stop-loss em pontos de símbolo. Defina como 0 para desativar.
TakeProfitPoints
Distância de lucro em pontos de símbolo. Defina como 0 para desativar.
MinStopDistancePoints
Buffer de preço mínimo (em pontos) adicionado ao spread antes de colocar uma ordem pendente.
MaxPositions
Número máximo de posições simultâneas por direção (as contas de compensação usam efetivamente 0 ou 1).
KPeriod
Número de barras utilizadas para o cálculo de %K.
DPeriod
Comprimento da linha %D de suavização.
Slowing
Fator de suavização adicional aplicado a %K antes da comparação.
PendingExpiry
Vida útil opcional de ordens stop pendentes. Pedidos expirados são cancelados na próxima vela.
CandleType
Prazo usado para assinatura de velas e cálculos de indicadores.
Notas de implementação
Todo gerenciamento de pedidos depende de ajudantes de alto nível, como BuyStop, SellStop, SellLimit e BuyLimit conforme exigido por AGENTS.md.
Os valores do indicador são consumidos diretamente dentro do retorno de chamada SubscribeCandles().BindEx(...), evitando quaisquer chamadas GetValue.
A estratégia monitora eventos MyTrade para instalar e remover ordens de proteção, emulando a lógica OnTradeTransaction do Expert Advisor original.
Os comentários dentro do código são escritos em inglês e o recuo é feito com tabulações, obedecendo às diretrizes do repositório.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Trades based on the slope of the Stochastic %K line.
/// Buys when %K is rising, sells when %K is falling.
/// Uses take-profit and stop-loss protection.
/// </summary>
public class OpenPendingorderAfterPositionGetStopLossStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<int> _slowing;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<int> _signalCooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal? _lastK;
private decimal? _prevK;
private decimal _entryPrice;
private int _candlesSinceTrade;
public int KPeriod
{
get => _kPeriod.Value;
set => _kPeriod.Value = value;
}
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
public int Slowing
{
get => _slowing.Value;
set => _slowing.Value = value;
}
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public int SignalCooldownCandles
{
get => _signalCooldownCandles.Value;
set => _signalCooldownCandles.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public OpenPendingorderAfterPositionGetStopLossStrategy()
{
_kPeriod = Param(nameof(KPeriod), 22)
.SetDisplay("%K Period", "Number of bars for %K", "Indicators");
_dPeriod = Param(nameof(DPeriod), 7)
.SetDisplay("%D Period", "Smoothing period for %K", "Indicators");
_slowing = Param(nameof(Slowing), 2)
.SetDisplay("Slowing", "Additional smoothing factor", "Indicators");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop-loss as percentage of entry price", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take-profit as percentage of entry price", "Risk");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for indicator", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastK = null;
_prevK = null;
_entryPrice = 0;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_lastK = null;
_prevK = null;
_entryPrice = 0;
_candlesSinceTrade = SignalCooldownCandles;
var rsi = new RelativeStrengthIndex { Length = KPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal currentK)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
// Check stop-loss / take-profit on existing position
if (Position != 0 && _entryPrice > 0)
{
if (Position > 0)
{
var pnlPct = (close - _entryPrice) / _entryPrice * 100m;
if (pnlPct <= -StopLossPct || pnlPct >= TakeProfitPct)
{
SellMarket();
_entryPrice = 0;
_prevK = currentK;
_lastK = currentK;
return;
}
}
else if (Position < 0)
{
var pnlPct = (_entryPrice - close) / _entryPrice * 100m;
if (pnlPct <= -StopLossPct || pnlPct >= TakeProfitPct)
{
BuyMarket();
_entryPrice = 0;
_prevK = currentK;
_lastK = currentK;
return;
}
}
}
// Need at least 2 values to determine signal transition.
if (_lastK is not decimal prevK)
{
_lastK = currentK;
return;
}
_prevK = _lastK;
_lastK = currentK;
var crossedUp = prevK <= 45m && currentK > 45m;
var crossedDown = prevK >= 55m && currentK < 55m;
if (crossedUp && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_entryPrice = close;
_candlesSinceTrade = 0;
}
else if (crossedDown && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_entryPrice = close;
_candlesSinceTrade = 0;
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class open_pendingorder_after_position_get_stop_loss_strategy(Strategy):
def __init__(self):
super(open_pendingorder_after_position_get_stop_loss_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._k_period = self.Param("KPeriod", 22)
self._stop_loss_pct = self.Param("StopLossPct", 2.0)
self._take_profit_pct = self.Param("TakeProfitPct", 3.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = 4
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def KPeriod(self):
return self._k_period.Value
@KPeriod.setter
def KPeriod(self, value):
self._k_period.Value = value
@property
def StopLossPct(self):
return self._stop_loss_pct.Value
@StopLossPct.setter
def StopLossPct(self, value):
self._stop_loss_pct.Value = value
@property
def TakeProfitPct(self):
return self._take_profit_pct.Value
@TakeProfitPct.setter
def TakeProfitPct(self, value):
self._take_profit_pct.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(open_pendingorder_after_position_get_stop_loss_strategy, self).OnReseted()
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(open_pendingorder_after_position_get_stop_loss_strategy, self).OnStarted2(time)
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = self.SignalCooldownCandles
rsi = RelativeStrengthIndex()
rsi.Length = self.KPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, current_k):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
k_val = float(current_k)
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
# Check stop-loss / take-profit on existing position
if self.Position != 0 and self._entry_price > 0:
if self.Position > 0:
pnl_pct = (close - self._entry_price) / self._entry_price * 100.0
if pnl_pct <= -float(self.StopLossPct) or pnl_pct >= float(self.TakeProfitPct):
self.SellMarket()
self._entry_price = 0.0
self._last_k = k_val
return
elif self.Position < 0:
pnl_pct = (self._entry_price - close) / self._entry_price * 100.0
if pnl_pct <= -float(self.StopLossPct) or pnl_pct >= float(self.TakeProfitPct):
self.BuyMarket()
self._entry_price = 0.0
self._last_k = k_val
return
if self._last_k is None:
self._last_k = k_val
return
prev_k = self._last_k
self._last_k = k_val
crossed_up = prev_k <= 45.0 and k_val > 45.0
crossed_down = prev_k >= 55.0 and k_val < 55.0
if crossed_up and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._entry_price = close
self._candles_since_trade = 0
elif crossed_down and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._entry_price = close
self._candles_since_trade = 0
def CreateClone(self):
return open_pendingorder_after_position_get_stop_loss_strategy()