The Tokyo Session Strategy replicates the logic of the MetaTrader expert advisor TokyoSessionEA_v2.8 in StockSharp. The
strategy is designed for intraday breakout or mean-reversion trading around the Asian (Tokyo) session. It captures a
reference candle at a configurable hour, builds a price channel from that candle, and evaluates breakout or rebound
conditions at another target hour. Depending on the chosen signal mode, the strategy can trade either contrary to the
level breakout (fade moves that extend beyond the reference range) or along the breakout direction.
The StockSharp port focuses on high-level API usage. All signal calculations are performed inside the candle subscription
handler, stops are managed through StartProtection, and every action is logged through LogInfo to keep the behaviour
transparent during backtests and live trading.
Trading Logic
Reference candle – at TimeSetLevels (broker hour) the strategy records the candle high, low and close. These
values define the session channel and reset the internal validation flags.
Channel validation – every finished candle between the reference hour and the entry hour can invalidate the
pending signal depending on configuration:
CheckAllBars: if enabled, the close must remain between the captured high and low.
ReCheckPrices: at TimeRecheckPrices the candle close is compared with the running average to confirm momentum.
Entry evaluation – when the candle that precedes TimeCheckLevels closes, the strategy compares its close price
with the channel borders. If the close is inside the configured distance range the corresponding position is opened.
Exits – positions can be closed by three mechanisms:
CloseInSignal closes a trade once price returns inside the channel (logic mirrors the original EA).
CloseOrdersOnTime flattens at TimeCloseOrders to avoid holding risk into the next session.
Protective stops, trailing stops and break-even handling are delegated to the StockSharp protection subsystem.
Parameters
General
Parameter
Description
CandleType
Candle series used for analysis (defaults to H1).
BrokerOffset
Difference between broker time and GMT in hours.
Signals
Parameter
Description
TypeOfSignals
ContraryTrend replicates fading the breakout; AccordingTrend follows the breakout direction.
TimeSetLevels
Hour (0–23) when the reference candle is captured.
TimeCheckLevels
Hour when breakout conditions are evaluated.
TimeRecheckPrices
Additional momentum check hour.
MinDistanceOfLevel
Minimum distance (in pips) between the close and the channel before allowing a trade.
MaxDistanceOfLevel
Maximum distance (in pips) from the level. Zero disables the limit.
ReCheckPrices
Enables/disables the additional momentum filter.
CheckAllBars
Requires all intermediate closes to stay within the channel.
Risk Management
Parameter
Description
CloseInSignal
Exit once price crosses back through the channel boundary.
CloseOrdersOnTime
Flatten positions after TimeCloseOrders.
TimeCloseOrders
Hour used by the time-based exit.
UseTakeProfit, TakeProfit
Enable and configure a fixed take-profit (pips).
UseStopLoss, StopLoss
Enable and configure a protective stop-loss (pips).
Move stop-loss to break-even once profit reaches the trigger distance.
Trading
Parameter
Description
Volume
Base order volume. When flipping direction the opposite position is closed automatically.
MaxOrders
Maximum number of Volume blocks allowed in one direction. Set to 0 for no limit.
Workflow
Deploy the strategy on an instrument with a valid price step (Security.PriceStep).
Select the desired timeframe and configure the broker hour offsets to align the daily schedule with the exchange.
Tune the distance and validation filters to match the behaviour of the original EA or to adapt to different markets.
Configure risk parameters. The StockSharp port natively manages stops and trailing logic through StartProtection.
Start the strategy. Logging messages will report the captured levels, opened trades and exit decisions.
Differences from the MetaTrader Version
Floating-point entries based on UseFloatingPoint and PipsFloatingPoint are not implemented because StockSharp
executes market orders at the time the signal is generated.
Spread and slippage filters are omitted because high-level candle subscriptions do not provide tick-level bid/ask data.
Automatic money management (AutoLotSize, RiskFactor, recovery lots, preset symbol switching) is replaced with the
simpler Volume and MaxOrders parameters. Position sizing should be adjusted directly in the strategy settings.
Sound and print notifications are replaced by LogInfo messages.
All other signal conditions, validation gates and time-based exits mirror the behaviour of the original EA.
Notes
The default configuration is aligned with the H1 timeframe recommended by the original expert advisor. Other timeframes
can be used, but the hour-based logic assumes that candle durations divide an hour evenly.
Ensure that the data feed delivers continuous candles for the selected timeframe. Missing candles may invalidate the
average and channel checks.
Because the strategy closes positions by sending market orders, brokers that require limit orders or minimum holding
times may need additional adaptations.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Tokyo Session strategy: breakout from session range.
/// Tracks high/low during first hours, then buys breakout above high, sells below low.
/// </summary>
public class TokyoSessionStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public TokyoSessionStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for volatility filter", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrPeriod };
var sessionHigh = decimal.MinValue;
var sessionLow = decimal.MaxValue;
var candleCount = 0;
var rangeSet = false;
decimal? prevClose = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, (candle, atrVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
candleCount++;
// Build range from first 4 candles (2 hours of 30min candles)
if (candleCount <= 4)
{
if (candle.HighPrice > sessionHigh)
sessionHigh = candle.HighPrice;
if (candle.LowPrice < sessionLow)
sessionLow = candle.LowPrice;
if (candleCount == 4)
rangeSet = true;
prevClose = candle.ClosePrice;
return;
}
if (!rangeSet)
{
prevClose = candle.ClosePrice;
return;
}
// Reset range every 48 candles (24 hours of 30min candles)
if (candleCount % 48 == 0)
{
sessionHigh = candle.HighPrice;
sessionLow = candle.LowPrice;
rangeSet = false;
candleCount = 0;
prevClose = candle.ClosePrice;
return;
}
var close = candle.ClosePrice;
if (prevClose.HasValue)
{
var crossAboveHigh = prevClose.Value <= sessionHigh && close > sessionHigh;
var crossBelowLow = prevClose.Value >= sessionLow && close < sessionLow;
if (crossAboveHigh && Position <= 0)
BuyMarket();
else if (crossBelowLow && Position >= 0)
SellMarket();
}
prevClose = close;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class tokyo_session_strategy(Strategy):
def __init__(self):
super(tokyo_session_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for volatility filter", "Indicators")
self._atr = None
self._session_high = None
self._session_low = None
self._candle_count = 0
self._range_set = False
self._prev_close = None
@property
def atr_period(self):
return self._atr_period.Value
def OnReseted(self):
super(tokyo_session_strategy, self).OnReseted()
self._atr = None
self._session_high = None
self._session_low = None
self._candle_count = 0
self._range_set = False
self._prev_close = None
def OnStarted2(self, time):
super(tokyo_session_strategy, self).OnStarted2(time)
self._atr = AverageTrueRange()
self._atr.Length = self.atr_period
self._session_high = -999999999.0
self._session_low = 999999999.0
self._candle_count = 0
self._range_set = False
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.Bind(self._atr, self._process_candle)
subscription.Start()
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
if not self._atr.IsFormed:
return
self._candle_count += 1
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._candle_count <= 4:
if high > self._session_high:
self._session_high = high
if low < self._session_low:
self._session_low = low
if self._candle_count == 4:
self._range_set = True
self._prev_close = close
return
if not self._range_set:
self._prev_close = close
return
if self._candle_count % 48 == 0:
self._session_high = high
self._session_low = low
self._range_set = False
self._candle_count = 0
self._prev_close = close
return
if self._prev_close is not None:
cross_above = self._prev_close <= self._session_high and close > self._session_high
cross_below = self._prev_close >= self._session_low and close < self._session_low
if cross_above and self.Position <= 0:
self.BuyMarket()
elif cross_below and self.Position >= 0:
self.SellMarket()
self._prev_close = close
def CreateClone(self):
return tokyo_session_strategy()