Flat Trend EA Strategy is a StockSharp port of the MQL5 expert advisor "Flat Trend EA". The algorithm combines the Parabolic SAR indicator with the Average Directional Index (ADX) to detect four market states: trending up, trending down, end of buy, and end of sell. The strategy reacts only to completed candles from the configured timeframe and mirrors the original logic of closing opposite positions before opening a new one.
Trading Logic
Buy signal: the Parabolic SAR dot prints below the close price and the ADX +DI line is above the -DI line. Any short exposure is closed immediately, and a new long is opened when no position is active.
Sell signal: the Parabolic SAR dot prints above the close price and +DI is less than or equal to -DI. Any long exposure is closed before opening a short trade.
End-of-trend filters: when the SAR is above price while +DI is greater than -DI the strategy marks the end of a short trend; when SAR is below price while +DI is less than or equal to -DI it marks the end of a long trend. Both events force existing positions to be closed without opening a new trade.
Trading window: optional session filters restrict entries to the [StartHour, EndHour) interval. Signals outside the session can still close trades, but new entries are skipped.
Risk Management
Stop-loss and take-profit distances are measured in pips (automatically scaled for three- and five-digit instruments). Prices are normalized to the security step.
Trailing stop activates after the position gains more than TrailingStopPips + TrailingStepPips. Long positions trail below the latest close, shorts trail above. When trailing is disabled the stop level remains fixed.
Protective exits: on every finished candle the strategy checks low/high prices against stop-loss, take-profit, and trailing levels. Any breach closes the position and resets risk tracking.
Parameters
StopLossPips – distance to the protective stop in pips.
TakeProfitPips – distance to the target in pips.
TrailingStopPips – trailing stop distance in pips (set to 0 to disable trailing).
TrailingStepPips – extra progress required before the trailing stop moves; must be positive when trailing is enabled.
UseTradingHours – enables the trading window filter.
StartHour / EndHour – inclusive start hour and exclusive end hour for entries (exchange time).
AdxPeriod – smoothing period for ADX, which controls +DI and -DI sensitivity.
SarStart, SarIncrement, SarMaximum – Parabolic SAR acceleration settings matching the original indicator (0.02 / 0.02 / 0.2 by default).
CandleType – timeframe used for candle subscriptions and indicator calculations.
Volume – inherited from Strategy, represents the order size used when entering new positions.
Indicators
Average Directional Index (ADX) provides the +DI and -DI components used to determine the current trend direction.
Parabolic SAR defines whether the market structure is bullish or bearish and supplies the dot level for trailing logic.
Additional Notes
Pip size is computed from the security settings: for three- and five-decimal instruments the price step is multiplied by ten to match the MQL definition of a pip.
The strategy always closes existing positions when opposite or end signals appear before evaluating new entries, reproducing the original EA workflow.
Only the C# implementation is supplied; no Python version or folder is created, as requested.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Flat Trend EA strategy. Uses DEMA crossover to distinguish flat from trend.
/// </summary>
public class FlatTrendEaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _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 FlatTrendEaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast DEMA", "Fast DEMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow DEMA", "Slow DEMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(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 DoubleExponentialMovingAverage { Length = FastPeriod };
var slow = new DoubleExponentialMovingAverage { 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 fastVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fastVal > slowVal;
_prevFast = fastVal;
_prevSlow = slowVal;
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 DoubleExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class flat_trend_ea_strategy(Strategy):
def __init__(self):
super(flat_trend_ea_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", 10) \
.SetDisplay("Fast DEMA", "Fast DEMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow DEMA", "Slow DEMA 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(flat_trend_ea_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(flat_trend_ea_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = DoubleExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = DoubleExponentialMovingAverage()
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 flat_trend_ea_strategy()