Candle Shadows V1 is a price action reversal strategy that recreates the original MetaTrader expert advisor logic inside the StockSharp high-level API. The system looks for candles with a strong dominant wick and minimal opposite shadow during a configurable trading session. Trades are only allowed during the first minutes of a bar, emulating the intrabar execution of the MQL version while still working on closed candles.
Trading logic
Subscribe to the configured time-frame candles (default 5 minutes) and evaluate only finished bars.
Enforce a session window using the StartHour and EndHour parameters. If the candle opens outside the window no trade is considered.
Allow entries only if the candle closes before OpenWithinMinutes from its open time, preventing late signals on long bars.
Long setup: the candle must print a lower shadow greater than CandleSizeMinPips pips and the upper shadow must stay within OppositeShadowMaxPips pips. When the conditions are satisfied and there is no open position a market buy is sent.
Short setup: the candle must print an upper shadow greater than CandleSizeMinPips pips and the lower shadow must stay within OppositeShadowMaxPips pips. A market sell is issued if the account is flat.
Only one trade per candle is permitted, matching the original “one order per bar” constraint.
Position management
Initial protective distances are expressed in pips and converted through the PipValue parameter for every instrument.
Hard stop-loss and take-profit checks are performed on every finished candle. If the candle’s high/low touches the threshold the position is flattened.
Trailing management mimics the MQL trailing stop: once price advances by at least TrailingStopPips + TrailingStepPips the stop is moved in increments of TrailingStepPips pips.
If a position stays open longer than PositionLivesBars bars it is closed immediately. Profitable trades are also forced out after CloseProfitsOnBar bars to lock in gains.
The next trade volume is reduced by dividing the BaseVolume by LossReductionFactor whenever the previous trade closed with a loss, just like the lot reduction in the original expert advisor.
Parameters
Name
Description
Default
PipValue
Monetary value of one pip used to transform pip distances into price offsets.
0.0001
StopLossPips
Stop-loss distance in pips. Set to 0 to disable the hard stop.
50
TakeProfitPips
Take-profit distance in pips. Set to 0 to disable the hard target.
50
TrailingStopPips
Trailing stop distance in pips. When 0 no trailing is applied.
15
TrailingStepPips
Minimum step in pips between trailing stop adjustments. Must be positive when trailing is enabled.
5
PositionLivesBars
Maximum number of completed bars a position can remain open before it is force-closed.
4
CloseProfitsOnBar
When greater than zero, profitable positions are closed after this many bars from entry.
2
OpenWithinMinutes
Maximum amount of minutes after bar open when new trades are allowed.
7
CandleSizeMinPips
Required wick length (in pips) on the dominant side of the candle.
15
OppositeShadowMaxPips
Maximum size (in pips) of the opposite candle shadow.
1
StartHour
Session start hour in exchange time (0–23).
6
EndHour
Session end hour in exchange time (0–23).
18
LossReductionFactor
Divisor applied to BaseVolume after a losing trade.
1.5
BaseVolume
Default market order size used for entries.
1
CandleType
Candle series used for the calculations. Default is a 5-minute time frame.
5 min
Notes
Always adjust PipValue to match the instrument’s tick size (for example 0.01 for JPY crosses or 1 for index futures).
Because the strategy works with completed candles, executions will happen at bar close. Lower time frames (1–5 minutes) best replicate the intrabar behavior of the original expert advisor.
No external indicators are required, which makes the strategy easy to run on any StockSharp data source.
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()