Fibo Stop Strategy
Overview
The Fibo Stop strategy trails the protective stop along Fibonacci retracement levels defined by two prices: start and end. The strategy opens a position in the direction from the start level to the end level and moves the stop to each new Fibonacci level once price crosses it.
Algorithm
- Determine direction from start to end price. If end is higher than start, a long position is opened; otherwise a short position.
- Calculate Fibonacci levels: 0%, 23.6%, 38.6%, 50%, 61.8%, 78.6%, 100%, 127% based on the range.
- Initial stop is placed behind the start level using the specified offset in price steps.
- As the market price moves and crosses the next Fibonacci level, the stop is moved to that level minus/plus the offset.
- Position is closed when price hits the trailing stop.
Parameters
FiboStart– base price where Fibonacci calculation begins.FiboEnd– final price defining the Fibonacci range.OffsetPoints– number of price steps added behind each Fibonacci level to place the stop.CandleType– candle series used for monitoring price.
Notes
The strategy uses only completed candles and does not rely on indicator value history. It is intended as an example of managing a trailing stop with high-level StockSharp API.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that uses Fibonacci retracement levels for entry and trailing stop.
/// Enters on pullback to Fibonacci level and trails stop along levels.
/// </summary>
public class FiboStopStrategy : Strategy
{
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<decimal> _entryFiboLevel;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _highestHigh;
private decimal _lowestLow;
private int _barCount;
private int _barsSinceTrade;
private bool _rangeSet;
private decimal _entryPrice;
public int LookbackPeriod { get => _lookbackPeriod.Value; set => _lookbackPeriod.Value = value; }
public decimal EntryFiboLevel { get => _entryFiboLevel.Value; set => _entryFiboLevel.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FiboStopStrategy()
{
_lookbackPeriod = Param(nameof(LookbackPeriod), 500)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Bars to calculate high/low range", "General");
_entryFiboLevel = Param(nameof(EntryFiboLevel), 0.382m)
.SetDisplay("Entry Fibo", "Fibonacci level for entry (0.236, 0.382, 0.5, 0.618)", "Fibonacci");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 50)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Bars between new entries", "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();
_highestHigh = decimal.MinValue;
_lowestLow = decimal.MaxValue;
_barCount = 0;
_barsSinceTrade = CooldownBars;
_rangeSet = false;
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highestHigh = decimal.MinValue;
_lowestLow = decimal.MaxValue;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
takeProfit: new Unit(2, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_barCount++;
_barsSinceTrade++;
if (candle.HighPrice > _highestHigh)
_highestHigh = candle.HighPrice;
if (candle.LowPrice < _lowestLow)
_lowestLow = candle.LowPrice;
if (_barCount < LookbackPeriod)
return;
if (!_rangeSet)
{
_rangeSet = true;
return;
}
var range = _highestHigh - _lowestLow;
if (range <= 0)
return;
// Calculate Fibonacci retracement levels from high
var fiboLevel = _highestHigh - range * EntryFiboLevel;
var fibo618 = _highestHigh - range * 0.618m;
if (Position == 0)
{
if (_barsSinceTrade < CooldownBars)
return;
if (candle.ClosePrice <= fiboLevel && candle.ClosePrice > fibo618)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_barsSinceTrade = 0;
}
else if (candle.ClosePrice >= _lowestLow + range * (1m - EntryFiboLevel) && candle.ClosePrice < _lowestLow + range * 0.382m)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_barsSinceTrade = 0;
}
}
// Exits handled by StartProtection
// Update rolling high/low
if (_barCount > LookbackPeriod * 2)
{
_highestHigh = candle.HighPrice;
_lowestLow = candle.LowPrice;
_barCount = LookbackPeriod;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class fibo_stop_strategy(Strategy):
def __init__(self):
super(fibo_stop_strategy, self).__init__()
self._lookback_period = self.Param("LookbackPeriod", 500) \
.SetGreaterThanZero() \
.SetDisplay("Lookback", "Bars to calculate high/low range", "General")
self._entry_fibo_level = self.Param("EntryFiboLevel", 0.382) \
.SetDisplay("Entry Fibo", "Fibonacci level for entry (0.236, 0.382, 0.5, 0.618)", "Fibonacci")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 50) \
.SetGreaterThanZero() \
.SetDisplay("Cooldown Bars", "Bars between new entries", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._highest_high = -1e18
self._lowest_low = 1e18
self._bar_count = 0
self._bars_since_trade = 0
self._range_set = False
self._entry_price = 0.0
@property
def lookback_period(self):
return self._lookback_period.Value
@property
def entry_fibo_level(self):
return self._entry_fibo_level.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fibo_stop_strategy, self).OnReseted()
self._highest_high = -1e18
self._lowest_low = 1e18
self._bar_count = 0
self._bars_since_trade = self.cooldown_bars
self._range_set = False
self._entry_price = 0.0
def OnStarted2(self, time):
super(fibo_stop_strategy, self).OnStarted2(time)
self._highest_high = -1e18
self._lowest_low = 1e18
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
Unit(float(self.stop_loss_pct), UnitTypes.Percent),
Unit(2, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._bar_count += 1
self._bars_since_trade += 1
h = float(candle.HighPrice)
l = float(candle.LowPrice)
if h > self._highest_high:
self._highest_high = h
if l < self._lowest_low:
self._lowest_low = l
if self._bar_count < self.lookback_period:
return
if not self._range_set:
self._range_set = True
return
rng = self._highest_high - self._lowest_low
if rng <= 0:
return
fibo = float(self.entry_fibo_level)
fibo_level = self._highest_high - rng * fibo
fibo_618 = self._highest_high - rng * 0.618
close = float(candle.ClosePrice)
if self.Position == 0:
if self._bars_since_trade < self.cooldown_bars:
return
if close <= fibo_level and close > fibo_618:
self.BuyMarket()
self._entry_price = close
self._bars_since_trade = 0
elif close >= self._lowest_low + rng * (1.0 - fibo) and close < self._lowest_low + rng * 0.382:
self.SellMarket()
self._entry_price = close
self._bars_since_trade = 0
if self._bar_count > self.lookback_period * 2:
self._highest_high = h
self._lowest_low = l
self._bar_count = self.lookback_period
def CreateClone(self):
return fibo_stop_strategy()