Ver no GitHub

SE Fractals

Visão geral

IFS Fractals é uma porta do script MetaTrader 5 IFS_Fractals. O especialista original renderiza um bitmap do sistema de função iterada (IFS) da "palavra fractal" aplicando repetidamente 28 transformações afins a uma nuvem de pontos. A versão StockSharp transforma o mesmo processo caótico em um oscilador direcional: a coordenada X dos pontos gerados é dimensionada, suavizada com uma média móvel exponencial (EMA) e interpretada como um medidor de momento que impulsiona entradas longas e curtas.

Lógica estratégica

Sistema de função iterado

  • Transformações afins – cada vela finalizada aciona um lote de iterações (configuráveis). Durante cada iteração, uma das 28 transformadas é selecionada de acordo com os pesos de probabilidade originais (todos iguais a 35). A transformação atualiza o ponto atual (x, y) usando os coeficientes portados literalmente do código MQL5.
  • Tabela de probabilidades – a estratégia pré-calcula uma matriz de probabilidade cumulativa uma vez no início, permitindo a seleção rápida da próxima transformação usando um único sorteio aleatório dentro da massa total de probabilidades.

Construção de sinal

  • Normalização – a coordenada X é dividida pelo mesmo fator de escala (50 por padrão) que o script usou ao projetar o fractal no bitmap. Isto mantém o sinal em uma faixa numérica estável, independentemente do preço do instrumento.
  • EMA suavização – a série normalizada alimenta um EMA cujo período é configurável. O EMA atua como um filtro passa-baixa que extrai a deriva dominante das iterações caóticas.
  • Lógica de entrada – quando o EMA sobe acima do limite de entrada positivo, a estratégia abre ou reverte para uma posição longa. Simetricamente, quando o EMA cai abaixo do limite negativo, ele abre ou reverte para um curto.
  • Lógica de saída – as posições compradas abertas saem quando o EMA volta para ou abaixo do limite de saída, enquanto as posições vendidas saem quando o EMA sobe novamente acima do limite de saída negativo. Isso cria uma banda de histerese que evita oscilações rápidas em torno de zero.

Gestão de risco

  • Proteção de posição – distâncias absolutas opcionais de stop-loss e take-profit podem ser habilitadas por meio de StartProtection. Um valor de 0 desativa o respectivo nível, correspondendo ao comportamento do script de origem que operou sem ordens de proteção.
  • Controle de volume – as entradas utilizam um parâmetro fixo de volume de mercado. Qualquer exposição oposta existente é fechada antes de uma nova negociação ser aberta para manter uma posição direcional única.

Parâmetros

  • Volume – volume de mercado para novas entradas.
  • Tipo de vela – período de tempo que orienta as iterações fractais (padrão: velas de 5 minutos).
  • Iterações – número de iterações IFS processadas após cada vela concluída.
  • Escala – divisor aplicado à coordenada X antes de alimentá-la no EMA.
  • Limite de entrada – valor absoluto EMA necessário para abrir uma posição (positivo para posições longas, negativo espelhado para posições vendidas).
  • Limite de saída – valor EMA que aciona saídas quando o sinal reverte para zero.
  • EMA Período – período de suavização da média móvel exponencial aplicada ao sinal fractal normalizado.
  • Take Profit – distância absoluta de take-profit; defina como 0 para desativar.
  • Stop Loss – distância absoluta de stop-loss; defina como 0 para desativar.

Notas adicionais

  • Cada execução produz uma sequência de negociação diferente, a menos que uma semente aleatória determinística seja injetada pela modificação da fonte; isso reflete a aleatoriedade do script de renderização de bitmap original.
  • A estratégia não requer quaisquer indicadores derivados do mercado. Todos os dados são gerados internamente a partir dos coeficientes IFS, portanto as velas subscritas simplesmente fornecem o tempo para as iterações.
  • Nenhuma implementação Python está incluída neste pacote. Somente a estratégia C# está disponível em CS/.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// IFS Fractals strategy: Williams %R oscillator crossover.
/// Buys when WPR crosses above oversold, sells when crosses below overbought.
/// </summary>
public class IfsFractalsStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _wprPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevWpr;
	private int _candlesSinceTrade;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int WprPeriod { get => _wprPeriod.Value; set => _wprPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public IfsFractalsStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_wprPeriod = Param(nameof(WprPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("WPR Period", "Williams %R period", "Indicators");
		_oversold = Param(nameof(Oversold), -85m)
			.SetDisplay("Oversold", "WPR oversold level", "Signals");
		_overbought = Param(nameof(Overbought), -15m)
			.SetDisplay("Overbought", "WPR overbought level", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevWpr = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevWpr = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
		var wpr = new WilliamsR { Length = WprPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(wpr, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (_hasPrev)
		{
			if (_prevWpr < Oversold && wprValue >= Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (_prevWpr > Overbought && wprValue <= Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevWpr = wprValue;
		_hasPrev = true;
	}
}