Стратегия Adaptive Fibonacci Pullback
Стратегия усредняет три линии SuperTrend с коэффициентами Фибоначчи (0.618, 1.618, 2.618) и сглаживает результат EMA. Торги ведутся по откатам к адаптивному тренду, направление подтверждается средней линией AMA и опциональным фильтром RSI.
Детали
- Условия входа:
- Лоу ниже средней SuperTrend и закрытие выше её сглаженного значения.
- Предыдущее закрытие относительно средней AMA определяет откат.
- Лонг: закрытие выше средней и RSI > порога.
- Шорт: закрытие ниже средней и RSI < порога.
- Лонг/Шорт: обе стороны.
- Условия выхода:
- Закрытие пересекает сглаженный SuperTrend в противоположную сторону.
- Стопы: процентные стоп-лосс и тейк-профит через
StartProtection. - Значения по умолчанию:
AtrPeriod= 8SmoothLength= 21AmaLength= 55RsiLength= 7RsiBuy= 70RsiSell= 30TakeProfitPercent= 5StopLossPercent= 0.75
- Фильтры:
- Категория: откат по тренду
- Направление: обе
- Индикаторы: SuperTrend, EMA, AMA, RSI
- Стопы: да
- Сложность: средняя
- Таймфрейм: любой
- Сезонность: нет
- Нейросети: нет
- Дивергенции: нет
- Уровень риска: средний
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()