Fibo iSAR Strategy
This strategy combines fast and slow Parabolic SAR indicators with Fibonacci retracement levels. When the fast SAR lies above the slow SAR and below the price, the strategy expects an uptrend and places a buy limit order at the 50% Fibonacci retracement of the recent range. The stop loss is placed below the swing low and the take profit at the 161% extension. For a downtrend the logic is mirrored with sell limit orders.
Details
- Entry Criteria: Trend direction from fast/slow SAR; entry on 50% Fibonacci retracement.
- Long/Short: Both directions.
- Exit Criteria: Stop loss or take profit levels.
- Stops: Yes.
- Default Values:
StepFast= 0.02MaximumFast= 0.2StepSlow= 0.01MaximumSlow= 0.1CountBarSearch= 3IndentStopLoss= 30FiboEntranceLevel= 50FiboProfitLevel= 161CandleType= TimeSpan.FromMinutes(5)
- Filters:
- Category: Trend
- Direction: Both
- Indicators: Parabolic SAR, Fibonacci
- Stops: Yes
- Complexity: Moderate
- Timeframe: Intraday (5m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on fast and slow Parabolic SAR combined with Fibonacci levels.
/// It places pending limit orders at 50% retracement and exits at predefined stop or target.
/// </summary>
public class FiboISarStrategy : Strategy
{
// parameters
private readonly StrategyParam<decimal> _stepFast;
private readonly StrategyParam<decimal> _maxFast;
private readonly StrategyParam<decimal> _stepSlow;
private readonly StrategyParam<decimal> _maxSlow;
private readonly StrategyParam<int> _countBarSearch;
private readonly StrategyParam<int> _indentStopLoss;
private readonly StrategyParam<decimal> _fiboEntranceLevel;
private readonly StrategyParam<decimal> _fiboProfitLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _useTimeFilter;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _stopHour;
// indicators
private ParabolicSar _fastSar = null!;
private ParabolicSar _slowSar = null!;
private Highest _highest = null!;
private Lowest _lowest = null!;
// state
private Order _pendingOrder;
private decimal _stopPrice;
private decimal _takeProfitPrice;
/// <summary>
/// Step for the fast SAR indicator.
/// </summary>
public decimal StepFast { get => _stepFast.Value; set => _stepFast.Value = value; }
/// <summary>
/// Maximum acceleration for the fast SAR indicator.
/// </summary>
public decimal MaximumFast { get => _maxFast.Value; set => _maxFast.Value = value; }
/// <summary>
/// Step for the slow SAR indicator.
/// </summary>
public decimal StepSlow { get => _stepSlow.Value; set => _stepSlow.Value = value; }
/// <summary>
/// Maximum acceleration for the slow SAR indicator.
/// </summary>
public decimal MaximumSlow { get => _maxSlow.Value; set => _maxSlow.Value = value; }
/// <summary>
/// Lookback period for searching extremes.
/// </summary>
public int CountBarSearch { get => _countBarSearch.Value; set => _countBarSearch.Value = value; }
/// <summary>
/// Stop loss offset in pips.
/// </summary>
public int IndentStopLoss { get => _indentStopLoss.Value; set => _indentStopLoss.Value = value; }
/// <summary>
/// Fibonacci level for placing limit orders (percent).
/// </summary>
public decimal FiboEntranceLevel { get => _fiboEntranceLevel.Value; set => _fiboEntranceLevel.Value = value; }
/// <summary>
/// Fibonacci level for profit taking (percent).
/// </summary>
public decimal FiboProfitLevel { get => _fiboProfitLevel.Value; set => _fiboProfitLevel.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Enable trading hours filter.
/// </summary>
public bool UseTimeFilter { get => _useTimeFilter.Value; set => _useTimeFilter.Value = value; }
/// <summary>
/// Start hour for trading (local time).
/// </summary>
public int StartHour { get => _startHour.Value; set => _startHour.Value = value; }
/// <summary>
/// Stop hour for trading (local time).
/// </summary>
public int StopHour { get => _stopHour.Value; set => _stopHour.Value = value; }
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public FiboISarStrategy()
{
_stepFast = Param(nameof(StepFast), 0.02m)
.SetDisplay("Fast SAR Step", "Acceleration step for fast SAR", "Indicators")
;
_maxFast = Param(nameof(MaximumFast), 0.2m)
.SetDisplay("Fast SAR Max", "Maximum acceleration for fast SAR", "Indicators")
;
_stepSlow = Param(nameof(StepSlow), 0.01m)
.SetDisplay("Slow SAR Step", "Acceleration step for slow SAR", "Indicators")
;
_maxSlow = Param(nameof(MaximumSlow), 0.1m)
.SetDisplay("Slow SAR Max", "Maximum acceleration for slow SAR", "Indicators")
;
_countBarSearch = Param(nameof(CountBarSearch), 3)
.SetGreaterThanZero()
.SetDisplay("Count Bar Search", "Lookback for extremes", "General")
;
_indentStopLoss = Param(nameof(IndentStopLoss), 30)
.SetDisplay("Indent Stop Loss", "Stop loss offset in pips", "General")
;
_fiboEntranceLevel = Param(nameof(FiboEntranceLevel), 50m)
.SetDisplay("Fibo Entry", "Fibonacci entry level percentage", "Fibonacci")
;
_fiboProfitLevel = Param(nameof(FiboProfitLevel), 161m)
.SetDisplay("Fibo Profit", "Fibonacci profit level percentage", "Fibonacci")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_useTimeFilter = Param(nameof(UseTimeFilter), false)
.SetDisplay("Use Time Filter", "Enable trading hours filter", "General");
_startHour = Param(nameof(StartHour), 7)
.SetDisplay("Start Hour", "Trading start hour", "General");
_stopHour = Param(nameof(StopHour), 17)
.SetDisplay("Stop Hour", "Trading stop hour", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pendingOrder = null;
_stopPrice = 0m;
_takeProfitPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastSar = new ParabolicSar { Acceleration = StepFast, AccelerationMax = MaximumFast };
_slowSar = new ParabolicSar { Acceleration = StepSlow, AccelerationMax = MaximumSlow };
_highest = new Highest { Length = CountBarSearch };
_lowest = new Lowest { Length = CountBarSearch };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_fastSar, _slowSar, _highest, _lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastSar, decimal slowSar, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.CloseTime.Hour;
if (UseTimeFilter && (hour < StartHour || hour > StopHour))
{
CancelPendingOrder();
return;
}
var price = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Manage open position exits
if (Position > 0)
{
if (price <= _stopPrice || price >= _takeProfitPrice)
SellMarket();
}
else if (Position < 0)
{
if (price >= _stopPrice || price <= _takeProfitPrice)
BuyMarket();
}
// Remove pending order if opposite trend detected
if (_pendingOrder != null)
{
var side = _pendingOrder.Side;
if ((side == Sides.Buy && (slowSar > fastSar || fastSar >= price)) ||
(side == Sides.Sell && (slowSar < fastSar || fastSar <= price)))
{
CancelPendingOrder();
}
}
if (_pendingOrder != null || Position != 0)
return;
var rangeHigh = highest;
var rangeLow = lowest;
var range = rangeHigh - rangeLow;
if (slowSar < fastSar && fastSar < price)
{
var entry = rangeLow + range * (FiboEntranceLevel / 100m);
var profit = rangeLow + range * (FiboProfitLevel / 100m);
var stop = rangeLow - IndentStopLoss * step;
_stopPrice = stop;
_takeProfitPrice = profit;
_pendingOrder = BuyLimit(entry);
}
else if (slowSar > fastSar && fastSar > price)
{
var entry = rangeHigh - range * (FiboEntranceLevel / 100m);
var profit = rangeHigh - range * (FiboProfitLevel / 100m);
var stop = rangeHigh + IndentStopLoss * step;
_stopPrice = stop;
_takeProfitPrice = profit;
_pendingOrder = SellLimit(entry);
}
}
/// <inheritdoc />
protected override void OnOrderReceived(Order order)
{
base.OnOrderReceived(order);
if (_pendingOrder != null && order == _pendingOrder && order.State != OrderStates.Active)
_pendingOrder = null;
}
private void CancelPendingOrder()
{
if (_pendingOrder != null)
{
if (_pendingOrder.State == OrderStates.Active)
CancelOrder(_pendingOrder);
_pendingOrder = null;
}
}
}
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, Sides, OrderStates
from StockSharp.Algo.Indicators import ParabolicSar, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class fibo_i_sar_strategy(Strategy):
def __init__(self):
super(fibo_i_sar_strategy, self).__init__()
self._step_fast = self.Param("StepFast", 0.02)
self._max_fast = self.Param("MaximumFast", 0.2)
self._step_slow = self.Param("StepSlow", 0.01)
self._max_slow = self.Param("MaximumSlow", 0.1)
self._count_bar_search = self.Param("CountBarSearch", 3)
self._indent_stop_loss = self.Param("IndentStopLoss", 30)
self._fibo_entrance_level = self.Param("FiboEntranceLevel", 50.0)
self._fibo_profit_level = self.Param("FiboProfitLevel", 161.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._use_time_filter = self.Param("UseTimeFilter", False)
self._start_hour = self.Param("StartHour", 7)
self._stop_hour = self.Param("StopHour", 17)
self._pending_order = None
self._stop_price = 0.0
self._take_profit_price = 0.0
@property
def StepFast(self):
return self._step_fast.Value
@StepFast.setter
def StepFast(self, value):
self._step_fast.Value = value
@property
def MaximumFast(self):
return self._max_fast.Value
@MaximumFast.setter
def MaximumFast(self, value):
self._max_fast.Value = value
@property
def StepSlow(self):
return self._step_slow.Value
@StepSlow.setter
def StepSlow(self, value):
self._step_slow.Value = value
@property
def MaximumSlow(self):
return self._max_slow.Value
@MaximumSlow.setter
def MaximumSlow(self, value):
self._max_slow.Value = value
@property
def CountBarSearch(self):
return self._count_bar_search.Value
@CountBarSearch.setter
def CountBarSearch(self, value):
self._count_bar_search.Value = value
@property
def IndentStopLoss(self):
return self._indent_stop_loss.Value
@IndentStopLoss.setter
def IndentStopLoss(self, value):
self._indent_stop_loss.Value = value
@property
def FiboEntranceLevel(self):
return self._fibo_entrance_level.Value
@FiboEntranceLevel.setter
def FiboEntranceLevel(self, value):
self._fibo_entrance_level.Value = value
@property
def FiboProfitLevel(self):
return self._fibo_profit_level.Value
@FiboProfitLevel.setter
def FiboProfitLevel(self, value):
self._fibo_profit_level.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def UseTimeFilter(self):
return self._use_time_filter.Value
@UseTimeFilter.setter
def UseTimeFilter(self, value):
self._use_time_filter.Value = value
@property
def StartHour(self):
return self._start_hour.Value
@StartHour.setter
def StartHour(self, value):
self._start_hour.Value = value
@property
def StopHour(self):
return self._stop_hour.Value
@StopHour.setter
def StopHour(self, value):
self._stop_hour.Value = value
def OnStarted2(self, time):
super(fibo_i_sar_strategy, self).OnStarted2(time)
self._fast_sar = ParabolicSar()
self._fast_sar.Acceleration = self.StepFast
self._fast_sar.AccelerationMax = self.MaximumFast
self._slow_sar = ParabolicSar()
self._slow_sar.Acceleration = self.StepSlow
self._slow_sar.AccelerationMax = self.MaximumSlow
self._highest = Highest()
self._highest.Length = self.CountBarSearch
self._lowest = Lowest()
self._lowest.Length = self.CountBarSearch
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_sar, self._slow_sar, self._highest, self._lowest, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_sar, slow_sar, highest, lowest):
if candle.State != CandleStates.Finished:
return
hour = candle.CloseTime.Hour
if self.UseTimeFilter and (hour < self.StartHour or hour > self.StopHour):
self._cancel_pending_order()
return
price = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
fast_val = float(fast_sar)
slow_val = float(slow_sar)
high_val = float(highest)
low_val = float(lowest)
if self.Position > 0:
if price <= self._stop_price or price >= self._take_profit_price:
self.SellMarket()
elif self.Position < 0:
if price >= self._stop_price or price <= self._take_profit_price:
self.BuyMarket()
if self._pending_order is not None:
side = self._pending_order.Side
if side == Sides.Buy and (slow_val > fast_val or fast_val >= price):
self._cancel_pending_order()
elif side == Sides.Sell and (slow_val < fast_val or fast_val <= price):
self._cancel_pending_order()
if self._pending_order is not None or self.Position != 0:
return
range_high = high_val
range_low = low_val
rng = range_high - range_low
if slow_val < fast_val and fast_val < price:
entry = range_low + rng * (float(self.FiboEntranceLevel) / 100.0)
profit = range_low + rng * (float(self.FiboProfitLevel) / 100.0)
stop = range_low - float(self.IndentStopLoss) * step
self._stop_price = stop
self._take_profit_price = profit
self._pending_order = self.BuyLimit(entry)
elif slow_val > fast_val and fast_val > price:
entry = range_high - rng * (float(self.FiboEntranceLevel) / 100.0)
profit = range_high - rng * (float(self.FiboProfitLevel) / 100.0)
stop = range_high + float(self.IndentStopLoss) * step
self._stop_price = stop
self._take_profit_price = profit
self._pending_order = self.SellLimit(entry)
def OnOrderReceived(self, order):
super(fibo_i_sar_strategy, self).OnOrderReceived(order)
if self._pending_order is not None and order == self._pending_order:
if order.State != OrderStates.Active:
self._pending_order = None
def _cancel_pending_order(self):
if self._pending_order is not None:
if self._pending_order.State == OrderStates.Active:
self.CancelOrder(self._pending_order)
self._pending_order = None
def OnReseted(self):
super(fibo_i_sar_strategy, self).OnReseted()
self._pending_order = None
self._stop_price = 0.0
self._take_profit_price = 0.0
def CreateClone(self):
return fibo_i_sar_strategy()