Ver no GitHub

Estratégia DLMv FX Fish Grid

Visão geral

A Estratégia DLMv FX Fish Grid replica o comportamento do consultor especialista original do MetaTrader construído em torno do oscilador "FX Fish 2MA". A estratégia avalia a Transformada de Fisher do preço, suaviza-a com uma média móvel e abre posições quando o oscilador cruza sua linha de base suavizada no lado apropriado de zero. O gerenciamento de posições imita o comportamento em grade do EA de origem: as entradas adicionais são espaçadas por uma distância configurável, as ordens limite pendentes podem ser estratificadas e a automação protetora gerencia os controles de risco.

Lógica de trading

  1. Cálculo do indicador
    • Os preços mais altos e mais baixos durante as velas CalculatePeriod definem o intervalo rolante.
    • Uma Transformada de Fisher é aplicada ao preço selecionado (AppliedPrice), usando o mesmo fator de suavização 0.67 que o indicador MT5.
    • Uma média móvel simples (MaPeriod) do valor de Fisher fornece a linha de base do sinal.
  2. Geração de sinais
    • Sinal comprado: os valores atual e anterior de Fisher estão abaixo de zero enquanto o oscilador cruza acima de sua média móvel (valor anterior abaixo da média, valor atual acima).
    • Sinal vendido: os valores atual e anterior de Fisher estão acima de zero enquanto o oscilador cruza abaixo da média móvel (valor anterior acima da média, valor atual abaixo).
    • Os sinais podem ser invertidos habilitando ReverseSignals.
  3. Execução de ordens
    • Quando um sinal de compra (ou venda) aparece, a estratégia pode opcionalmente fechar a exposição oposta existente (CloseOpposite).
    • Entradas adicionais são permitidas até que a contagem total atinja MaxTrades. Cada nova entrada deve respeitar o espaçamento mínimo dado por DistancePips a partir da última operação preenchida.
    • Ordens limite opcionais (SetLimitOrders) colocam ofertas/demandas em repouso no espaçamento configurado, replicando a grade escalonada do EA original.
  4. Gestão de riscos
    • Valores fixos de stop-loss, take-profit e trailing stop são aplicados via StartProtection, todos definidos em pips.
    • TimeLiveSeconds fecha toda a exposição quando uma operação ficou aberta mais do que o tempo de vida permitido.
    • O trading pode ser desabilitado às sextas-feiras (TradeOnFriday = false). Quando desabilitado, a estratégia fecha posições e cancela ordens pendentes assim que uma vela de sexta-feira chega.

Parâmetros

Parâmetro Descrição
OrderVolume Tamanho da ordem para cada entrada (lotes).
StopLossPips Distância do stop-loss protetor a partir da entrada. Definir como 0 para desabilitar.
TakeProfitPips Distância do nível de take-profit. Definir como 0 para desabilitar.
TrailingStopPips Distância do trailing stop (0 desabilita o trailing).
TrailingStepPips Passo pelo qual o trailing stop é ajustado.
MaxTrades Número máximo de operações simultâneas por direção. 0 remove o limite.
DistancePips Distância mínima entre entradas consecutivas e para as ordens de grade opcionais.
TradeOnFriday Quando false, a estratégia para de operar às sextas-feiras e liquida a exposição.
TimeLiveSeconds Tempo máximo (segundos) que as posições podem permanecer abertas antes de serem fechadas forçosamente.
ReverseSignals Inverter condições comprado/vendido.
SetLimitOrders Habilitar ordens limite adicionais em repouso em DistancePips.
CloseOpposite Fechar a exposição oposta antes de entrar em uma nova operação.
CalculatePeriod Lookback para o intervalo da Transformada de Fisher.
MaPeriod Período da média móvel aplicada ao valor de Fisher.
AppliedPrice Fonte de preço usada na Transformada de Fisher (close, open, high, low, median, typical, weighted).
CandleType Tipo de dados/período das velas processadas pela estratégia.

Notas

  • As distâncias de stop-loss, take-profit e trailing stop são convertidas de pips para offsets de preço absolutos usando Security.PriceStep * 10, correspondendo à lógica de pip de cinco dígitos da versão MQL.
  • As ordens limite são canceladas automaticamente quando os sinais mudam, o trading é pausado ou as proteções de tempo/sexta-feira são acionadas.
  • A Transformada de Fisher evita buscas repetidas de valores, armazenando as leituras anteriores do oscilador e da linha de base para detecção precisa de cruzamentos.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// DLMv FX Fish Grid strategy. Uses Highest/Lowest range with Fisher transform crossover.
/// </summary>
public class DlmvFxFishGridStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;

	private decimal? _prevFish;
	private decimal _prevValue;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int Period
	{
		get => _period.Value;
		set => _period.Value = value;
	}

	public DlmvFxFishGridStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_period = Param(nameof(Period), 20)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Lookback period for high/low range", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFish = null;
		_prevValue = 0m;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFish = null;
		_prevValue = 0m;

		var highest = new Highest { Length = Period };
		var lowest = new Lowest { Length = Period };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, highest);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var range = high - low;
		var midPrice = (candle.HighPrice + candle.LowPrice) / 2m;

		var normalized = range != 0m ? (midPrice - low) / range : 0.5m;
		var value = 0.66m * (normalized - 0.5m) + 0.67m * _prevValue;
		value = Math.Min(Math.Max(value, -0.999m), 0.999m);

		var ratio = (double)((1m + value) / (1m - value));
		var fish = 0.5m * (decimal)Math.Log(ratio);

		_prevValue = value;

		if (_prevFish == null)
		{
			_prevFish = fish;
			return;
		}

		// Fisher crosses zero from below → buy
		if (_prevFish.Value < 0m && fish >= 0m && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Fisher crosses zero from above → sell
		else if (_prevFish.Value > 0m && fish <= 0m && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFish = fish;
	}
}