Открыть на GitHub

Стратегия ComFracti

Обзор

ComFracti — портированная с MT4 стратегия "ComFracti". Алгоритм совмещает подтверждение фракталами на двух таймфреймах с фильтрами RSI и стохастика, а также предлагает дополнительные фильтры: наклон EMA, параболический SAR, канал и перцептрон. Реализация на C# использует высокоуровневый API StockSharp, анализирует только завершённые свечи и в каждый момент времени удерживает одну позицию.

Логика торговли

  • Основной сигнал
    • Лонг подтверждается, если фрактальный сигнал вверх присутствует и на рабочем, и на старшем таймфрейме.
    • Шорт подтверждается, если оба таймфрейма формируют нисходящий фрактал.
    • При включённом фильтре RSI трёхпериодный RSI на старшем таймфрейме должен быть ниже 50 - RsiLevelBuy для лонга или выше 50 + RsiLevelSell для шорта.
    • При включённом фильтре стохастика %K (период 5, сглаживание 3/3) должен быть ниже 50 - StochasticLevelBuy для лонга или выше 50 + StochasticLevelSell для шорта.
  • Дополнительные фильтры
    • Наклон EMA: EMA на фильтровом таймфрейме должна расти для лонга и снижаться для шорта.
    • Parabolic SAR: значение SAR должно располагаться ниже цены открытия (лонг) или выше (шорт).
    • Канальный фильтр: сравнивает предыдущую свечу с адаптивным каналом; минимум прошлой свечи должен быть выше нижней границы (лонг), максимум — ниже верхней (шорт).
    • Перцептрон: взвешенная сумма разностей максимумов и минимумов последних баров должна быть положительной (лонг) или отрицательной (шорт).
  • Управление позицией
    • Одновременно удерживается только одна позиция; при появлении сигнала противоположного направления стратегия сначала закрывает текущую позицию.
    • Стоп-лосс и тейк-профит задаются в пунктах инструмента.
    • Опциональный трейлинг-стоп двигается вслед за ценой после достижения заданного буфера (ProfitTrailing = true).
    • При включённом CloseOnOppositeSignal позиция закрывается при появлении противоположного основного сигнала.

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

  • Базовый объём задаётся параметром BaseVolume (по умолчанию 0.1 лота). При активном AccountMicro объём делится на 10.
  • Если UseMoneyManagement включён, стратегия рассчитывает объём исходя из процента риска RiskPercent, текущего расстояния стоп-лосса и стоимости шага цены. Полученный объём ограничивается параметром MinimumVolume.

Параметры

Параметр Описание
TakeProfitPoints, StopLossPoints Расстояние до тейк-профита и стоп-лосса в пунктах.
UseTrailingStop, TrailingStopPoints, ProfitTrailing Настройки трейлинг-стопа (дистанция и требование предварительной прибыли).
BaseVolume, UseMoneyManagement, RiskPercent, AccountMicro, MinimumVolume Управление объёмом позиции.
UseFractals, FractalShift* Включение фрактального фильтра и смещения анализируемых баров.
UseRsi, RsiLevelBuy, RsiLevelSell, RsiType Настройки фильтра RSI и таймфрейм расчёта.
UseStochastic, StochasticPeriod*, StochasticLevel* Настройки стохастика и порогов.
UseMaFilter, MaPeriod Фильтр EMA на выбранном таймфрейме.
UsePsarFilter, PsarStep Фильтр Parabolic SAR.
UseChannelFilter, ChannelLookback, ChannelK Параметры канального фильтра.
UsePerceptronFilter, PerceptronV1PerceptronV4 Весовые коэффициенты перцептронного фильтра (0–100, смещены относительно 50).
CandleType, HigherFractalType, FilterType Используемые таймфреймы данных.

Примечания

  • Стратегия работает со свечами закрытия, поэтому поведение может немного отличаться от оригинального тикового советника MT4.
  • Реализованный трекер фракталов воспроизводит пятибарный алгоритм MT4 и поддерживает настройку смещений (sh1/sh2).
  • Деньговое управление опирается на стоимость портфеля, доступную в StockSharp; при отсутствии оценки стратегия возвращается к фиксированному объёму.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// ComFracti: Fractal detection with RSI confirmation and ATR stops.
/// </summary>
public class ComFractiStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _entryPrice;
	private decimal _h1, _h2, _h3, _h4, _h5;
	private decimal _l1, _l2, _l3, _l4, _l5;
	private int _barCount;

	public ComFractiStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_emaLength = Param(nameof(EmaLength), 50)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_entryPrice = 0;
		_barCount = 0;
		_h1 = _h2 = _h3 = _h4 = _h5 = 0;
		_l1 = _l2 = _l3 = _l4 = _l5 = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_entryPrice = 0;
		_barCount = 0;
		_h1 = _h2 = _h3 = _h4 = _h5 = 0;
		_l1 = _l2 = _l3 = _l4 = _l5 = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ema, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);
		}
	}

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

		_h5 = _h4; _h4 = _h3; _h3 = _h2; _h2 = _h1;
		_h1 = candle.HighPrice;
		_l5 = _l4; _l4 = _l3; _l3 = _l2; _l2 = _l1;
		_l1 = candle.LowPrice;
		_barCount++;

		if (_barCount < 5 || atrVal <= 0)
			return;

		var close = candle.ClosePrice;

		var fractalUp = _h3 > _h1 && _h3 > _h2 && _h3 > _h4 && _h3 > _h5;
		var fractalDown = _l3 < _l1 && _l3 < _l2 && _l3 < _l4 && _l3 < _l5;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || (fractalUp && rsiVal > 65))
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || (fractalDown && rsiVal < 35))
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fractalDown && rsiVal < 55)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fractalUp && rsiVal > 45)
			{
				_entryPrice = close;
				SellMarket();
			}
		}
	}
}