La estrategia ComFracti Fractal RSI es una versión StockSharp del experto MetaTrader ComFracti. El algoritmo busca sesgo direccional utilizando fractales de Bill Williams en dos períodos de tiempo y filtra las señales con un rápido RSI calculado en velas diarias. Una vez que aparece una configuración válida, la estrategia abre una única posición, la protege con distancias configurables de stop-loss y take-profit y, opcionalmente, puede salir cuando la señal se invierte o cuando se alcanza un límite de tiempo de tenencia.
La configuración predeterminada replica el período de negociación de 15 minutos con un período de confirmación de 1 hora y una duración diaria RSI de tres períodos utilizando el precio de apertura de la vela, al igual que el experto original.
Lógica de trading
Detección de sesgo fractal
Las velas terminadas del período de negociación y del período superior se procesan a través de una ventana fractal de cinco barras.
Los parámetros Primary*Shift y Higher*Shift definen cuántas barras retroceden la estrategia para detectar el último fractal confirmado. Los valores predeterminados coinciden con el valor original de 3, lo que significa que el código evalúa el fractal que se confirmó hace tres velas.
Un fractal bajista (baja) sin un fractal alcista que lo acompañe se trata como alcista (+1). Un fractal alcista sin un fractal bajista se trata como bajista (-1).
Filtro diario RSI
Un RelativeStrengthIndex con el RsiPeriod configurable (predeterminado 3) se ejecuta en el período de tiempo diario y utiliza el precio de apertura de la vela, coincidiendo con la implementación de MetaTrader.
Las configuraciones largas requieren que RSI esté por debajo de 50 - RsiBuyOffset; las configuraciones cortas requieren que RSI esté por encima de 50 + RsiSellOffset.
Condiciones de entrada
Comprar: Ambos rastreadores de fractales informan +1 y el filtro RSI es alcista. La estrategia abre una posición larga si es plana o corta, enviando suficiente volumen para girar hacia el lado largo.
Vender: Ambos rastreadores de fractales informan -1 y el filtro RSI es bajista. La estrategia abre una posición corta si es plana o larga, enviando suficiente volumen para volcarse hacia el lado corto.
Gestión de posiciones
Los niveles protectores de stop-loss y take-profit se calculan inmediatamente después de que cambia la posición en función de StopLossPips y TakeProfitPips multiplicados por el tamaño del pip del instrumento.
La posición se puede cerrar cuando el precio alcanza el stop o el objetivo, cuando transcurre ExpiryMinutes o cuando CloseOnOppositeSignal esté habilitado y la señal se invierta.
Parámetros
Nombre
Descripción
Predeterminado
Volume
Volumen de pedidos utilizado para cada entrada.
0.1
TakeProfitPips
Distancia objetivo de ganancias en pips. Establezca en 0 para desactivar.
700
StopLossPips
Distancia de stop-loss en pips. Establezca en 0 para desactivar.
2500
ExpiryMinutes
Tiempo máximo de espera en minutos antes de forzar una salida. 0 desactiva el temporizador.
5555
CloseOnOppositeSignal
Cierre la posición activa cuando la señal cambie en la dirección opuesta.
false
PrimaryBuyShift
Las barras retroceden para inspeccionar el fractal alcista en el período de negociación.
3
HigherBuyShift
Las barras retroceden para inspeccionar el fractal alcista en el marco temporal superior.
3
PrimarySellShift
Las barras retroceden para inspeccionar el fractal bajista en el período de negociación.
3
HigherSellShift
Las barras retroceden para inspeccionar el fractal bajista en el marco temporal superior.
3
RsiBuyOffset
Se requiere una compensación inferior a 50 para configuraciones largas.
3
RsiSellOffset
Se requiere una compensación superior a 50 para configuraciones cortas.
3
RsiPeriod
RSI duración en el período de tiempo diario.
3
CandleType
Tipo de vela de marco temporal de negociación.
velas de 15 minutos
HigherTimeFrame
Tipo de vela del plazo de confirmación.
velas de 1 hora
DailyTimeFrame
Tipo de vela utilizada para el RSI diario.
velas de 1 dia
Notas de implementación
La estrategia utiliza la suscripción de vela de alto nivel API (SubscribeCandles().Bind(...)) y gestiona los indicadores internamente sin exponerlos a través de Strategy.Indicators, como lo exigen las pautas.
Fractals se calculan a través de un asistente interno que almacena una ventana móvil de cinco velas y actualiza la señal solo después de que se confirma un fractal.
Los valores RSI se recuperan a través de RelativeStrengthIndex.Process(...) con el precio de apertura de la vela, coincidiendo con el modo MetaTrader PRICE_OPEN.
Sólo se mantiene una posición a la vez. Las órdenes de mercado invierten la posición cuando es necesario añadiendo el volumen necesario para cubrir una exposición existente.
El tamaño del pip se estima a partir de Security.PriceStep y Security.Decimals, utilizando un multiplicador de 10x para activos cotizados con tres o más decimales, reproduciendo la conversión de MetaTrader Point a pip.
Consejos de uso
Los cambios fractales deben ser lo suficientemente grandes como para garantizar que exista el índice de vela solicitado. Con un cambio de tres, el rastreador requiere al menos cinco velas terminadas por período de tiempo antes de generar señales.
Cuando opere con instrumentos con diferentes tamaños de tick (por ejemplo, índices o acciones), ajuste TakeProfitPips y StopLossPips para que coincidan con la definición de pip del instrumento.
Al deshabilitar CloseOnOppositeSignal se replica el comportamiento original del asesor experto (estaba deshabilitado de manera predeterminada) y se basa únicamente en paradas, objetivos o el temporizador de vencimiento para las salidas.
La estrategia no implementa martingala ni dimensionamiento basado en riesgos; el cálculo del lote MetaTrader se basó en funciones de margen de cuenta que no están disponibles en StockSharp. Utilice el parámetro Volume o ajuste la estrategia en un administrador de cartera si se requiere un tamaño de posición dinámico.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// ComFracti Fractal RSI: Fractal breakout with RSI filter and ATR stops.
/// </summary>
public class ComFractiFractalRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _prevHigh5;
private decimal _prevLow5;
private decimal _high1, _high2, _high3, _high4, _high5;
private decimal _low1, _low2, _low3, _low4, _low5;
private int _barCount;
public ComFractiFractalRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_barCount = 0;
_prevHigh5 = 0;
_prevLow5 = 0;
_high1 = _high2 = _high3 = _high4 = _high5 = 0;
_low1 = _low2 = _low3 = _low4 = _low5 = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_barCount = 0;
_prevHigh5 = 0;
_prevLow5 = 0;
_high1 = _high2 = _high3 = _high4 = _high5 = 0;
_low1 = _low2 = _low3 = _low4 = _low5 = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
// Shift fractal window
_high5 = _high4; _high4 = _high3; _high3 = _high2; _high2 = _high1;
_high1 = candle.HighPrice;
_low5 = _low4; _low4 = _low3; _low3 = _low2; _low2 = _low1;
_low1 = candle.LowPrice;
_barCount++;
if (_barCount < 5 || atrVal <= 0)
return;
var close = candle.ClosePrice;
// Detect fractal high (center bar _high3 is highest)
var fractalUp = _high3 > _high1 && _high3 > _high2 && _high3 > _high4 && _high3 > _high5;
// Detect fractal low (center bar _low3 is lowest)
var fractalDown = _low3 < _low1 && _low3 < _low2 && _low3 < _low4 && _low3 < _low5;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || fractalDown)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || fractalUp)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (fractalDown && rsiVal < 45)
{
_entryPrice = close;
BuyMarket();
}
else if (fractalUp && rsiVal > 55)
{
_entryPrice = close;
SellMarket();
}
}
_prevHigh5 = _high5;
_prevLow5 = _low5;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import RelativeStrengthIndex, AverageTrueRange
class com_fracti_fractal_rsi_strategy(Strategy):
def __init__(self):
super(com_fracti_fractal_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._prev_high5 = 0.0
self._prev_low5 = 0.0
self._high1 = 0.0
self._high2 = 0.0
self._high3 = 0.0
self._high4 = 0.0
self._high5 = 0.0
self._low1 = 0.0
self._low2 = 0.0
self._low3 = 0.0
self._low4 = 0.0
self._low5 = 0.0
self._bar_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(com_fracti_fractal_rsi_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._bar_count = 0
self._prev_high5 = 0.0
self._prev_low5 = 0.0
self._high1 = self._high2 = self._high3 = self._high4 = self._high5 = 0.0
self._low1 = self._low2 = self._low3 = self._low4 = self._low5 = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
av = float(atr_val)
# Shift fractal window
self._high5 = self._high4
self._high4 = self._high3
self._high3 = self._high2
self._high2 = self._high1
self._high1 = float(candle.HighPrice)
self._low5 = self._low4
self._low4 = self._low3
self._low3 = self._low2
self._low2 = self._low1
self._low1 = float(candle.LowPrice)
self._bar_count += 1
if self._bar_count < 5 or av <= 0:
return
close = float(candle.ClosePrice)
# Detect fractal high (center bar _high3 is highest)
fractal_up = (self._high3 > self._high1 and self._high3 > self._high2 and
self._high3 > self._high4 and self._high3 > self._high5)
# Detect fractal low (center bar _low3 is lowest)
fractal_down = (self._low3 < self._low1 and self._low3 < self._low2 and
self._low3 < self._low4 and self._low3 < self._low5)
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 2.0 or fractal_down:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 2.0 or fractal_up:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high5 = self._high5
self._prev_low5 = self._low5
return
if self.Position == 0:
if fractal_down and rv < 45:
self._entry_price = close
self.BuyMarket()
elif fractal_up and rv > 55:
self._entry_price = close
self.SellMarket()
self._prev_high5 = self._high5
self._prev_low5 = self._low5
def OnReseted(self):
super(com_fracti_fractal_rsi_strategy, self).OnReseted()
self._entry_price = 0.0
self._bar_count = 0
self._prev_high5 = 0.0
self._prev_low5 = 0.0
self._high1 = self._high2 = self._high3 = self._high4 = self._high5 = 0.0
self._low1 = self._low2 = self._low3 = self._low4 = self._low5 = 0.0
def CreateClone(self):
return com_fracti_fractal_rsi_strategy()