Стратегия разворота от уровней Фибоначчи
Рынки часто откатываются на часть предыдущего движения перед продолжением тренда. Эта стратегия определяет недавние экстремумы и отслеживает тест уровня коррекции 61.8% или 78.6%, где нередко происходит истощение.
Тестирование показывает среднегодичную доходность около 115%. Стратегию лучше запускать на фондовом рынке.
Алгоритм ведёт учёт свечных экстремумов за скользящее окно и вычисляет между ними уровни Фибоначчи. Когда цена приближается к ключевой коррекции и формирует свечу в сторону исходного тренда, открывается сделка со стопом на фиксированном процентном расстоянии. Цели располагаются около уровня 50% от размера свинга.
Фокусируясь на глубоких откатах в существующем тренде, метод стремится захватить начальные стадии продолжения после кратковременного доминирования продавцов или покупателей.
Детали
- Условия входа: Цена тестирует уровень 61.8% или 78.6% и печатает подтверждающую свечу.
- Лонг/Шорт: Оба в зависимости от тренда.
- Условия выхода: Цена достигает уровня 50% или стоп‑лосс.
- Стопы: Да, на процентной основе.
- Значения по умолчанию:
SwingLookbackPeriod= 20FibLevelBuffer= 0.5 Закрытие не дальше этого числа процентов от уровня коррекции считается его тестом.CandleType= 5 минутStopLossPercent= 2
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: Уровни Фибоначчи
- Стопы: Да
- Сложность: Продвинутая
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейронные сети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Fibonacci Retracement Reversal strategy.
/// The swing is the highest high and lowest low of the previous SwingLookbackPeriod candles; it rose when the low came first.
/// In a rising swing a bullish candle closing within FibLevelBuffer percent of the 61.8% or 78.6% retracement buys,
/// in a falling swing a bearish candle near those levels sells. The target is the swing's 50% level, and a percent stop protects the trade.
/// </summary>
public class FibonacciRetracementReversalStrategy : Strategy
{
private readonly StrategyParam<int> _swingLookbackPeriod;
private readonly StrategyParam<decimal> _fibLevelBuffer;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<(decimal High, decimal Low)> _candles = [];
private decimal _target;
/// <summary>
/// Number of previous candles that form the swing.
/// </summary>
public int SwingLookbackPeriod
{
get => _swingLookbackPeriod.Value;
set => _swingLookbackPeriod.Value = value;
}
/// <summary>
/// How close to a retracement level the close must be, in percent of the level.
/// </summary>
public decimal FibLevelBuffer
{
get => _fibLevelBuffer.Value;
set => _fibLevelBuffer.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public FibonacciRetracementReversalStrategy()
{
_swingLookbackPeriod = Param(nameof(SwingLookbackPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Swing Lookback", "Previous candles that form the swing", "Indicators");
_fibLevelBuffer = Param(nameof(FibLevelBuffer), 0.5m)
.SetNotNegative()
.SetDisplay("Level Buffer %", "Distance from a retracement level that counts as a test, in percent", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
_target = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
_target = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// The swing is formed by the candles before this one.
var swing = _candles.ToArray();
_candles.Add((candle.HighPrice, candle.LowPrice));
if (_candles.Count > SwingLookbackPeriod)
_candles.RemoveAt(0);
if (swing.Length < SwingLookbackPeriod || !IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _target)
SellMarket(Position);
return;
}
if (Position < 0)
{
if (close <= _target)
BuyMarket(-Position);
return;
}
var highIndex = Array.FindIndex(swing, c => c.High == swing.Max(x => x.High));
var lowIndex = Array.FindIndex(swing, c => c.Low == swing.Min(x => x.Low));
var high = swing[highIndex].High;
var low = swing[lowIndex].Low;
var range = high - low;
if (range <= 0)
return;
var middle = low + range / 2m;
bool Near(decimal level) => Math.Abs(close - level) <= level * FibLevelBuffer / 100m;
if (lowIndex < highIndex)
{
// Rising swing: buy a bullish candle at a deep pullback.
if (close > candle.OpenPrice && close < middle && (Near(high - range * 0.618m) || Near(high - range * 0.786m)))
{
BuyMarket(Volume);
_target = middle;
}
}
else if (highIndex < lowIndex)
{
// Falling swing: sell a bearish candle at a deep rebound.
if (close < candle.OpenPrice && close > middle && (Near(low + range * 0.618m) || Near(low + range * 0.786m)))
{
SellMarket(Volume);
_target = middle;
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class fibonacci_retracement_reversal_strategy(Strategy):
"""
Fibonacci Retracement Reversal strategy.
The swing is the highest high and lowest low of the previous SwingLookbackPeriod candles; it rose when the low came first.
In a rising swing a bullish candle closing within FibLevelBuffer percent of the 61.8% or 78.6% retracement buys,
in a falling swing a bearish candle near those levels sells. The target is the swing's 50% level, and a percent stop protects the trade.
"""
def __init__(self):
super(fibonacci_retracement_reversal_strategy, self).__init__()
self._swing_lookback_period = self.Param("SwingLookbackPeriod", 20).SetGreaterThanZero().SetDisplay("Swing Lookback", "Previous candles that form the swing", "Indicators")
self._fib_level_buffer = self.Param("FibLevelBuffer", 0.5).SetNotNegative().SetDisplay("Level Buffer %", "Distance from a retracement level that counts as a test, in percent", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._candles = []
self._target = Decimal(0)
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fibonacci_retracement_reversal_strategy, self).OnReseted()
self._candles = []
self._target = Decimal(0)
def OnStarted2(self, time):
super(fibonacci_retracement_reversal_strategy, self).OnStarted2(time)
self._candles = []
self._target = Decimal(0)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
# The swing is formed by the candles before this one.
period = self._swing_lookback_period.Value
swing = list(self._candles)
self._candles.append((candle.HighPrice, candle.LowPrice))
if len(self._candles) > period:
self._candles.pop(0)
if len(swing) < period or not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
if self.Position > 0:
if close >= self._target:
self.SellMarket(self.Position)
return
if self.Position < 0:
if close <= self._target:
self.BuyMarket(-self.Position)
return
high = max(c[0] for c in swing)
low = min(c[1] for c in swing)
high_index = next(i for i, c in enumerate(swing) if c[0] == high)
low_index = next(i for i, c in enumerate(swing) if c[1] == low)
price_range = high - low
if price_range <= 0:
return
middle = low + price_range / Decimal(2)
buffer = Decimal(self._fib_level_buffer.Value) / Decimal(100)
def near(level):
return Math.Abs(close - level) <= level * buffer
if low_index < high_index:
# Rising swing: buy a bullish candle at a deep pullback.
if close > candle.OpenPrice and close < middle and (near(high - price_range * Decimal(0.618)) or near(high - price_range * Decimal(0.786))):
self.BuyMarket(self.Volume)
self._target = middle
elif high_index < low_index:
# Falling swing: sell a bearish candle at a deep rebound.
if close < candle.OpenPrice and close > middle and (near(low + price_range * Decimal(0.618)) or near(low + price_range * Decimal(0.786))):
self.SellMarket(self.Volume)
self._target = middle
def CreateClone(self):
return fibonacci_retracement_reversal_strategy()