A Estratégia Stochastic Momentum Filter é um port do StockSharp do expert advisor do MetaTrader Stochastic.mq4 (pasta MQL/23473). O robô original combina dois osciladores estocásticos, médias móveis ponderadas lineares (LWMA), um filtro de desvio de momentum e uma verificação de tendência MACD de período maior. Esta versão em C# recria os mesmos blocos de construção no topo da API de alto nível do StockSharp e mantém o fluxo de trabalho de confirmação multicamadas:
Filtro de tendência – uma LWMA rápida deve estar acima (ou abaixo) de uma LWMA lenta antes que trades longos (ou curtos) sejam permitidos.
Confirmação do oscilador – tanto um estocástico rápido (5/2/2) quanto um estocástico lento (21/4/10) devem concordar em zonas de sobrevendido/sobrecomprado.
Desvio de momentum – pelo menos uma das três leituras de momentum mais recentes deve desviar da linha base 100 em mais de um limite configurável, correspondendo ao uso da função MT4 iMomentum pelo expert.
MACD de período maior – a linha principal do MACD em um período maior configurável deve permanecer acima da linha de sinal para longos (e abaixo para curtos). O período padrão de 30 dias aproxima o filtro mensal original.
Lógica de risco – stop loss, take profit e trailing opcional são tratados através do StartProtection, espelhando os parâmetros protetores do EA. As reversões de posição fecham automaticamente a exposição oposta antes de estabelecer a nova posição líquida.
A estratégia assina dois fluxos de velas: o período de negociação e o período maior que alimenta o filtro MACD. Todos os cálculos são realizados com indicadores do StockSharp e processados através dos helpers de alto nível Bind.
Parâmetros
Nome
Padrão
Descrição
StochasticBuyLevel
30
Nível de sobrevendido que ambos os osciladores estocásticos devem romper para configurações longas.
StochasticSellLevel
80
Nível de sobrecomprado que ambos os osciladores estocásticos devem atingir para configurações curtas.
FastMaPeriod
6
Comprimento do filtro de tendência LWMA rápido.
SlowMaPeriod
85
Comprimento do filtro de tendência LWMA lento.
FastStochasticPeriod
5
Período %K do oscilador estocástico rápido.
FastStochasticSignal
2
Período de suavização %D do estocástico rápido.
FastStochasticSmoothing
2
Suavização extra aplicada ao estocástico rápido (corresponde ao "slowing" do MT4).
SlowStochasticPeriod
21
Período %K do oscilador estocástico lento.
SlowStochasticSignal
4
Período de suavização %D do estocástico lento.
SlowStochasticSmoothing
10
Suavização extra aplicada ao estocástico lento.
MomentumPeriod
14
Lookback do oscilador de momentum (igual ao iMomentum do MT4).
MomentumThreshold
0.3
Desvio absoluto mínimo da linha base 100 exigido dentro dos últimos três valores de momentum.
MacdFastPeriod
12
Período EMA rápido para o MACD de período maior.
MacdSlowPeriod
26
Período EMA lento para o MACD de período maior.
MacdSignalPeriod
9
Período EMA de sinal para o MACD de período maior.
TakeProfitPoints
50
Distância de take-profit (em pontos de preço). Defina como 0 para desabilitar.
StopLossPoints
20
Distância de stop-loss (em pontos de preço). Defina como 0 para desabilitar.
EnableTrailing
true
Habilita o trailing do StockSharp da proteção de stop.
TradeVolume
1
Tamanho de posição líquida alvo em cada sinal.
MaxNetPositions
1
Limita a exposição líquida empilhada (multiplica TradeVolume).
CandleType
período de 15m
Período de negociação principal.
HigherTimeframe
período de 30d
Período usado para confirmação MACD.
Lógica de negociação
Preparação de indicadores – a estratégia vincula ambas as LWMAs, ambos os osciladores estocásticos, o indicador de momentum e o MACD aos seus respectivos fluxos de velas.
Histórico de momentum – a distância absoluta do oscilador de momentum de 100 é armazenada para as últimas três barras concluídas. Isso replica os arrays MomLevelB/MomLevelS do EA.
Regras de entrada
Longo: LWMA rápida acima da LWMA lenta, valores de %K e %D de ambos os estocásticos abaixo de StochasticBuyLevel, desvio de momentum acima de MomentumThreshold, e linha principal do MACD acima da linha de sinal.
Curto: LWMA rápida abaixo da LWMA lenta, valores de %K e %D de ambos os estocásticos acima de StochasticSellLevel, desvio de momentum acima do limite, e linha principal do MACD abaixo da linha de sinal.
Gerenciamento de posições – as ordens são enviadas com BuyMarket/SellMarket. Quando um sinal de reversão aparece, a estratégia fecha automaticamente qualquer exposição líquida oposta antes de estabelecer a nova direção.
Proteção – StartProtection aplica as distâncias configuradas de take-profit e stop-loss (em pontos). Quando EnableTrailing é true, o StockSharp gerencia o trailing de stop de forma semelhante à rotina de trailing do EA.
Diferenças em relação à versão MQL
Escalonamento de volume: o EA escala tamanhos de lote usando LotExponent e permite vários tickets simultâneos. O port do StockSharp foca na exposição líquida e visa um único TradeVolume por direção (limitado por MaxNetPositions).
Gestão de margem: verificações de margem, paradas de equity e funções de notificação do script original não são reproduzidas porque dependem de APIs de conta MT4.
Níveis de congelamento: verificações de nível de congelamento específicas de corretor de baixo nível são omitidas; o roteamento de ordens do StockSharp lida com as restrições da bolsa.
Toggle de break-even: o helper "move to breakeven" do MT4 é substituído pela proteção de trailing do StockSharp.
Notas de uso
Atribuir um instrumento e conector, em seguida iniciar a estratégia. Ela assinará automaticamente tanto o período de negociação quanto o período maior exigido pelo filtro MACD.
Se sua fonte de dados não suportar um tipo de vela de 30 dias, ajuste HigherTimeframe para um intervalo suportado (por exemplo, semanal ou diário).
Definir TradeVolume para corresponder às suas unidades de portfólio.
Definir TakeProfitPoints/StopLossPoints como zero se as ordens protetoras devem ser desabilitadas.
Todos os comentários dentro do código são escritos em inglês, e o recuo usa tabulações.
Arquivos
CS/StochasticMomentumFilterStrategy.cs – implementação do StockSharp da lógica da estratégia.
README.md – documentação em inglês (este arquivo).
README_ru.md – documentação em russo.
README_zh.md – documentação em chinês.
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;
public class StochasticMomentumFilterStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public StochasticMomentumFilterStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class stochastic_momentum_filter_strategy(Strategy):
def __init__(self):
super(stochastic_momentum_filter_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(stochastic_momentum_filter_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(stochastic_momentum_filter_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return stochastic_momentum_filter_strategy()