Открыть на GitHub

Стратегия GoldWarrior02b

Подробная адаптация советника MetaTrader 4 GoldWarrior02b (каталог MQL/7694) под StockSharp. Стратегия объединяет Commodity Channel Index (CCI), пользовательский импульсный фильтр и собственную реализацию ZigZag. Сигналы проверяются только в последние секунды каждого пятнадцатиминутного блока. Основная цель перевода — воспроизвести логику оригинального советника с учётом того, что в StockSharp используются неттинговые позиции.

Основные особенности

  • Импульсный фильтр — вместо индикатора DayImpuls используется среднее значение разницы между открытием и закрытием свечи, нормированное на шаг цены инструмента.
  • Структура ZigZag — пересчитывает последние экстремумы, чтобы определить преобладающее направление тренда.
  • Временной фильтр — входы разрешены только когда свеча закрывается в последние 15 секунд минут 14, 29, 44 или 59.
  • Контроль рисков — стоп-лосс, тейк-профит, при необходимости трейлинг-стоп и общий целевой профит в валюте счёта. Значения по умолчанию повторяют параметры MQL (стоп 1000 пунктов, тейк 150 пунктов, трейлинг выключен).
  • Неттовая позиция — в отличие от сеточного хеджирования в MT4, в StockSharp поддерживается только одна суммарная позиция, поэтому наращивание и ступенчатый хедж не реализованы.

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

Подготовка сигналов

  1. Подписка на свечи типа CandleType (по умолчанию таймфрейм 5 минут).
  2. Расчёт CCI и импульсного среднего по периоду ImpulsePeriod (по умолчанию 21 бар).
  3. Обновление направления ZigZag после превышения порога ZigZagDeviation и соблюдения параметров глубины/отката.
  4. Сохранение предыдущих значений индикаторов для имитации буферов cci0, cci1, imp, nimp из советника.

Правила входа

Сетап оценивается только при отсутствии позиции, если прошло минимум 15 секунд с момента последнего выхода и функция AllowEntryTime возвращает true (конец 15-минутного блока).

Покупка:

  • Последний поворот ZigZag направлен вниз (новый минимум ниже предыдущего).
  • Выполнено одно из условий:
    • Текущий CCI растёт относительно предыдущего, прошлый CCI ниже -50, текущий CCI выше -30, импульс становится положительным, а предыдущий был отрицательным.
    • Текущий CCI ниже -200, предыдущий CCI ещё ниже, импульс остаётся ниже ImpulseBuyThreshold и сильнее предыдущего значения.

Продажа:

  • Последний поворот ZigZag направлен вверх (новый максимум выше предыдущего).
  • Выполнено одно из условий:
    • Текущий CCI падает относительно предыдущего, прошлый CCI выше 50, текущий CCI выше 30, импульс становится отрицательным, а предыдущий был положительным.
    • Текущий CCI выше 200, предыдущий CCI ещё выше, импульс остаётся выше ImpulseSellThreshold и слабее предыдущего значения.

Если предыдущее значение импульса находится между ImpulseSellThreshold и ImpulseBuyThreshold, сигнал игнорируется.

Управление позицией

  • Стоп-лосс — закрывает позицию при движении против нас на StopLossPoints (по умолчанию 1000 пунктов).
  • Тейк-профит — закрывает после достижения TakeProfitPoints (150 пунктов).
  • Трейлинг-стоп — опционально: активируется после движения на TrailingStopPoints + TrailingStepPoints в нужную сторону и затем сопровождает цену на расстоянии TrailingStopPoints.
  • Целевой профит — пересчитывает плавающий результат в валюту счёта по PriceStep и StepPrice и закрывает позицию после превышения ProfitTarget (по умолчанию 300).

Параметры

Имя Описание Значение по умолчанию
BaseVolume Размер позиции при входе. 0.1
StopLossPoints Дистанция стоп-лосса в пунктах. 1000
TakeProfitPoints Дистанция тейк-профита в пунктах. 150
TrailingStopPoints Дистанция трейлинг-стопа (0 — выключен). 0
TrailingStepPoints Дополнительная дистанция для активации трейлинга. 0
ImpulsePeriod Период CCI и импульсного индикатора. 21
ZigZagDepth Минимальное число баров между поворотами ZigZag. 12
ZigZagDeviation Минимальное движение (в пунктах) для фиксации поворота. 5
ZigZagBackstep Минимальное число баров перед сменой направления. 3
ProfitTarget Порог плавающей прибыли в валюте счёта. 300
ImpulseSellThreshold Минимальное значение импульса для продаж. -30
ImpulseBuyThreshold Максимальное значение импульса для покупок. 30
CandleType Рабочий таймфрейм. 5 минут

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

  • В MT4 используется GlobalVariableSet и сложная система хеджирования. В StockSharp сохранён только временной фильтр, а сетка ордеров отсутствует из-за неттинга.
  • Исполнение команд выполняется рыночными ордерами (BuyMarket, SellMarket) в соответствии с требованиями высокоуровневого API.
  • Индикатор DayImpuls приближен усреднением открытий и закрытий. Роль двух буферов imp и nimp выполняют текущее и предыдущее значения индикатора.

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

  • Настройте CandleType под таймфрейм оптимизации (в оригинале используется M5).
  • Убедитесь, что инструмент предоставляет PriceStep и StepPrice для корректного перевода пунктов в валюту.
  • Тестируйте со слиппеджем и задержками, чтобы убедиться в корректной работе временного фильтра.

Отказ от ответственности

Стратегия предоставляется в ознакомительных целях. Перед работой на реальном счёте проведите всестороннее тестирование.

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>
/// Gold Warrior Impulse strategy - CCI crossover with EMA trend filter.
/// Buys when CCI crosses above zero while price is above EMA.
/// Sells when CCI crosses below zero while price is below EMA.
/// </summary>
public class GoldWarrior02bImpulseStrategy : Strategy
{
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevCci;
	private bool _hasPrev;

	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public GoldWarrior02bImpulseStrategy()
	{
		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetDisplay("CCI Period", "CCI lookback", "Indicators");

		_emaPeriod = Param(nameof(EmaPeriod), 21)
			.SetDisplay("EMA Period", "EMA trend filter", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevCci = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(cci, ema, ProcessCandle)
			.Start();
	}

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevCci = cci;
			_hasPrev = true;
			return;
		}

		// CCI crosses above zero + price above EMA = buy
		if (_prevCci <= 0 && cci > 0 && close > ema && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// CCI crosses below zero + price below EMA = sell
		else if (_prevCci >= 0 && cci < 0 && close < ema && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevCci = cci;
	}
}