The Eugene Strategy ports the original MetaTrader 4 expert advisor "Eugene" to the StockSharp high level API. The algorithm monitors hourly candles by default and looks for breakouts of inside candles that are confirmed by a retracement to a third of the prior candle. Once a breakout is confirmed the strategy enters in the breakout direction and can reverse existing positions when an opposite setup appears.
Trading Logic
Inside candle detection – the previous candle must be completely inside the range of the candle before it. Its closing direction determines whether it is classified as a black (bearish) or white (bullish) insider.
Bird filters – an inside candle confirmed by another candle of the same color behind it is marked as a "bird". Black birds block long trades, white birds block short trades. This mirrors the protective filter from the MQL version.
Zigzag confirmation levels – two confirmation prices are computed at one third of the prior candle body or wick:
The long confirmation level is one third below the previous close (body for bullish candles, wick for bearish candles).
The short confirmation level is one third above the previous close (body for bearish candles, wick for bullish candles).
Session filter – if the current candle opens at 08:00 or later, confirmations are considered satisfied even without a retracement.
Breakout condition – a buy signal requires the current candle to make a higher high than the previous candle while keeping a higher low and overlapping the range of the candle two bars back. A sell signal uses the symmetric conditions with lower lows and lower highs.
Position management – before opening a new trade the strategy closes any opposite exposure. Only one long and one short entry can be issued per candle, replicating the Counter_buy and Counter_sell constraints from the original expert advisor.
Parameters
Name
Description
Default
Trade Volume
Order size for market orders.
0.1
Candle Type
Time frame of the processed candle series.
1 hour
Charting
When a chart area is available the strategy plots the processed candles together with the executed trades, helping to visualise the breakout behaviour.
Notes
The StockSharp version keeps the hourly session filter from the MQL expert. Adjust the candle type when trading other markets or time zones.
Stop-loss and take-profit management is not included in the source MQL file. The port therefore leaves risk management to the hosting environment.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class EugeneStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevSma;
private bool _hasPrev;
private int _cooldownRemaining;
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public EugeneStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 50).SetDisplay("SMA Period", "SMA lookback", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetDisplay("RSI Period", "RSI lookback", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevSma = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevSma = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sma, decimal rsi)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevSma = sma; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevSma = sma;
return;
}
if (_prevClose <= _prevSma && close > sma && rsi < 70 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevSma && close < sma && rsi > 30 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevSma = sma;
}
}
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 SimpleMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class eugene_strategy(Strategy):
def __init__(self):
super(eugene_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 50) \
.SetDisplay("SMA Period", "SMA lookback", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def sma_period(self):
return self._sma_period.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(eugene_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(eugene_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
self._cooldown_remaining = 0
sma = SimpleMovingAverage()
sma.Length = self.sma_period
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, rsi, self.process_candle).Start()
def process_candle(self, candle, sma, rsi):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma_val = float(sma)
rsi_val = float(rsi)
if not self._has_prev:
self._prev_close = close
self._prev_sma = sma_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_sma = sma_val
return
if self._prev_close <= self._prev_sma and close > sma_val and rsi_val < 70 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_sma and close < sma_val and rsi_val > 30 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_sma = sma_val
def CreateClone(self):
return eugene_strategy()