Открыть на GitHub

Стратегия Fibonacci Time Zones

Описание

Стратегия представляет собой портирование советника MetaTrader "Fibonacci Time Zones" на платформу StockSharp. Основная идея сохранена: фильтрация сигналов по старшему таймфрейму MACD, выход по полосам Боллинджера и многоуровневое управление позицией. Логика реализована на высокоуровневом API — индикаторы подключаются через Bind/BindEx, а данные поступают из двух подписок на свечи.

Алгоритм работы

  1. MACD-фильтр. Для выбранного медленного таймфрейма вычисляется MACD (12/26/9). Пересечение основной линии выше сигнальной формирует намерение открыть длинную позицию, пересечение вниз — короткую. Открытие происходит на следующей свечке рабочего таймфрейма, чтобы не дублировать заявки.
  2. Открытие сделок. При появлении сигнала отправляется заданное количество рыночных заявок. Если уже есть позиция противоположного направления, она закрывается перед открытием новой.
  3. Закрытие позиции. Используется несколько правил:
    • Полосы Боллинджера: для длинных позиций выход при достижении верхней полосы, для коротких — нижней.
    • Классические стопы: фиксированные уровни стоп-лосса, тейк-профита и трейлинг-стопа в пунктах автоматически переводятся в цену и передаются в StartProtection.
    • Перевод в безубыток: после прохождения указанного количества пунктов стоп переносится на цену входа плюс отступ.
    • Денежный трейлинг: суммарная (реализованная + плавающая) прибыль отслеживается, при достижении порога включается защита прибыли с допустимым откатом.
    • Целевые значения по прибыли: по желанию можно включить закрытие всех позиций при достижении абсолютной или процентной цели по счету.

Настройки

  • UseTakeProfitMoney, TakeProfitMoney — закрыть позицию при достижении заданной денежной прибыли.
  • UseTakeProfitPercent, TakeProfitPercent — аналогичная цель, но в процентах от начального капитала.
  • EnableTrailingProfit, TrailingTakeProfitMoney, TrailingStopLossMoney — параметры денежного трейлинг-стопа.
  • UseStop, StopLossPips, TakeProfitPips, TrailingStopPips — основные стоповые параметры в пунктах.
  • UseMoveToBreakEven, WhenToMoveToBreakEven, PipsToMoveStopLoss — контроль перевода в безубыток.
  • NumberOfTrades — сколько заявок отправлять на каждый сигнал (порт повторяет возможность наращивать позицию несколькими ордерами).
  • CandleType, MacdCandleType — рабочий таймфрейм и таймфрейм фильтра MACD.

Отличия от оригинала

  • Кнопки на графике и построение самих "Fibonacci Time Zones" не реализованы; стратегия ориентирована на автоматическое исполнение.
  • В оригинале вход осуществлялся вручную нажатием кнопок, здесь — автоматически по пересечениям MACD.
  • Функции управления счетом переписаны под Portfolio и PnL StockSharp.

Рекомендации по использованию

  1. Перед запуском выберите подходящие типы свечей. По умолчанию используется 15-минутный рабочий таймфрейм и месячный фильтр MACD.
  2. Настройте значения в пунктах в соответствии с шагом цены инструмента — стратегия автоматически переводит их в абсолютные цены.
  3. Если требуется больше гибкости, можно отключить денежные цели и оставить только выход по полосам Боллинджера.
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;

public class FibonacciTimeZonesStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public FibonacciTimeZonesStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}