The One Two Three strategy trades breakouts of the Chaikin oscillator after an extended period of flat accumulation. It emulates the original MetaTrader 5 expert by combining an accumulation/distribution line with two EMAs, validating that market pressure has stayed neutral for several bars, and then entering on a strong surge of Chaikin momentum. The StockSharp port keeps the lot sizing, stop management, and trailing logic configurable through strategy parameters.
Concept
Build the Chaikin oscillator as the difference between a fast and a slow exponential moving average applied to the accumulation/distribution line derived from the incoming candles.
Track the last BarsCount oscillator readings and classify bars where the absolute Chaikin value stays within FlatLevel.
Allow trading only when more than FlatPercent percent of those stored readings stayed inside the flat range, signalling quiet accumulation.
When a new candle finishes, enter in the direction of the Chaikin surge if its magnitude exceeds OpenLevel.
Entry Rules
Long: The Chaikin oscillator on the just closed candle is greater than or equal to OpenLevel and the current net position is non-positive.
Short: The Chaikin oscillator on the just closed candle is less than or equal to the negative OpenLevel and the current net position is non-negative.
Orders are issued at market. If the strategy holds an opposite position, the order size is increased to flatten the existing exposure before establishing the new trade.
Exit Rules
A fixed stop-loss (StopLossPips) and take-profit (TakeProfitPips) are translated into price offsets using the security price step (1 pip = 1 price step) and applied immediately after entry.
An optional trailing stop adjusts the protective stop once price moves in favour of the trade by at least TrailingStopPips + TrailingStepPips. The new stop is placed exactly TrailingStopPips away from the current close while requiring the step buffer to avoid premature tightening.
If either the stop or the target is touched within the completed candle range, the position is closed at market.
Risk and Money Management
OrderVolume controls the quantity sent with every market order. The strategy automatically adds or subtracts the current position size when flipping direction so that reversals happen in a single trade.
Setting any of the pip-based parameters to zero disables that component (for example, a zero take-profit keeps trades open until the stop or opposite signal occurs).
Parameters
OrderVolume – Base volume for entries.
StopLossPips – Distance, in pips, between the entry price and the protective stop.
TakeProfitPips – Distance, in pips, between the entry price and the profit target.
TrailingStopPips – Distance, in pips, maintained between price and the trailing stop. Set to zero to disable trailing.
TrailingStepPips – Minimum pip gain beyond the trailing distance required before the stop is moved again.
FastLength – Period of the fast EMA in the Chaikin oscillator.
SlowLength – Period of the slow EMA in the Chaikin oscillator.
FlatLevel – Absolute Chaikin value that still counts as flat market behaviour.
OpenLevel – Chaikin magnitude required to trigger a new trade once the flat condition is satisfied.
BarsCount – Number of recent Chaikin values to evaluate when computing the flat ratio.
FlatPercent – Minimum percentage of the stored values that must stay within the flat range to allow trading.
CandleType – Candle data type or timeframe that feeds the indicator calculations.
Notes
The trailing logic mirrors the MetaTrader expert: if TrailingStopPips is non-zero, keep TrailingStepPips positive to avoid a stagnant stop.
Because StockSharp strategies work with the security price step, the pip-based distances assume that one pip equals one price step; adjust the parameter values accordingly for instruments with different tick sizes.
The strategy processes completed candles only and does not attempt to react intra-bar, matching the original expert that executes on new bar openings.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class OneTwoThreeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public OneTwoThreeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 5).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 13).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
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 one_two_three_strategy(Strategy):
def __init__(self):
super(one_two_three_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast EMA", "Fast period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 13) \
.SetDisplay("Slow EMA", "Slow period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(one_two_three_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(one_two_three_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return one_two_three_strategy()