Открыть на GitHub

Стратегия Fibonacci Potential Entries Retracement

Обзор

Fibonacci Potential Entries Retracement Strategy повторяет эксперт EA_PUB_FibonacciPotentialEntries для MetaTrader. Стратегия подписывается на стакан уровня 1, после получения положительных бид/аск размещает две отложенные заявки возле заданных уровней Фибоначчи. При достижении общего целевого уровня каждая позиция сокращается на 50%, а стоп-приказ переносится в безубыток для оставшегося объёма.

Соответствие оригиналу

  • Размещение заявок:
    • Первая заявка: лимит на уровне 50% (P50Level). В бычьем режиме стоп ставится на три спреда ниже уровня 61%, в медвежьем — на три спреда выше.
    • Вторая заявка: лимит на уровне 61% (P61Level) со стопом в трёх спредах от середины между уровнями 61% и 100%.
  • Направление – параметр bType преобразован в MarketBias (Bull для покупок, Bear для продаж).
  • Распределение риска – первая сделка всегда использует риск 0.7% от капитала, вторая берёт остаток max(RiskPercent - 0.7, 0) в точном соответствии с MQL-реализацией.
  • Расчёт объёма – риск переводится в объём через Portfolio.CurrentValue (с резервом на CurrentBalance и BeginValue) и характеристики инструмента: шаг цены, стоимость шага, мультипликатор.
  • Частичная фиксация – при пробое TargetLevel по каждой ноге отправляется рыночная заявка на закрытие половины объёма. После этого стоп перемещается на цену входа, что повторяет последовательность OrderClose + OrderModify оригинального эксперта.

Параметры

Название Описание
P50Level Цена уровня Фибоначчи 50%.
P61Level Цена уровня Фибоначчи 61.8%.
P100Level Цена уровня Фибоначчи 100% (используется при расчёте второго стопа).
TargetLevel Общий уровень фиксации прибыли.
RiskPercent Суммарный риск в процентах от капитала (не меньше 0.7).
MarketBias Режим торговли: Bull (покупки) или Bear (продажи).

Последовательность работы

  1. Выполняется подписка на SubscribeLevel1() и ожидание валидных котировок bid/ask.
  2. После расчёта спреда, стопов и объёмов заявки выставляются по одному разу за запуск, как и в MetaTrader.
  3. При исполнении заявок фиксируется средняя цена входа, размещается соответствующий стоп и ведётся учёт объёма по каждой ноге.
  4. При достижении TargetLevel по каждой ноге отправляется рыночный приказ на закрытие половины позиции, затем стоп переносится в точку входа.
  5. Стоп-заявки снимаются при закрытии объёма либо при остановке стратегии.

Замечания

  • Стоп-приказ переоформляется при каждом изменении объёма. Если биржа отклоняет заявку, проверьте права адаптера и специфику инструмента.
  • Отдельная заявка take-profit не используется – выход управляется в онлайне, что отражает поведение исходного советника.
  • Для повторной постановки отложенных заявок после изменения параметров требуется перезапуск стратегии, аналогично MetaTrader.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Fibonacci Retracement Entries strategy: EMA trend with Fibonacci retracement levels.
/// Buys on retracement to 61.8% in uptrend, sells on retracement to 38.2% in downtrend.
/// </summary>
public class FibonacciPotentialEntriesRetracementStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _lookback;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _high;
	private decimal _low;
	private int _barCount;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int Lookback { get => _lookback.Value; set => _lookback.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public FibonacciPotentialEntriesRetracementStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");
		_lookback = Param(nameof(Lookback), 50)
			.SetGreaterThanZero()
			.SetDisplay("Lookback", "Lookback for high/low", "General");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_high = 0;
		_low = decimal.MaxValue;
		_barCount = 0;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_high = 0;
		_low = decimal.MaxValue;
		_barCount = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (candle.HighPrice > _high) _high = candle.HighPrice;
		if (candle.LowPrice < _low) _low = candle.LowPrice;
		_barCount++;

		if (_barCount < 20) return;

		var range = _high - _low;
		if (range <= 0) return;

		var close = candle.ClosePrice;
		var fib618 = _high - range * 0.618m;
		var fib382 = _high - range * 0.382m;
		if (close > emaValue && close <= fib618 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			BuyMarket();
			_candlesSinceTrade = 0;
		}
		else if (close < emaValue && close >= fib382 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			SellMarket();
			_candlesSinceTrade = 0;
		}
	}
}