Открыть на GitHub

Стратегия Fibo Arc Momentum

Обзор

Стратегия является портом советника MetaTrader "FiboArc" (каталог MQL/24924) на платформу StockSharp. В оригинале использовались несколько фильтров импульса и пробой линий Фибоначчи. В версии для StockSharp сохранена та же идея, но реализована на высокоуровневом API свечей:

  • Две линейно-взвешенные скользящие средние (FastMaPeriod, SlowMaPeriod) задают направление тренда.
  • Индикатор Momentum проверяет удалённость от нейтрального уровня 100 и отсеивает слабые сигналы.
  • MACD подтверждает импульс и отслеживает свежие пересечения основной и сигнальной линий.
  • Упрощённая дуга Фибоначчи пересчитывается на каждом баре по ценам открытия двух опорных свечей, выбранных параметрами TrendAnchorLength и ArcAnchorLength. Пробой этого динамического уровня заменяет работу с объектом графика из версии для MetaTrader.

Расчёт выполняется только по закрытым свечам, что полностью соответствует поведению оригинального советника и исключает эффект подглядывания вперёд.

Индикаторы и поток данных

Подписка ведётся на один поток свечей, задаваемый параметром CandleType. Каждая новая завершённая свеча передаётся в индикаторы через SubscribeCandles(...).BindEx(...):

Индикатор Назначение Настройки по умолчанию
LinearWeightedMovingAverage (быстрая) Краткосрочный тренд, сигнал на вход FastMaPeriod = 6, типичная цена
LinearWeightedMovingAverage (медленная) Фильтр направления тренда SlowMaPeriod = 85, типичная цена
Momentum Подтверждение силы движения через отклонение от 100 MomentumPeriod = 14
MovingAverageConvergenceDivergenceSignal Подтверждение тренда и пересечения MacdFastPeriod = 12, MacdSlowPeriod = 26, MacdSignalPeriod = 9

Обрабатываются только финальные значения индикаторов, ручные вызовы GetValue() не используются и требования репозитория соблюдены.

Перестройка дуги Фибоначчи

В MetaTrader дуга рисовалась как объект и считывалась функцией ObjectGetValueByShift. В StockSharp графические объекты не нужны, поэтому дуга моделируется численно:

  1. Стратегия хранит скользящее окно завершённых свечей (_history).
  2. TrendAnchorLength задаёт индекс базовой точки, а ArcAnchorLength — второй опорной точки.
  3. Уровень дуги вычисляется как линейная интерполяция между ценами открытия опорных свечей с коэффициентом FibonacciRatio (по умолчанию 0.618).
  4. Для фиксации пробоя сравниваются открытие предыдущей свечи с прежним уровнем дуги и открытие текущей свечи с новым уровнем. Пересечение снизу вверх (fibCrossUp) или сверху вниз (fibCrossDown) воспроизводит логику исходного советника.

Торговые правила

Вход в покупки

Покупка выполняется, когда соблюдены все условия:

  1. Фиксируется пробой дуги Фибоначчи вверх (fibCrossUp).
  2. Быстрая LWMA выше медленной (bullishTrend).
  3. Модуль отклонения Momentum от 100 не меньше MomentumThreshold.
  4. Линия MACD выше сигнальной либо произошло пересечение вверх.
  5. Отсутствует открытая длинная позиция (позиция ≤ 0).

Объём заявки — значение Volume плюс величина открытого короткого объёма, чтобы стратегия могла перевернуться из шорта в лонг.

Вход в продажи

Логика полностью симметрична: fibCrossDown, быстрая LWMA ниже медленной, Momentum превышает порог, MACD ниже сигнальной линии.

Выходы

Позиция закрывается при возникновении любого из событий:

  • Изменение условий тренда или MACD в противоположную сторону.
  • Появление обратного сигнала пробоя дуги Фибоначчи.
  • Срабатывание адаптивного стоп-лосса или тейк-профита.

Закрытие всегда выполняется рыночными заявками, как и в версии MetaTrader.

Управление рисками

Сохранены ключевые элементы оригинального советника:

  • StopLossDistance и TakeProfitDistance задают фиксированные расстояния в ценовых единицах.
  • EnableBreakEven, BreakEvenTrigger, BreakEvenOffset управляют переводом стопа в безубыток.
  • EnableTrailing, TrailingTrigger, TrailingDistance включают свечной трейлинг.

Текущие уровни стопа и цели рассчитываются внутри стратегии и обновляются на каждом баре. Метод OnNewMyTrade пересчитывает уровни после фактического исполнения заявки.

Параметры

Параметр Описание
CandleType Таймфрейм и тип свечей для расчёта.
FastMaPeriod, SlowMaPeriod Периоды быстрых и медленных LWMA.
MomentumPeriod, MomentumThreshold Настройки индикатора Momentum.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod Параметры MACD.
TrendAnchorLength, ArcAnchorLength, FibonacciRatio Контроль реконструкции дуги Фибоначчи.
StopLossDistance, TakeProfitDistance Первичные уровни стопа и цели (в абсолютных ценовых величинах).
EnableBreakEven, BreakEvenTrigger, BreakEvenOffset Перевод в безубыток.
EnableTrailing, TrailingTrigger, TrailingDistance Настройки трейлинг-стопа.

Все параметры объявлены через StrategyParam<T> и при необходимости поддерживают оптимизацию. Значения по умолчанию повторяют оригинальный эксперт.

Использование

  1. Привяжите стратегию к нужному инструменту и установите Volume в соответствии с размером позиции.
  2. При необходимости скорректируйте таймфрейм, периоды скользящих и параметры дуги Фибоначчи под целевой рынок.
  3. Запустите стратегию. Все решения принимаются только по закрытым свечам.
  4. Для наглядности можно включить встроенные графики быстрых/медленных LWMA и MACD (если среда исполнения их поддерживает).

Версия на Python не создаётся согласно требованиям задачи. Тестовый набор проекта не изменён.

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 FiboArcMomentumStrategy : 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 FiboArcMomentumStrategy()
	{
		_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;
	}
}