Открыть на GitHub

Стратегия SilverTrend ColorJFatl Digit

Общее описание

Стратегия SilverTrend ColorJFatl Digit объединяет две популярные MQL-системы в одном высокоуровневом алгоритме StockSharp. Блок SilverTrend измеряет импульс выхода цены из короткого канального диапазона, а блок ColorJFatl Digit сглаживает выбранную цену с помощью Юрьевского скользящего среднего (Jurik Moving Average) и анализирует знак его наклона. Сделки совершаются только тогда, когда оба блока синхронно подтверждают одно направление; при расхождении сигналов позиция закрывается.

Реализация следует рекомендациям AGENTS.md: используется подписка на свечи, индикаторы подключаются через Bind, накопительные массивы заменены компактными очередями, а в коде присутствуют подробные английские комментарии.

Логика работы

1. Детектор пробоя SilverTrend

  • Используются индикаторы Highest и Lowest с окном SilverTrendLength + 1, формирующие актуальный ценовой канал.
  • Параметр SilverTrendRisk сжимает границы канала (формула оригинала 33 - risk): чем больше значение, тем ближе пороги к середине диапазона и тем раньше фиксируется разворот.
  • При закрытии свечи выше верхнего порога блок генерирует бычий тренд (+1), ниже нижнего порога — медвежий (-1).
  • Параметр SilverTrendSignalBar задаёт задержку в количестве полностью сформированных свечей перед подтверждением смены цвета индикатора.

2. Фильтр подтверждения ColorJFatl Digit

  • JurikMovingAverage сглаживает цену, выбранную параметром JmaPriceType. Поддерживаются все варианты applied price из MetaTrader (close, open, median, typical, weighted, simple, quarter, TrendFollow0/1, Demark).
  • Результат JMA округляется до JmaRoundDigits знаков — так воспроизводится «дискретизация» оригинального индикатора.
  • Знак наклона округлённого JMA задаёт направление: положительный наклон -> +1, отрицательный -> -1. При нулевом наклоне сохраняется предыдущий знак, что снижает дёрганость.
  • Параметр JmaSignalBar добавляет задержку, требуя чтобы новый наклон удержался нужное число свечей.

3. Исполнение сделок

  • Входы:
    • Открытие лонга — когда оба блока дают +1, а текущая позиция не длинная.
    • Открытие шорта — когда оба блока дают -1, а текущая позиция не короткая.
  • Выходы:
    • Немедленное закрытие позиции при расхождении сигналов или появлении нейтрального состояния (0).
    • Перед разворотом стратегия сначала закрывает встречную позицию, затем открывает новую — без усреднений и пирамидинга.
  • Активные заявки отменяются перед разворотом, чтобы не оставлять «хвостов» в стакане.

Параметры

Имя Описание
SilverTrendCandleType Таймфрейм свечей для расчёта канала SilverTrend (по умолчанию аналог H4).
SilverTrendLength Длина окна канала (SSP в исходном советнике).
SilverTrendRisk Риск-параметр, сжимающий границы канала (33 - risk).
SilverTrendSignalBar Количество закрытых свечей, которое нужно дождаться перед подтверждением смены цвета SilverTrend.
ColorJfatlCandleType Таймфрейм свечей для расчёта Jurik (может отличаться от SilverTrend).
JmaLength Длина Jurik Moving Average.
JmaSignalBar Задержка (в свечах) перед использованием нового наклона Jurik.
JmaPriceType Тип цены, подаваемой на вход Jurik (все варианты applied price).
JmaRoundDigits Количество знаков для округления результата Jurik.

Особенности реализации

  • Для задержки сигналов применяются короткие очереди FIFO — они экономят память и повторяют логику MQL без обращения к историческим буферам.
  • Индикаторы не опрашиваются вручную. Всё вычисление идёт через высокоуровневый Bind, что упрощает поддержку и тестирование.
  • В коде оставлены английские комментарии, поясняющие ключевые шаги: перерасчёт порогов, работу очередей, условия выхода и т.п.
  • Если доступен график, стратегия рисует свечи, линии канала и совершённые сделки для визуального контроля.

Практические рекомендации

  1. Рынки и таймфрейм. Оригинальная система создавалась для форекса на H4. Хорошо работают криптовалюты и сырьевые фьючерсы с отчётливыми свингами. Для более быстрых данных уменьшайте SilverTrendLength и JmaLength аккуратно.
  2. Оптимизация. Оптимизируйте параметры пробоя и фильтра одновременно — асимметричное изменение приводит к частым конфликтам сигналов.
  3. Applied price. Экспериментируйте с режимами TrendFollow и Demark, особенно на свечах Хейкен-Аши и Ренко — они лучше сглаживают шум.
  4. Риск-менеджмент. Внутренние выходы защищают от большинства ложных пробоев, но резкие движения всё же могут выйти за пределы канала. Используйте дополнительные портфельные стопы или StartProtection.
  5. Управление объёмом. Размер позиции задаётся свойством Strategy.Volume. При необходимости подключайте внешние модули управления капиталом (например, процент от капитала или ступенчатое уменьшение).

Идеи для развития

  • Добавить StartProtection со стоп-лоссом/тейк-профитом на основе ATR после статистического анализа.
  • Использовать более высокий таймфрейм (день/неделя) для блока Jurik в качестве трендового фильтра.
  • Расширить стратегию фильтрами по объёму или сантименту, чтобы сократить количество сделок в боковике.
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;

/// <summary>
/// SilverTrend ColorJFatl Digit strategy (simplified). Uses Highest/Lowest channel
/// breakout combined with EMA slope for trend confirmation.
/// </summary>
public class SilverTrendColorJFatlDigitStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _channelLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _riskLevel;

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

	public int ChannelLength
	{
		get => _channelLength.Value;
		set => _channelLength.Value = value;
	}

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

	public int RiskLevel
	{
		get => _riskLevel.Value;
		set => _riskLevel.Value = value;
	}

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

		_channelLength = Param(nameof(ChannelLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Channel Length", "Highest/Lowest lookback", "Indicators");

		_emaLength = Param(nameof(EmaLength), 10)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "EMA period for trend confirmation", "Indicators");

		_riskLevel = Param(nameof(RiskLevel), 3)
			.SetDisplay("Risk Level", "Channel threshold tightness", "Logic");
	}

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

		var highest = new Highest { Length = ChannelLength + 1 };
		var lowest = new Lowest { Length = ChannelLength + 1 };

		var lastTrend = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, (ICandleMessage candle, decimal highVal, decimal lowVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var range = highVal - lowVal;
				if (range <= 0)
					return;

				var riskModifier = 33m - RiskLevel;
				if (riskModifier < 0m) riskModifier = 0m;
				if (riskModifier > 33m) riskModifier = 33m;

				var thresholdPercent = riskModifier / 100m;
				var lowerThreshold = lowVal + range * thresholdPercent;
				var upperThreshold = highVal - range * thresholdPercent;

				var close = candle.ClosePrice;

				// SilverTrend breakout logic
				if (close < lowerThreshold)
					lastTrend = -1;
				else if (close > upperThreshold)
					lastTrend = 1;

				// Simple EMA slope confirmation using close vs channel midpoint
				var midpoint = (highVal + lowVal) / 2m;
				var emaConfirmUp = close > midpoint;
				var emaConfirmDown = close < midpoint;

				if (lastTrend > 0 && emaConfirmUp && Position <= 0)
					BuyMarket();
				else if (lastTrend < 0 && emaConfirmDown && Position >= 0)
					SellMarket();
			})
			.Start();

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