IFS Fractals es un puerto del script MetaTrader 5 IFS_Fractals. El experto original genera un mapa de bits del sistema de funciones iteradas (IFS) de la "palabra fractal" aplicando repetidamente 28 transformaciones afines a una nube de puntos. La versión StockSharp convierte el mismo proceso caótico en un oscilador direccional: la coordenada X de los puntos generados se escala, se suaviza con un promedio móvil exponencial (EMA) y se interpreta como un indicador de impulso que impulsa entradas largas y cortas.
Lógica estratégica
Sistema de funciones iteradas
Transformaciones afines: cada vela terminada desencadena un lote de iteraciones (configurables). Durante cada iteración, se selecciona una de las 28 transformaciones de acuerdo con los pesos de probabilidad originales (todos iguales a 35). La transformación actualiza el punto actual (x, y) utilizando los coeficientes transferidos palabra por palabra del código MQL5.
Tabla de probabilidad: la estrategia precalcula una matriz de probabilidad acumulada una vez al inicio, lo que permite una selección rápida de la siguiente transformación utilizando un único sorteo aleatorio dentro de la masa de probabilidad total.
Construcción de señal
Normalización: la coordenada X se divide por el mismo factor de escala (50 por defecto) que el script utilizó al proyectar el fractal en el mapa de bits. Esto mantiene la señal en un rango numérico estable independientemente del precio del instrumento.
EMA suavizado: la serie normalizada alimenta un EMA cuyo período es configurable. El EMA actúa como un filtro de paso bajo que extrae la deriva dominante de las iteraciones caóticas.
Lógica de entrada: cuando EMA supera el umbral de entrada positivo, la estrategia abre o invierte en una posición larga. Simétricamente, cuando el EMA cae por debajo del umbral negativo, se abre o se invierte en corto.
Lógica de salida: las posiciones largas abiertas salen una vez que EMA vuelve a caer al umbral de salida o por debajo de él, mientras que las posiciones cortas salen cuando el EMA vuelve a subir por encima del umbral de salida negativo. Esto crea una banda de histéresis que evita cambios rápidos alrededor de cero.
Gestión de riesgos
Protección de posición: se pueden habilitar distancias opcionales de stop-loss y take-profit a través de StartProtection. Un valor de 0 deshabilita el nivel respectivo, coincidiendo con el comportamiento del script fuente que operaba sin órdenes de protección.
Control de volumen: las entradas utilizan un parámetro de volumen de mercado fijo. Cualquier exposición opuesta existente se cierra antes de abrir una nueva operación para mantener una posición direccional única.
Parámetros
Volumen – volumen de mercado para nuevas entradas.
Tipo de vela: período de tiempo que impulsa las iteraciones fractales (predeterminado: velas de 5 minutos).
Iteraciones: número de iteraciones de IFS procesadas después de cada vela terminada.
Escala: divisor aplicado a la coordenada X antes de introducirla en EMA.
Umbral de entrada: valor absoluto EMA requerido para abrir una posición (positivo para largos, negativo reflejado para cortos).
Umbral de salida: valor EMA que activa las salidas cuando la señal vuelve a cero.
EMA Período – período de suavizado de la media móvil exponencial aplicada a la señal fractal normalizada.
Take Profit – distancia absoluta de obtención de beneficios; configúrelo en 0 para deshabilitarlo.
Stop Loss – distancia absoluta de stop-loss; configúrelo en 0 para deshabilitarlo.
Notas adicionales
Cada ejecución produce una secuencia comercial diferente a menos que se inyecte una semilla aleatoria determinista modificando la fuente; esto refleja la aleatoriedad del script de representación de mapa de bits original.
La estrategia no requiere ningún indicador derivado del mercado. Todos los datos se generan internamente a partir de los coeficientes IFS, por lo que las velas suscritas simplemente proporcionan tiempo para las iteraciones.
No se incluye ninguna implementación de Python en este paquete. Solo la estrategia C# está disponible en 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;
}
}