Estrategia de Pullback Fibonacci Adaptativo
La estrategia promedia tres líneas SuperTrend construidas con multiplicadores de Fibonacci (0.618, 1.618, 2.618) y suaviza el resultado con una EMA. Las operaciones siguen pullbacks hacia esta tendencia adaptativa, mientras que una línea media basada en AMA y un filtro RSI opcional confirman la dirección.
Detalles
- Criterios de entrada:
- Mínimo por debajo del SuperTrend promediado y cierre por encima de su valor suavizado.
- El cierre previo relativo a la línea media AMA define el pullback.
- Largo: cierre por encima de la línea media y RSI > umbral.
- Corto: cierre por debajo de la línea media y RSI < umbral.
- Largo/Corto: Ambos lados.
- Criterios de salida:
- Cierre que cruza el SuperTrend suavizado en dirección opuesta.
- Stops: Stop loss y take profit porcentuales mediante
StartProtection. - Valores predeterminados:
AtrPeriod= 8SmoothLength= 21AmaLength= 55RsiLength= 7RsiBuy= 70RsiSell= 30TakeProfitPercent= 5StopLossPercent= 0.75
- Filtros:
- Categoría: Pullback de tendencia
- Dirección: Ambos
- Indicadores: SuperTrend, EMA, AMA, RSI
- Stops: Sí
- Complejidad: Moderado
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Adaptive Fibonacci Pullback strategy.
/// Combines multiple SuperTrend lines and an adaptive moving average channel.
/// </summary>
public class AdaptiveFibonacciPullbackStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _factor1;
private readonly StrategyParam<decimal> _factor2;
private readonly StrategyParam<decimal> _factor3;
private readonly StrategyParam<int> _smoothLength;
private readonly StrategyParam<int> _amaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _rsiBuy;
private readonly StrategyParam<decimal> _rsiSell;
private readonly StrategyParam<int> _cooldownBars;
private SuperTrend _st1;
private SuperTrend _st2;
private SuperTrend _st3;
private ExponentialMovingAverage _stSmooth;
private ExponentialMovingAverage _amaMid;
private RelativeStrengthIndex _rsi;
private decimal _prevClose;
private decimal _prevSmooth;
private bool _isFirst;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal Factor1 { get => _factor1.Value; set => _factor1.Value = value; }
public decimal Factor2 { get => _factor2.Value; set => _factor2.Value = value; }
public decimal Factor3 { get => _factor3.Value; set => _factor3.Value = value; }
public int SmoothLength { get => _smoothLength.Value; set => _smoothLength.Value = value; }
public int AmaLength { get => _amaLength.Value; set => _amaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public decimal RsiBuy { get => _rsiBuy.Value; set => _rsiBuy.Value = value; }
public decimal RsiSell { get => _rsiSell.Value; set => _rsiSell.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AdaptiveFibonacciPullbackStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_atrPeriod = Param(nameof(AtrPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("SuperTrend ATR Length", "ATR period for SuperTrend", "SuperTrend");
_factor1 = Param(nameof(Factor1), 0.618m)
.SetDisplay("Factor 1", "Weak Fibonacci factor", "SuperTrend");
_factor2 = Param(nameof(Factor2), 1.618m)
.SetDisplay("Factor 2", "Golden Ratio factor", "SuperTrend");
_factor3 = Param(nameof(Factor3), 2.618m)
.SetDisplay("Factor 3", "Extended Fibonacci factor", "SuperTrend");
_smoothLength = Param(nameof(SmoothLength), 21)
.SetGreaterThanZero()
.SetDisplay("Smoothing Length", "EMA length for SuperTrend average", "SuperTrend");
_amaLength = Param(nameof(AmaLength), 55)
.SetGreaterThanZero()
.SetDisplay("AMA Length", "Length for AMA midline", "AMA");
_rsiLength = Param(nameof(RsiLength), 7)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_rsiBuy = Param(nameof(RsiBuy), 50m)
.SetDisplay("RSI Buy Threshold", "RSI must be above for long", "RSI");
_rsiSell = Param(nameof(RsiSell), 50m)
.SetDisplay("RSI Sell Threshold", "RSI must be below for short", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevSmooth = default;
_isFirst = true;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_st1 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor1 };
_st2 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor2 };
_st3 = new SuperTrend { Length = AtrPeriod, Multiplier = Factor3 };
_stSmooth = new ExponentialMovingAverage { Length = SmoothLength };
_amaMid = new ExponentialMovingAverage { Length = AmaLength };
_rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(new IIndicator[] { _st1, _st2, _st3, _amaMid, _rsi }, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (values[0].IsEmpty || values[1].IsEmpty || values[2].IsEmpty ||
values[3].IsEmpty || values[4].IsEmpty)
return;
var st1Val = values[0].ToDecimal();
var st2Val = values[1].ToDecimal();
var st3Val = values[2].ToDecimal();
var mid = values[3].ToDecimal();
var rsiVal = values[4].ToDecimal();
var avg = (st1Val + st2Val + st3Val) / 3m;
var smooth = _stSmooth.Process(new DecimalIndicatorValue(_stSmooth, avg, candle.ServerTime)).ToDecimal();
if (_isFirst)
{
_prevClose = candle.ClosePrice;
_prevSmooth = smooth;
_isFirst = false;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = candle.ClosePrice;
_prevSmooth = smooth;
return;
}
var baseLong = candle.LowPrice < avg && candle.ClosePrice > smooth && _prevClose > mid;
var baseShort = candle.HighPrice > avg && candle.ClosePrice < smooth && _prevClose < mid;
var longEntry = baseLong && candle.ClosePrice > mid && rsiVal > RsiBuy;
var shortEntry = baseShort && candle.ClosePrice < mid && rsiVal < RsiSell;
if (longEntry && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
else if (shortEntry && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit conditions
var longExit = _prevClose > _prevSmooth && candle.ClosePrice <= smooth && Position > 0;
var shortExit = _prevClose < _prevSmooth && candle.ClosePrice >= smooth && Position < 0;
if (longExit)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
else if (shortExit)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevClose = candle.ClosePrice;
_prevSmooth = smooth;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Array
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (SuperTrend, ExponentialMovingAverage,
RelativeStrengthIndex,
IndicatorHelper, IIndicator)
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class adaptive_fibonacci_pullback_strategy(Strategy):
"""Adaptive Fibonacci Pullback Strategy."""
def __init__(self):
super(adaptive_fibonacci_pullback_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._atr_period = self.Param("AtrPeriod", 8) \
.SetDisplay("SuperTrend ATR Length", "ATR period for SuperTrend", "SuperTrend")
self._factor1 = self.Param("Factor1", 0.618) \
.SetDisplay("Factor 1", "Weak Fibonacci factor", "SuperTrend")
self._factor2 = self.Param("Factor2", 1.618) \
.SetDisplay("Factor 2", "Golden Ratio factor", "SuperTrend")
self._factor3 = self.Param("Factor3", 2.618) \
.SetDisplay("Factor 3", "Extended Fibonacci factor", "SuperTrend")
self._smooth_length = self.Param("SmoothLength", 21) \
.SetDisplay("Smoothing Length", "EMA length for SuperTrend average", "SuperTrend")
self._ama_length = self.Param("AmaLength", 55) \
.SetDisplay("AMA Length", "Length for AMA midline", "AMA")
self._rsi_length = self.Param("RsiLength", 7) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._rsi_buy = self.Param("RsiBuy", 50.0) \
.SetDisplay("RSI Buy Threshold", "RSI must be above for long", "RSI")
self._rsi_sell = self.Param("RsiSell", 50.0) \
.SetDisplay("RSI Sell Threshold", "RSI must be below for short", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._st1 = None
self._st2 = None
self._st3 = None
self._st_smooth = None
self._ama_mid = None
self._rsi = None
self._prev_close = 0.0
self._prev_smooth = 0.0
self._is_first = True
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adaptive_fibonacci_pullback_strategy, self).OnReseted()
self._st1 = None
self._st2 = None
self._st3 = None
self._st_smooth = None
self._ama_mid = None
self._rsi = None
self._prev_close = 0.0
self._prev_smooth = 0.0
self._is_first = True
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(adaptive_fibonacci_pullback_strategy, self).OnStarted2(time)
self._st1 = SuperTrend()
self._st1.Length = int(self._atr_period.Value)
self._st1.Multiplier = self._factor1.Value
self._st2 = SuperTrend()
self._st2.Length = int(self._atr_period.Value)
self._st2.Multiplier = self._factor2.Value
self._st3 = SuperTrend()
self._st3.Length = int(self._atr_period.Value)
self._st3.Multiplier = self._factor3.Value
self._st_smooth = ExponentialMovingAverage()
self._st_smooth.Length = int(self._smooth_length.Value)
self._ama_mid = ExponentialMovingAverage()
self._ama_mid.Length = int(self._ama_length.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
indicators = Array[IIndicator]([self._st1, self._st2, self._st3, self._ama_mid, self._rsi])
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(indicators, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, values):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if values[0].IsEmpty or values[1].IsEmpty or values[2].IsEmpty or values[3].IsEmpty or values[4].IsEmpty:
return
st1_v = float(IndicatorHelper.ToDecimal(values[0]))
st2_v = float(IndicatorHelper.ToDecimal(values[1]))
st3_v = float(IndicatorHelper.ToDecimal(values[2]))
mid = float(IndicatorHelper.ToDecimal(values[3]))
rsi_v = float(IndicatorHelper.ToDecimal(values[4]))
avg = (st1_v + st2_v + st3_v) / 3.0
smooth_result = process_float(self._st_smooth, avg, candle.ServerTime, True)
smooth = float(IndicatorHelper.ToDecimal(smooth_result))
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
if self._is_first:
self._prev_close = close
self._prev_smooth = smooth
self._is_first = False
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_smooth = smooth
return
low = float(candle.LowPrice)
high = float(candle.HighPrice)
rsi_buy = float(self._rsi_buy.Value)
rsi_sell = float(self._rsi_sell.Value)
base_long = low < avg and close > smooth and self._prev_close > mid
base_short = high > avg and close < smooth and self._prev_close < mid
long_entry = base_long and close > mid and rsi_v > rsi_buy
short_entry = base_short and close < mid and rsi_v < rsi_sell
if long_entry and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif short_entry and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
# Exit conditions
long_exit = self._prev_close > self._prev_smooth and close <= smooth and self.Position > 0
short_exit = self._prev_close < self._prev_smooth and close >= smooth and self.Position < 0
if long_exit:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif short_exit:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_close = close
self._prev_smooth = smooth
def CreateClone(self):
return adaptive_fibonacci_pullback_strategy()