Открыть на GitHub

Стратегия Wajdyss Ichimoku Candle MMRec

Обзор

Стратегия представляет собой перенос советника Exp_wajdyss_Ichimoku_Candle_MMRec с платформы MetaTrader. В StockSharp она заново вычисляет базовую линию Ишимоку (Kijun) с помощью индикаторов Highest и Lowest и окрашивает каждую закрытую свечу в один из четырех состояний. Когда предыдущая свеча находилась выше Kijun, а сигнальная свеча опускается ниже линии, алгоритм закрывает короткие позиции и открывает длинную. Обратный переход формирует вход в шорт. Модуль MMRec уменьшает торговый объем после заданного количества убыточных сделок в одном направлении, полностью воспроизводя логику оригинального советника.

Переведенная версия использует высокоуровневый API StockSharp. Исторические и онлайн данные подключаются через SubscribeCandles, решения принимаются только по завершенным свечам, что обеспечивает одинаковое поведение в тестере и в реальном времени.

Логика окраски свечей

Каждая закрытая свеча получает числовой код, совпадающий с индикатором wajdyss Ichimoku Candle:

Цвет Условие Интерпретация
0 Закрытие ниже Kijun и медвежье тело Сильный медвежий импульс ниже базовой линии
1 Закрытие ниже Kijun, но бычье тело Слабый отскок под линией
2 Закрытие выше Kijun, но медвежье тело Слабое давление продавцов над линией
3 Закрытие выше Kijun и бычье тело Сильное бычье продолжение над линией

Торговые сигналы

Сигналы формируются по двум последним закрытым свечам:

  • Вход в лонг: свеча на позиции SignalBarShift + 1 имела цвет больше 1 (цена была выше Kijun), а свеча SignalBarShift окрасилась в цвет ниже 2 (цена ушла ниже Kijun). Стратегия при необходимости закрывает короткую позицию и может открыть новую длинную.
  • Вход в шорт: свеча на позиции SignalBarShift + 1 имела цвет ниже 2, а свеча SignalBarShift получила цвет выше 1. В этом случае закрываются лонги и открывается короткая позиция (если направление разрешено).

Параметр SignalBarShift соответствует входу SignalBar в оригинале. Значение 1 означает работу с двумя последними полностью закрытыми свечами; увеличение сдвига задерживает входы на указанное количество баров.

Управление объемом

Блок MMRec ведет короткую историю результатов по каждому направлению. Если последние LossTriggerCount сделок по лонгам (или шортам) оказались убыточными, объем заявок уменьшается до ReducedVolume. После прибыльной сделки или при отсутствии необходимого количества истории стратегия возвращается к основному объему NormalVolume. Это повторяет функции BuyTradeMMRecounter и SellTradeMMRecounter из библиотеки MQL5.

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

Стоп-лосс и тейк-профит задаются в шагах цены. Для длинной позиции проверяется достижение уровней вход - StopLossPoints * PriceStep и вход + TakeProfitPoints * PriceStep. Для короткой позиции логика зеркальна. Контроль выполняется один раз на закрытии свечи, что соответствует работе исходного советника с фиксированными ордерами на сервере.

Параметры

Параметр Описание Значение по умолчанию
CandleType Таймфрейм свечей Свечи 1H
KijunLength Длина окна для линии Kijun 26
SignalBarShift Сдвиг сигнальной свечи относительно текущей 1
BuyPosOpen / SellPosOpen Разрешение на открытие позиций в каждом направлении true
BuyPosClose / SellPosClose Разрешение закрывать позиции по обратному сигналу true
NormalVolume Базовый объем сделки 1
ReducedVolume Объем после серии убытков 0.1
LossTriggerCount Количество убытков подряд для снижения объема 2
StopLossPoints Стоп-лосс в шагах цены (0 — отключен) 1000
TakeProfitPoints Тейк-профит в шагах цены (0 — отключен) 2000

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

  • Входы совершаются только по изменению цвета свечи; можно отключить любое направление через параметры.
  • Для корректной работы модуля MMRec необходимы данные о результатах сделок, которые стратегия генерирует автоматически при закрытии позиций.
  • Если у инструмента не задан шаг цены, стоп и тейк-профит игнорируются.
  • Значение SignalBarShift = 0 дает более ранние сигналы, но повышает вероятность ложных входов.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Ichimoku-inspired strategy using Highest/Lowest midline (Kijun-Sen concept).
/// Trades when price crosses above/below the midline.
/// </summary>
public class WajdyssIchimokuCandleMmrecStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;

	private decimal? _prevMid;
	private decimal? _prevClose;

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

	public int Period
	{
		get => _period.Value;
		set => _period.Value = value;
	}

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

		_period = Param(nameof(Period), 26)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Kijun-Sen lookback period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevMid = null;
		_prevClose = null;
	}

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

		_prevMid = null;
		_prevClose = null;

		var middle = new SimpleMovingAverage { Length = Period };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(middle, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevMid = mid;
			_prevClose = candle.ClosePrice;
			return;
		}

		var close = candle.ClosePrice;

		if (_prevMid == null || _prevClose == null)
		{
			_prevMid = mid;
			_prevClose = close;
			return;
		}

		// Price crosses above midline → buy
		if (_prevClose.Value <= _prevMid.Value && close > mid && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Price crosses below midline → sell
		else if (_prevClose.Value >= _prevMid.Value && close < mid && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevMid = mid;
		_prevClose = close;
	}
}