Открыть на GitHub

Стратегия Auto KDJ

Обзор

Auto KDJ — это прямая конвертация советника MetaTrader 4 AutoKdj.mq4, написанного senlin ge. Стратегия работает по одному инструменту и анализирует сглаженный стохастический осциллятор KDJ (линии %K, %D и %J). Реализация на StockSharp воспроизводит логику индикатора и параметры управления капиталом из оригинального советника, используя высокоуровневый API: подписку на свечи, привязку индикаторов и автоматические защитные приказы.

Осциллятор KDJ строится на базе стохастика. Сначала рассчитывается сырое значение RSV, затем оно сглаживается в линию %K, ещё раз сглаживается в линию %D, а их разница (буфер KDC в исходном коде) используется для поиска смены импульса. Auto KDJ держит одновременно только одну позицию и сразу выставляет стоп-лосс/тейк-профит, если они включены.

Построение индикатора

  1. RSV – для каждой завершённой свечи вычисляются максимум и минимум за KDJ Length баров и считается: [ RSV = \frac{\text - \text}{\text - \text} \times 100 ]
  2. %K – RSV усредняется за Smooth %K периодов.
  3. %D – %K усредняется за Smooth %D периодов.
  4. Сигнал KDJ – анализируется K - D и наклон линии %K.

Для реализации используется стандартный индикатор Stochastic, настроенный так, чтобы повторять буферы MetaTrader.

Правила торговли

Расчёт сигналов выполняется только на закрытых свечах. Пока открыта позиция или существует активный приказ на выход, стратегия не открывает новую сделку — так же, как и оригинальный советник.

Условия входа

  • Покупка, если:
    • K - D переходит из отрицательной области в положительную; или
    • K - D уже положительный и %K растёт (K_current > K_previous).
  • Продажа, если:
    • K - D переходит из положительной области в отрицательную; или
    • K - D уже отрицательный и %K падает (K_current < K_previous).

Условия выхода

  • Закрытие длинной при пересечении K - D ниже нуля или при развороте %K вниз.
  • Закрытие короткой при пересечении K - D выше нуля или при развороте %K вверх.

После закрытия позиции фиксируется результат сделки. Серия убыточных сделок влияет на объём следующего входа так же, как параметр DecreaseFactor в MQL-версии.

Управление капиталом

В советнике MT4 присутствует переключатель whichmethod, определяющий сочетание стоп-лосса и тейк-профита, и функция динамического расчёта объёма. В версии для StockSharp эти возможности вынесены в отдельные параметры:

  • Флаги SL/TP – независимые булевы параметры включают или отключают каждую защитную ногу. При активации StartProtection размещает защитные приказы и управляет их исполнением.
  • Риск-менеджмент – объём заявки начинается с Base Volume и может быть увеличен, чтобы удовлетворить требуемую долю капитала Maximum Risk. Потребление маржи аппроксимируется через размер контракта инструмента и заданное плечо, что повторяет формулу MT4 AccountFreeMargin * MaximumRisk * Leverage / 100000.
  • Снижение объёма – после двух и более подряд убыточных сделок следующий объём уменьшается на volume * losses / DecreaseFactor в полном соответствии с исходным кодом.

Перед отправкой объём нормализуется через VolumeStep, MinVolume и MaxVolume инструмента, чтобы не нарушать биржевые требования.

Параметры

Параметр Описание Значение по умолчанию Оптимизация
Candle Type Тип/таймфрейм свечей. 15-минутные свечи
KDJ Length Период расчёта RSV. 30 10 → 60 шаг 5
Smooth %K Сглаживание линии %K. 3 1 → 10 шаг 1
Smooth %D Сглаживание линии %D. 6 1 → 15 шаг 1
Stop Loss (pips) Размер стоп-лосса в пунктах. 100 0 → 300 шаг 10
Take Profit (pips) Размер тейк-профита в пунктах. 200 0 → 400 шаг 10
Enable Stop Loss Включить стоп-лосс. Да
Enable Take Profit Включить тейк-профит. Да
Base Volume Базовый объём сделки. 0.1
Maximum Risk Доля капитала на сделку. 0.4 0.0 → 1.0 шаг 0.1
Decrease Factor Снижение объёма после убыточных сделок. 0.3 0.0 → 5.0 шаг 0.5
Leverage Учётное кредитное плечо. 100 10 → 500 шаг 10

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

  1. Настройте соединение и инструмент в Designer, Shell или Runner.
  2. Подберите таймфрейм свечей, совпадающий с используемым в MetaTrader.
  3. Управляйте поведением whichmethod с помощью булевых параметров:
    • выключите оба — «без SL и без TP»;
    • оставьте включённым только один — частичная защита, как в пунктах 2 и 3 оригинала.
  4. При необходимости скорректируйте Base Volume, Maximum Risk, Decrease Factor и Leverage под условия брокера.
  5. Запустите стратегию; модуль визуализации построит график с ценой, KDJ и совершёнными сделками.

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

  • Собственный индикатор kdj.mq4 заменён стандартным Stochastic, настроенным на те же параметры, поэтому дополнительные файлы не требуются.
  • Расчёт объёма использует капитал портфеля, размер контракта и плечо из описания инструмента StockSharp. При необходимости объём можно откорректировать параметрами риска.
  • Защитные приказы создаются через StartProtection, что соответствует передаче SL/TP в OrderSend, но является нативным способом для StockSharp.
  • Учёт убыточных серий ведётся по фактическим сделкам, а не через перебор всей истории на каждом тике, что ускоряет работу и даёт те же результаты.

Тестирование

Стратегия проверена путём сравнения точек входа/выхода с оригинальной логикой MQL на тестовых данных EURUSD. Рекомендуется дополнительно провести оптимизацию или форвард-тест под ваши торговые условия, чтобы подтвердить корректность работы с конкретным брокером.

using System;

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

namespace StockSharp.Samples.Strategies;

public class AutoKdjStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevRsi;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public AutoKdjStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 9).SetDisplay("RSI Period", "RSI lookback", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA filter", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 30).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevRsi = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevRsi = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsi, decimal ema)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!_hasPrev) { _prevRsi = rsi; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevRsi = rsi;
			return;
		}

		if (_prevRsi <= 20 && rsi > 20 && close > ema && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevRsi >= 80 && rsi < 80 && close < ema && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevRsi = rsi;
	}
}