ComFracti Fractal RSI Strategy é uma versão StockSharp do especialista MetaTrader ComFracti. O algoritmo procura viés direcional usando fractais Bill Williams em dois intervalos de tempo e filtra os sinais com um RSI rápido calculado em velas diárias. Assim que uma configuração válida aparecer, a estratégia abre uma única posição, protege-a com distâncias configuráveis de stop-loss e take-profit e pode opcionalmente sair quando o sinal for revertido ou quando um limite de tempo de retenção for atingido.
A configuração padrão replica o período de negociação de 15 minutos com um período de confirmação de 1 hora e uma duração diária de RSI três períodos usando o preço de abertura da vela, assim como o especialista original.
Lógica de negociação
Detecção de polarização fractal
As velas finalizadas do período de negociação e do período superior são processadas através de uma janela fractal de cinco barras.
Os parâmetros Primary*Shift e Higher*Shift definem quantas barras a estratégia inspeciona em busca do último fractal confirmado. Os padrões correspondem ao valor original de 3, o que significa que o código avalia o fractal que foi confirmado há três velas.
Um fractal descendente (oscilação baixa) sem um fractal ascendente é tratado como altista (+1). Um fractal ascendente sem um fractal descendente é tratado como baixista (-1).
Filtro diário RSI
Um RelativeStrengthIndex com o RsiPeriod configurável (padrão 3) é executado no período diário e usa o preço de abertura da vela, correspondendo à implementação MetaTrader.
Configurações longas exigem que RSI esteja abaixo de 50 - RsiBuyOffset; configurações curtas exigem que RSI esteja acima de 50 + RsiSellOffset.
Condições de entrada
Compra: ambos os rastreadores fractais reportam +1 e o filtro RSI é otimista. A estratégia abre uma posição longa se for plana ou curta, enviando volume suficiente para virar para o lado comprado.
Venda: Ambos os rastreadores fractais reportam -1 e o filtro RSI é de baixa. A estratégia abre uma posição curta se for plana ou longa, enviando volume suficiente para virar para o lado vendido.
Gerenciamento de posição
Os níveis protetores de stop-loss e take-profit são calculados imediatamente após a mudança de posição com base em StopLossPips e TakeProfitPips multiplicados pelo tamanho do pip do instrumento.
A posição pode ser fechada quando o preço atingir o stop ou alvo, quando ExpiryMinutes decorrer ou quando CloseOnOppositeSignal estiver ativado e o sinal for revertido.
Parâmetros
Nome
Descrição
Padrão
Volume
Volume de pedidos usado para cada entrada.
0.1
TakeProfitPips
Distância alvo de lucro em pips. Defina como 0 para desativar.
700
StopLossPips
Distância de stop-loss em pips. Defina como 0 para desativar.
2500
ExpiryMinutes
Tempo máximo de espera em minutos antes de forçar uma saída. 0 desativa o cronômetro.
5555
CloseOnOppositeSignal
Feche a posição ativa quando o sinal mudar para a direção oposta.
false
PrimaryBuyShift
As barras voltam para inspecionar o fractal de alta no período de negociação.
3
HigherBuyShift
As barras voltam para inspecionar o fractal de alta no período de tempo mais alto.
3
PrimarySellShift
As barras voltam para inspecionar o fractal de baixa no período de negociação.
3
HigherSellShift
As barras voltam para inspecionar o fractal de baixa no período de tempo mais alto.
3
RsiBuyOffset
Deslocamento abaixo de 50 necessário para configurações longas.
3
RsiSellOffset
Deslocamento acima de 50 necessário para configurações curtas.
3
RsiPeriod
Duração de RSI no período diário.
3
CandleType
Tipo de vela de período de negociação.
Velas de 15 minutos
HigherTimeFrame
Tipo de vela de prazo de confirmação.
Velas de 1 hora
DailyTimeFrame
Tipo de vela usado para o diário RSI.
Velas de 1 dia
Notas de implementação
A estratégia utiliza a assinatura de velas de alto nível API (SubscribeCandles().Bind(...)) e gerencia indicadores internamente sem expô-los por meio de Strategy.Indicators, conforme exigido pelas diretrizes.
Fractals são calculados por meio de um auxiliar interno que armazena uma janela rolante de cinco velas e atualiza o sinal somente após a confirmação de um fractal.
Os valores RSI são recuperados via RelativeStrengthIndex.Process(...) com o preço de abertura da vela, correspondendo ao modo MetaTrader PRICE_OPEN.
Apenas uma posição é mantida por vez. As ordens de mercado invertem a posição quando necessário, adicionando o volume necessário para cobrir uma exposição existente.
O tamanho do pip é estimado a partir de Security.PriceStep e Security.Decimals, usando um multiplicador de 10x para ativos cotados com três ou mais casas decimais, reproduzindo a conversão de MetaTrader Point para pip.
Dicas de uso
As mudanças fractais devem ser grandes o suficiente para garantir que o índice de vela solicitado exista. Com uma mudança de três, o rastreador requer pelo menos cinco velas finalizadas por período de tempo antes de gerar sinais.
Ao negociar instrumentos com diferentes tamanhos de tick (por exemplo, índices ou ações), ajuste TakeProfitPips e StopLossPips para corresponder à definição de pip do instrumento.
Desativar CloseOnOppositeSignal replica o comportamento original do consultor especialista (foi desativado por padrão) e depende apenas de paradas, metas ou do cronômetro de expiração para saídas.
A estratégia não implementa o Martingale ou o dimensionamento baseado no risco; o cálculo do lote MetaTrader baseou-se em funções de margem da conta que não estão disponíveis em StockSharp. Use o parâmetro Volume ou envolva a estratégia em um gerenciador de portfólio se o dimensionamento dinâmico da posição for necessário.
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()