A Estratégia de Níveis Stoch é uma conversão direta do MetaTrader 4 consultor especialista Stoch.mq4. O script original depende dos limites diários da sessão, calcula níveis de preços personalizados da vela anterior e coloca duas ordens pendentes para a próxima sessão. Esta versão C# mantém a mesma ideia comercial e a implementa com a estratégia de alto nível de StockSharp API.
A estratégia calcula uma faixa de negociação sintética expandindo o spread máximo/mínimo da vela anterior por um multiplicador configurável (padrão 1.1). Em seguida, posiciona:
Uma ordem de limite de venda acima do fechamento anterior na metade da faixa expandida.
Uma ordem de limite de compra abaixo do fechamento anterior na metade da faixa expandida.
Sempre que uma ordem pendente é preenchida, a estratégia anexa imediatamente saídas de colchetes (stop-loss e take-profit) usando as distâncias definidas nas etapas de preço. Todas as exposições pendentes e ordens pendentes são compensadas no início de cada novo dia de negociação, refletindo o bloco de redefinição à meia-noite do script MQL.
Lógica de negociação
Assine a série de velas configurada (diariamente por padrão) e aguarde as velas totalmente finalizadas.
Quando uma nova sessão chega:
Feche qualquer posição aberta e cancele todas as ordens de proteção ou de entrada.
Calcule o intervalo expandido range * RangeMultiplier usando a vela anterior.
Faça novos pedidos com limite de venda e compra em Close + range / 2 e Close - range / 2 respectivamente.
Ao preencher o pedido, crie pedidos correspondentes de stop-loss e take-profit usando as compensações de etapas de preço solicitadas.
Se uma das ordens de proteção for acionada, cancele a ordem de proteção irmã e aguarde a próxima redefinição da sessão.
Parâmetros
Nome
Descrição
Padrão
Notas
TakeProfitPoints
Distância de lucro medida em etapas de preço.
20
Equivalente à entrada TakeProfit no script MQL. Defina como 0 para desativar a ordem de realização de lucro.
StopLossPoints
Distância de stop-loss medida em etapas de preço.
40
Equivalente à entrada StopLoss no script MQL. Defina como 0 para desativar a ordem de stop-loss.
RangeMultiplier
Multiplicador aplicado ao intervalo de velas anterior (High - Low).
1.1
Corresponde ao fator de expansão 1.1 codificado em MQL.
OrderVolume
Volume para cada ordem pendente.
1
Espelha o parâmetro Lots.
CandleType
Série de velas que define o pregão.
Daily
Personalize se a estratégia deve operar em outros prazos.
Todos os parâmetros são configurados via Param() para oferecer suporte à otimização e vinculação da IU.
Gestão de risco
As entradas longas recebem um suporte protetor de sell stop e sell limit; shorts obtêm saídas espelhadas de buy stop e buy limit.
Os pedidos são dimensionados usando OrderVolume. Quando um lado do colchete é executado, a ordem de proteção restante é cancelada para evitar saídas duplicadas.
Uma reinicialização completa ocorre em cada nova vela, garantindo que a estratégia não leve exposição além da sessão atual.
Notas de conversão
A implementação MQL usou variáveis globais MetaTrader para evitar pedidos duplicados; a versão C# rastreia a última sessão processada internamente (_lastProcessedDay).
O ciclo de fechamento noturno foi traduzido no auxiliar ResetOrders() que cancela todas as ordens pendentes e envia um comando de achatamento de mercado se uma posição permanecer.
Os níveis de stop-loss e take-profit são recriados explicitamente por meio de métodos de pedido StockSharp em vez de serem incorporados em parâmetros OrderSend.
Trailing stop, gerenciamento de dinheiro e entradas de risco presentes no script MQL não foram utilizados lá e permanecem sem suporte nesta porta.
Dicas de uso
Anexe a estratégia a um título e defina OrderVolume, distâncias de stop e tipo de vela para corresponder ao instrumento negociado.
Certifique-se de que a segurança exponha um PriceStep adequado; caso contrário, a estratégia volta para 1 e registra um aviso.
Como os pedidos são recalculados apenas uma vez por vela concluída, mantenha o prazo diário padrão para alinhar com o comportamento original.
Revise os registros para confirmar a redefinição diária, a colocação de pedidos e o fluxo de trabalho de anexação de pedidos de proteção.
Arquivos
CS/StochLevelsStrategy.cs – implementação da estratégia principal.
README.md, README_zh.md, README_ru.md – documentação multilíngue para a estratégia convertida.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Stoch Levels: Previous candle range breakout with EMA filter and ATR stops.
/// </summary>
public class StochLevelsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _entryPrice;
public StochLevelsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0; _prevLow = 0; _entryPrice = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (_prevHigh == 0 || atrVal <= 0) { _prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; return; }
if (Position > 0)
{
if (close <= _entryPrice - atrVal * 1.5m || close >= _entryPrice + atrVal * 2.5m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close >= _entryPrice + atrVal * 1.5m || close <= _entryPrice - atrVal * 2.5m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (close > _prevHigh && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (close < _prevLow && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice;
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class stoch_levels_strategy(Strategy):
def __init__(self):
super(stoch_levels_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(stoch_levels_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
close = float(candle.ClosePrice)
if self._prev_high == 0 or av <= 0:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
return
if self.Position > 0:
if close <= self._entry_price - av * 1.5 or close >= self._entry_price + av * 2.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close >= self._entry_price + av * 1.5 or close <= self._entry_price - av * 2.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close > self._prev_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._prev_low and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
def OnReseted(self):
super(stoch_levels_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
def CreateClone(self):
return stoch_levels_strategy()