Открыть на GitHub

Стратегия Candle Shadows V1

Обзор

Candle Shadows V1 — это ценовая контртрендовая система, переносящая оригинальный советник MetaTrader в экосистему StockSharp. Стратегия анализирует свечи с выраженной тенью в сторону импульса и практически отсутствующей противоположной тенью. Сделки разрешены только в первые минуты формирования бара, что позволяет имитировать работу MQL-версии, оставаясь в рамках обработки завершённых свечей.

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

  1. Подписаться на свечи заданного таймфрейма (по умолчанию 5 минут) и обрабатывать только закрытые бары.
  2. Ограничить торговлю сессией, задаваемой параметрами StartHour и EndHour. Если свеча открылась вне интервала, сигнал игнорируется.
  3. Разрешать вход только тогда, когда свеча закрылась раньше, чем через OpenWithinMinutes минут после открытия. Это отсеивает сигналы на слишком длинных барах.
  4. Условие для покупки: нижняя тень должна быть длиннее CandleSizeMinPips, а верхняя тень не превышать OppositeShadowMaxPips. При отсутствии позиции отправляется рыночный ордер Buy.
  5. Условие для продажи: верхняя тень длиннее CandleSizeMinPips, нижняя тень — не больше OppositeShadowMaxPips. При пустой позиции выполняется рыночный Sell.
  6. На один бар допускается только одна сделка, что повторяет ограничение исходного эксперта.

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

  • Все защитные расстояния задаются в пунктах и пересчитываются в цены через параметр PipValue.
  • После закрытия каждого бара проверяются жёсткие уровни стоп-лосса и тейк-профита. При достижении экстремума свечи позиция закрывается.
  • Трейлинг-стоп повторяет логику MQL: как только прибыль превышает TrailingStopPips + TrailingStepPips, стоп переносится с шагом TrailingStepPips пунктов.
  • Если позиция находится в рынке более PositionLivesBars баров, она закрывается. Дополнительно прибыльные позиции фиксируются после CloseProfitsOnBar баров, чтобы закрепить результат.
  • После убыточной сделки следующий объём рассчитывается как BaseVolume, делённый на LossReductionFactor, что воспроизводит механизм уменьшения лота после стопа.

Параметры

Имя Описание По умолчанию
PipValue Стоимость одного пункта, используемая для преобразования параметров в цену. 0.0001
StopLossPips Расстояние до стоп-лосса в пунктах. Значение 0 отключает жёсткий стоп. 50
TakeProfitPips Расстояние до тейк-профита в пунктах. Значение 0 отключает фиксированный таргет. 50
TrailingStopPips Базовое расстояние трейлинг-стопа в пунктах. При 0 трейлинг не используется. 15
TrailingStepPips Минимальный шаг подтягивания трейлинг-стопа в пунктах; должен быть > 0. 5
PositionLivesBars Максимальное число завершённых баров, в течение которых позиция может оставаться открытой. 4
CloseProfitsOnBar Если больше нуля, прибыльные позиции закрываются после указанного количества баров. 2
OpenWithinMinutes Максимальное время после открытия свечи (в минутах), в течение которого разрешён вход. 7
CandleSizeMinPips Минимальная длина основной тени свечи в пунктах. 15
OppositeShadowMaxPips Максимальная длина противоположной тени в пунктах. 1
StartHour Час начала торговой сессии (время площадки, 0–23). 6
EndHour Час окончания торговой сессии (время площадки, 0–23). 18
LossReductionFactor Делитель, уменьшающий базовый объём после убыточной сделки. 1.5
BaseVolume Базовый объём рыночных ордеров. 1
CandleType Тип свечей, используемых в расчётах; по умолчанию 5-минутные свечи. 5 мин

Примечания

  • Обязательно настройте PipValue в соответствии с тик-сайзом инструмента (например, 0.01 для пар с JPY или 1 для индексов).
  • Поскольку стратегия работает по закрытию бара, наиболее точное воспроизведение MQL-логики достигается на коротких таймфреймах (1–5 минут).
  • Дополнительные индикаторы не требуются, поэтому стратегия легко запускается на любом источнике данных StockSharp.
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>
/// Candle Shadows V1 strategy (simplified). Detects candles with long shadows
/// (wicks) relative to body as reversal signals, filtered by EMA trend.
/// </summary>
public class CandleShadowsV1Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<decimal> _shadowRatio;

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

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

	public decimal ShadowRatio
	{
		get => _shadowRatio.Value;
		set => _shadowRatio.Value = value;
	}

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

		_emaLength = Param(nameof(EmaLength), 30)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "Trend EMA period", "Indicators");

		_shadowRatio = Param(nameof(ShadowRatio), 3m)
			.SetDisplay("Shadow Ratio", "Min shadow/body ratio", "Logic");
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, (ICandleMessage candle, decimal emaValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;
				var open = candle.OpenPrice;
				var high = candle.HighPrice;
				var low = candle.LowPrice;

				var body = Math.Abs(close - open);
				if (body <= 0) return;

				var upperShadow = high - Math.Max(close, open);
				var lowerShadow = Math.Min(close, open) - low;

				// Long lower shadow (hammer) near EMA => buy signal
				if (lowerShadow > body * ShadowRatio && close > emaValue && Position <= 0)
					BuyMarket();
				// Long upper shadow (shooting star) near EMA => sell signal
				else if (upperShadow > body * ShadowRatio && close < emaValue && Position >= 0)
					SellMarket();
			})
			.Start();

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