Стратегия полностью повторяет логику оригинального советника EA_PUB_FibonacciPotentialEntries. Она выставляет две отложенные заявки на уровнях отката Фибо 50% и 61% и управляет ими через высокоуровневый API StockSharp.
Логика торговли
Начальная постановка
Как только становятся доступны котировки bid/ask, стратегия вычисляет текущий спред и отправляет две лимитные заявки:
Заявка №1: по уровню 50% со стопом ниже (или выше для шортов) уровня 61%.
Заявка №2: по уровню 61% со стопом, расположеным посередине к уровню 100%.
Объёмы рассчитываются так, чтобы первая сделка рисковала 0.7% капитала, а вторая — остатком параметра RiskPercent.
Обработка цели
Когда цена достигает уровня TargetPrice, стратегия закрывает половину объёма каждой исполненной позиции рыночными ордерами.
После частичной фиксации остаток защищается на уровне безубытка. Если рынок возвращается к цене входа, остаток закрывается автоматически.
Направление
IsBullish = true — постановка покупок (базовый вариант из MQL).
IsBullish = false — зеркальное поведение с продажами и инвертированными проверками стоп/тейк.
Параметры
Имя
Описание
PriceOn50Level
Уровень цены для первой лимитной заявки.
PriceOn61Level
Уровень цены для второй лимитной заявки.
PriceOn100Level
Референс для расчёта стопа второй сделки.
TargetPrice
Общая цель по прибыли для обеих позиций.
RiskPercent
Общий процент капитала под риском в обоих входах.
IsBullish
Выбор между длинным и коротким сценарием.
Особенности конвертации
Используются только высокоуровневые методы (SubscribeLevel1, BuyLimit, SellLimit, BuyMarket, SellMarket), что соответствует правилам репозитория.
Частичные закрытия и перевод в безубыток реализованы через рыночные заявки, без низкоуровневых модификаций, как и в исходном MQL-роботе.
Объёмы приводятся к шагу объёма инструмента, чтобы соответствовать требованиям StockSharp.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Fibonacci Potential Entries strategy: Two-candle reversal with RSI filter.
/// Uses price swing highs/lows as fibonacci reference points.
/// </summary>
public class FibonacciPotentialEntriesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _highestHigh;
private decimal _lowestLow;
private decimal _prevClose;
private int _barCount;
private int _candlesSinceTrade;
private bool _hasPrevClose;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public FibonacciPotentialEntriesStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highestHigh = 0;
_lowestLow = decimal.MaxValue;
_prevClose = 0;
_barCount = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrevClose = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highestHigh = 0;
_lowestLow = decimal.MaxValue;
_prevClose = 0;
_barCount = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrevClose = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (candle.HighPrice > _highestHigh) _highestHigh = candle.HighPrice;
if (candle.LowPrice < _lowestLow) _lowestLow = candle.LowPrice;
_barCount++;
if (_barCount < 20) return;
var range = _highestHigh - _lowestLow;
if (range <= 0) return;
var fib382 = _highestHigh - range * 0.382m;
var fib618 = _highestHigh - range * 0.618m;
var close = candle.ClosePrice;
var crossedIntoBuyZone = _hasPrevClose && _prevClose > fib618 && close <= fib618;
var crossedIntoSellZone = _hasPrevClose && _prevClose < fib382 && close >= fib382;
if (crossedIntoBuyZone && rsiValue < 40 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (crossedIntoSellZone && rsiValue > 60 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
_prevClose = close;
_hasPrevClose = true;
}
}
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
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class fibonacci_potential_entries_strategy(Strategy):
def __init__(self):
super(fibonacci_potential_entries_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._highest_high = 0.0
self._lowest_low = float('inf')
self._prev_close = 0.0
self._bar_count = 0
self._candles_since_trade = 6
self._has_prev_close = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(fibonacci_potential_entries_strategy, self).OnReseted()
self._highest_high = 0.0
self._lowest_low = float('inf')
self._prev_close = 0.0
self._bar_count = 0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev_close = False
def OnStarted2(self, time):
super(fibonacci_potential_entries_strategy, self).OnStarted2(time)
self._highest_high = 0.0
self._lowest_low = float('inf')
self._prev_close = 0.0
self._bar_count = 0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev_close = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
rsi_val = float(rsi_value)
if high > self._highest_high:
self._highest_high = high
if low < self._lowest_low:
self._lowest_low = low
self._bar_count += 1
if self._bar_count < 20:
self._prev_close = close
self._has_prev_close = True
return
range_val = self._highest_high - self._lowest_low
if range_val <= 0:
self._prev_close = close
self._has_prev_close = True
return
fib382 = self._highest_high - range_val * 0.382
fib618 = self._highest_high - range_val * 0.618
crossed_into_buy_zone = self._has_prev_close and self._prev_close > fib618 and close <= fib618
crossed_into_sell_zone = self._has_prev_close and self._prev_close < fib382 and close >= fib382
if crossed_into_buy_zone and rsi_val < 40 and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif crossed_into_sell_zone and rsi_val > 60 and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_close = close
self._has_prev_close = True
def CreateClone(self):
return fibonacci_potential_entries_strategy()