Conversão do consultor especialista MetaTrader 4 OzFx (pasta MQL/7994) para o StockSharp API de alto nível.
Usa o oscilador acelerador/desacelerador (AC) junto com a linha %K do oscilador estocástico para detectar reversões de momento em torno da linha zero.
Replica o comportamento do especialista de empilhar cinco ordens de mercado com lucros escalonados e proteção de equilíbrio após a primeira meta ser atingida.
Lógica de negociação
Construa o Awesome Oscillator (5/34) e subtraia seus 5 períodos SMA para obter o valor do Accelerator Oscillator da vela concluída anterior e atual.
Assine o oscilador estocástico (%K length = StochasticLength, suavização 3/3) e leia a linha principal no fechamento da vela.
Configuração longa requer:
%K acima do nível médio configurado (padrão 50).
Valor AC atual positivo e superior ao anterior.
Valor AC anterior ainda abaixo de zero (momentum cruza a linha de base).
Configuração curta reflete as regras na direção oposta.
Quando um sinal aparece numa nova barra, a estratégia abre cinco ordens de mercado iguais:
As camadas 1 a 4 recebem lucros espaçados por TakeProfitPips múltiplos.
A camada 5 não tem meta de lucro e continua acompanhando o movimento.
Se a configuração oposta aparecer enquanto uma pilha estiver aberta, as ordens restantes serão fechadas no mercado, mantendo a estratégia estável antes de novas entradas.
Gerenciamento de posição
Cada camada compartilha a mesma distância de stop loss definida por StopLossPips.
Após a execução do primeiro take-profit, as ordens restantes estreitam seus stops até o preço de equilíbrio (entrada), correspondendo à lógica "modok" original.
As saídas de proteção são executadas quando os extremos da vela perfuram o stop armazenado ou os níveis alvo; ordens pendentes do lado da corretora não são usadas.
A estratégia permite apenas uma direção por vez e aguarda o fechamento de todas as ordens antes de redefinir os sinalizadores de bloqueio de entrada.
Parâmetros
Nome
Descrição
Padrão
OrderVolume
Tamanho do lote para cada uma das cinco ordens de mercado.
0.1
StopLossPips
Distância entre entrada e stop loss, expressa em pips.
100
TakeProfitPips
Incremento entre níveis consecutivos de take-profit (camadas 1-4).
50
StochasticLevel
Limite aplicado ao valor %K estocástico.
50
StochasticLength
Período de lookback do cálculo %K estocástico.
5
CandleType
Série de velas de origem usada pela estratégia (o padrão é velas de 4 horas).
4h time-frame
Notas de implementação
Os sinais são avaliados apenas em velas finalizadas para permanecerem consistentes com o especialista MT4 que trabalha em novas barras.
A conversão de pip se adapta automaticamente a símbolos forex de 3/5 dígitos, multiplicando o preço mínimo por 10 quando necessário.
As entradas e saídas escalonadas são tratadas na memória por meio de objetos em camadas para que a estratégia possa fechar adequadamente partes da posição.
Todos os comentários dentro do código C# são escritos em inglês, conforme exigido pelas diretrizes do repositório.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class OzFxSimpleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _cooldownRemaining;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public OzFxSimpleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = default;
_prevSlow = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var fast = new WeightedMovingAverage { Length = FastPeriod };
var slow = new WeightedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevFast = fast;
_prevSlow = slow;
}
}
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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class oz_fx_simple_strategy(Strategy):
def __init__(self):
super(oz_fx_simple_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20).SetDisplay("Fast WMA", "Fast WMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 80).SetDisplay("Slow WMA", "Slow WMA period", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 100).SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(oz_fx_simple_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(oz_fx_simple_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._has_prev = False
self._cooldown_remaining = 0
fast = WeightedMovingAverage()
fast.Length = self._fast_period.Value
slow = WeightedMovingAverage()
slow.Length = self._slow_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, self.OnProcess).Start()
def OnProcess(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_fast = fast
self._prev_slow = slow
return
if self._prev_fast <= self._prev_slow and fast > slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self._cooldown_candles.Value
elif self._prev_fast >= self._prev_slow and fast < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self._cooldown_candles.Value
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return oz_fx_simple_strategy()