Flat 001a is a scalping system designed for the EURUSD hourly chart. It scans the most recent three hourly candles and measures the distance between the highest high and the lowest low. When the range of this three-candle window stays within a configurable number of points, the strategy anticipates that price will remain trapped inside the flat. It then looks to fade short-term excursions into the upper or lower quarter of the channel and immediately attaches protective orders.
The original MQL4 expert adviser traded only EURUSD on H1 and rejected trading if the symbol or timeframe was incorrect. This port keeps the same defaults (EURUSD, 60-minute candles) and reproduces all entry, stop-loss, take-profit, and trailing-stop calculations in StockSharp.
Indicators and data
Highest and Lowest indicators (period = 3) track the top and bottom of the last three finished candles.
A time-frame parameter defaults to 60-minute candles to mirror the H1 requirement of the source code.
No additional oscillators or smoothing filters are used, so the strategy reacts solely to raw price extremes.
Entry logic
Wait for the subscription candle to close. Only finished candles are processed.
Verify that the current security code matches the configured code (default: EURUSD). If it does not, the strategy remains idle.
Evaluate the optional trading window. By default, entries are allowed during the two hours starting at midnight platform time (hours 0 and 1). The filter can be disabled.
Compute the three-candle range range = highest - lowest and translate it to points via the instrument PriceStep.
Continue only if the number of points lies between DiffMinPoints and DiffMaxPoints.
If the closing price sits inside the lowest quarter of the range and no position is open, enter a long trade.
If the closing price sits inside the highest quarter of the range and no position is open, enter a short trade.
Order management
Initial stop-loss
Long trades: lowest - range / 3.
Short trades: highest + range / 3.
Take-profit
Long trades: entry price + TakeProfitPoints * PriceStep.
Short trades: entry price − TakeProfitPoints * PriceStep.
Trailing-stop
Once the unrealized profit exceeds TrailingStopPoints * PriceStep, the stop-loss is trailed candle by candle.
Long trades move the stop to closePrice - TrailingDistance if that is higher than the current stop.
Short trades move the stop to closePrice + TrailingDistance if that is lower than the current stop.
All exits are executed with market orders. The strategy closes the full position when either the stop-loss or take-profit level is touched by the subsequent candle.
Parameters
Group
Name
Description
Default
General
CandleType
Candle type used for calculations. Should be set to a 60-minute timeframe to match the original system.
TimeFrame(60m)
General
SecurityCode
Expected security code. Leave empty to trade any instrument.
EURUSD
Range Filter
DiffMinPoints
Minimum three-candle range in points required to trade.
18
Range Filter
DiffMaxPoints
Maximum three-candle range in points allowed to trade.
28
Trading Window
EnableTimeFilter
Enables or disables the hour filter.
true
Trading Window
OpenHour
Starting hour (0–23) for the trading window. The strategy also allows the immediate next hour.
0
Risk Management
TakeProfitPoints
Take-profit distance expressed in points. Set to zero to disable.
8
Risk Management
TrailingStopPoints
Trailing-stop distance expressed in points. Set to zero to disable trailing.
6
Practical notes
The StockSharp Strategy.Volume property controls the order size. Adjust it according to your broker contract size.
Ensure that the selected instrument exposes a valid PriceStep. If PriceStep is missing, the strategy falls back to 1 and logs a warning.
The MQL4 expert adviser offered optional money management by scaling lots according to account balance. StockSharp’s sample keeps the position size constant; you can script your own volume management if required.
Always test the strategy in simulation before running it live. The trailing logic assumes that the broker will fill protective orders when candle extremes cross the level; in fast markets, slippage may increase realized risk.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Flat 001a: RSI reversal with EMA filter and ATR stops.
/// </summary>
public class Flat001aStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevRsi;
private decimal _entryPrice;
public Flat001aStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0; _entryPrice = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevRsi == 0 || atrVal <= 0) { _prevRsi = rsiVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 70) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 30) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevRsi = rsiVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
class flat001a_strategy(Strategy):
def __init__(self):
super(flat001a_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_rsi = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(flat001a_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._entry_price = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
ev = float(ema_val)
av = float(atr_val)
if self._prev_rsi == 0 or av <= 0:
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + av * 2.5 or close <= self._entry_price - av * 1.5 or rv > 70:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 2.5 or close >= self._entry_price + av * 1.5 or rv < 30:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
if self.Position == 0:
if rv > 55 and self._prev_rsi <= 55 and close > ev:
self._entry_price = close
self.BuyMarket()
elif rv < 45 and self._prev_rsi >= 45 and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_rsi = rv
def OnReseted(self):
super(flat001a_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._entry_price = 0.0
def CreateClone(self):
return flat001a_strategy()