Ver no GitHub

Estratégia Simples OzFx

Visão geral

  • 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

  1. 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.
  2. Assine o oscilador estocástico (%K length = StochasticLength, suavização 3/3) e leia a linha principal no fechamento da vela.
  3. 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).
  4. Configuração curta reflete as regras na direção oposta.
  5. 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.
  6. 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;
	}
}