Candle Shadows V1 — это ценовая контртрендовая система, переносящая оригинальный советник MetaTrader в экосистему StockSharp. Стратегия анализирует свечи с выраженной тенью в сторону импульса и практически отсутствующей противоположной тенью. Сделки разрешены только в первые минуты формирования бара, что позволяет имитировать работу MQL-версии, оставаясь в рамках обработки завершённых свечей.
Логика торговли
Подписаться на свечи заданного таймфрейма (по умолчанию 5 минут) и обрабатывать только закрытые бары.
Ограничить торговлю сессией, задаваемой параметрами StartHour и EndHour. Если свеча открылась вне интервала, сигнал игнорируется.
Разрешать вход только тогда, когда свеча закрылась раньше, чем через OpenWithinMinutes минут после открытия. Это отсеивает сигналы на слишком длинных барах.
Условие для покупки: нижняя тень должна быть длиннее CandleSizeMinPips, а верхняя тень не превышать OppositeShadowMaxPips. При отсутствии позиции отправляется рыночный ордер Buy.
Условие для продажи: верхняя тень длиннее CandleSizeMinPips, нижняя тень — не больше OppositeShadowMaxPips. При пустой позиции выполняется рыночный Sell.
На один бар допускается только одна сделка, что повторяет ограничение исходного эксперта.
Управление позицией
Все защитные расстояния задаются в пунктах и пересчитываются в цены через параметр 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);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class candle_shadows_v1_strategy(Strategy):
def __init__(self):
super(candle_shadows_v1_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._ema_length = self.Param("EmaLength", 30) \
.SetDisplay("EMA Length", "Trend EMA period", "Indicators")
self._shadow_ratio = self.Param("ShadowRatio", 3.0) \
.SetDisplay("Shadow Ratio", "Min shadow/body ratio", "Logic")
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def ShadowRatio(self):
return self._shadow_ratio.Value
def OnStarted2(self, time):
super(candle_shadows_v1_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
ev = float(ema_value)
body = abs(close - open_p)
if body <= 0:
return
upper_shadow = high - max(close, open_p)
lower_shadow = min(close, open_p) - low
if lower_shadow > body * self.ShadowRatio and close > ev and self.Position <= 0:
self.BuyMarket()
elif upper_shadow > body * self.ShadowRatio and close < ev and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return candle_shadows_v1_strategy()