Fibonacci Swing Trading Bot
Strategy using Fibonacci retracement levels to trade swing moves.
This bot calculates 0.618 and 0.786 retracement levels from the last 50-bar range and opens positions when candles break above or below these levels. Risk management is handled through configurable stop loss and risk/reward parameters.
Details
- Entry Criteria: Price action with Fibonacci levels.
- Long/Short: Both directions.
- Exit Criteria: Stop loss or take profit.
- Stops: Yes, percent based.
- Default Values:
FiboLevel1= 0.618FiboLevel2= 0.786RiskRewardRatio= 2StopLossPercent= 1CandleType= TimeSpan.FromHours(4)
- Filters:
- Category: Swing
- Direction: Both
- Indicators: Fibonacci, Donchian
- Stops: Yes
- Complexity: Intermediate
- Timeframe: 4h
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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>
/// Fibonacci Swing Trading Bot strategy.
/// Retracement levels FiboLevel1 and FiboLevel2 are measured down from the highest high of the last 50 candles towards the
/// lowest low. A close breaking above the FiboLevel1 level goes long and a close breaking below the FiboLevel2 level goes short,
/// reversing an opposite position. Each position is protected by a percent stop loss and a take profit RiskRewardRatio times
/// further away.
/// </summary>
public class FibonacciSwingTradingBotStrategy : Strategy
{
private const int _rangeLength = 50;
private readonly StrategyParam<decimal> _fiboLevel1;
private readonly StrategyParam<decimal> _fiboLevel2;
private readonly StrategyParam<decimal> _riskRewardRatio;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevClose;
private decimal? _prevLevel1;
private decimal? _prevLevel2;
/// <summary>
/// Retracement ratio of the level whose upward break goes long.
/// </summary>
public decimal FiboLevel1
{
get => _fiboLevel1.Value;
set => _fiboLevel1.Value = value;
}
/// <summary>
/// Retracement ratio of the level whose downward break goes short.
/// </summary>
public decimal FiboLevel2
{
get => _fiboLevel2.Value;
set => _fiboLevel2.Value = value;
}
/// <summary>
/// Take profit distance as a multiple of the stop distance.
/// </summary>
public decimal RiskRewardRatio
{
get => _riskRewardRatio.Value;
set => _riskRewardRatio.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </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 FibonacciSwingTradingBotStrategy()
{
_fiboLevel1 = Param(nameof(FiboLevel1), 0.618m)
.SetDisplay("Fibo Level 1", "Retracement ratio of the long breakout level", "Fibonacci");
_fiboLevel2 = Param(nameof(FiboLevel2), 0.786m)
.SetDisplay("Fibo Level 2", "Retracement ratio of the short breakout level", "Fibonacci");
_riskRewardRatio = Param(nameof(RiskRewardRatio), 2m)
.SetNotNegative()
.SetDisplay("Risk/Reward", "Take profit distance as a multiple of the stop distance", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
ResetState();
}
private void ResetState()
{
_prevClose = null;
_prevLevel1 = null;
_prevLevel2 = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var highest = new Highest { Length = _rangeLength };
var lowest = new Lowest { Length = _rangeLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
StartProtection(
new Unit(StopLossPercent * RiskRewardRatio, UnitTypes.Percent),
new Unit(StopLossPercent, UnitTypes.Percent),
useMarketOrders: true,
isLocalStop: true);
// The stop and target have 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);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
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, decimal high, decimal low)
{
if (candle.State != CandleStates.Finished)
return;
var range = high - low;
var level1 = high - range * FiboLevel1;
var level2 = high - range * FiboLevel2;
var prevClose = _prevClose;
var prevLevel1 = _prevLevel1;
var prevLevel2 = _prevLevel2;
_prevClose = candle.ClosePrice;
_prevLevel1 = level1;
_prevLevel2 = level2;
if (prevClose is not decimal lastClose || prevLevel1 is not decimal lastLevel1 || prevLevel2 is not decimal lastLevel2)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (lastClose <= lastLevel1 && close > level1 && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (lastClose >= lastLevel2 && close < level2 && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
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, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
RANGE_LENGTH = 50
class fibonacci_swing_trading_bot_strategy(Strategy):
"""
Fibonacci Swing Trading Bot strategy.
Retracement levels FiboLevel1 and FiboLevel2 are measured down from the highest high of the last 50 candles towards the
lowest low. A close breaking above the FiboLevel1 level goes long and a close breaking below the FiboLevel2 level goes short,
reversing an opposite position. Each position is protected by a percent stop loss and a take profit RiskRewardRatio times
further away.
"""
def __init__(self):
super(fibonacci_swing_trading_bot_strategy, self).__init__()
self._fibo_level1 = self.Param("FiboLevel1", 0.618).SetDisplay("Fibo Level 1", "Retracement ratio of the long breakout level", "Fibonacci")
self._fibo_level2 = self.Param("FiboLevel2", 0.786).SetDisplay("Fibo Level 2", "Retracement ratio of the short breakout level", "Fibonacci")
self._risk_reward_ratio = self.Param("RiskRewardRatio", 2.0).SetNotNegative().SetDisplay("Risk/Reward", "Take profit distance as a multiple of the stop distance", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_close = None
self._prev_level1 = None
self._prev_level2 = None
def OnReseted(self):
super(fibonacci_swing_trading_bot_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(fibonacci_swing_trading_bot_strategy, self).OnStarted2(time)
self._reset_state()
highest = Highest()
highest.Length = RANGE_LENGTH
lowest = Lowest()
lowest.Length = RANGE_LENGTH
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self._process_candle).Start()
stop = float(self._stop_loss_percent.Value)
take = stop * float(self._risk_reward_ratio.Value)
self.StartProtection(Unit(Decimal(take), UnitTypes.Percent), Unit(Decimal(stop), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop and target have 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.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
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, high_value, low_value):
if candle.State != CandleStates.Finished:
return
high = float(high_value)
low = float(low_value)
rng = high - low
level1 = high - rng * float(self._fibo_level1.Value)
level2 = high - rng * float(self._fibo_level2.Value)
close = float(candle.ClosePrice)
last_close = self._prev_close
last_level1 = self._prev_level1
last_level2 = self._prev_level2
self._prev_close = close
self._prev_level1 = level1
self._prev_level2 = level2
if last_close is None or last_level1 is None or last_level2 is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if last_close <= last_level1 and close > level1 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif last_close >= last_level2 and close < level2 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return fibonacci_swing_trading_bot_strategy()