Fibo iSAR Strategy
Стратегия сочетает быстрый и медленный индикаторы Parabolic SAR с уровнями Фибоначчи. Когда быстрый SAR находится выше медленного и ниже цены, предполагается восходящий тренд и выставляется отложенный ордер Buy Limit на уровне 50% коррекции Фибоначчи от последнего диапазона. Стоп‑лосс ставится ниже локального минимума, тейк‑профит — на уровне 161% расширения. Для нисходящего тренда логика зеркальная с ордерами Sell Limit.
Детали
- Условия входа: Направление тренда по быстрым/медленным SAR; вход по 50% коррекции Фибоначчи.
- Длинные/короткие: Обе стороны.
- Условия выхода: Стоп‑лосс или тейк‑профит.
- Стопы: Да.
- Значения по умолчанию:
StepFast= 0.02MaximumFast= 0.2StepSlow= 0.01MaximumSlow= 0.1CountBarSearch= 3IndentStopLoss= 30FiboEntranceLevel= 50FiboProfitLevel= 161CandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Тренд
- Направление: Обе
- Индикаторы: Parabolic SAR, Фибоначчи
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной (5м)
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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()