High level StockSharp port of the MetaTrader "EA Stochastic" expert advisor. The strategy subscribes to one candle series, reads
stochastic oscillator values, and keeps at most a single net position. Entries occur when the main stochastic line has stayed on the
same side of the configured thresholds for a configurable number of bars. Protective exits and a trailing stop mirror the original
MQL implementation using pip-based distances.
Strategy Overview
Indicator: classic stochastic oscillator (%K and %D components with configurable smoothing)
Direction: both long and short
Positioning: single position at a time (new trades are ignored while an exit order is pending)
Order type: market orders using fixed volume
Data: a single user-selected candle type (default 15 minute candles)
Entry Logic
The main stochastic value is stored on every finished candle.
After at least ComparedBar values are cached, compare the current kValue with the value from ComparedBar - 1 candles ago.
Go Long when both values are below UpperLevel. This matches the original EA that only buys when the oscillator has stayed
below the upper threshold for the configured lookback length.
Go Short when both values are above LowerLevel. The original EA allowed shorts whenever stochastic stayed above the lower
bound.
New entries are skipped if a position exists or if a protective exit has already been requested for the current position.
Exit and Risk Management
Stop Loss: optional fixed pip distance from the entry price. Stops are evaluated against candle lows (for longs) or highs
(for shorts).
Take Profit: optional fixed pip target. High/low checks emulate the MetaTrader order-based take profit behavior.
Trailing Stop: activated once the open trade gains more than (TrailingStopPips + TrailingStepPips) pips. The stop is then
moved to TrailingStopPips behind the latest extreme, respecting the trailing step gap just like the original EA.
Exit orders: closes are issued with market orders (SellMarket / BuyMarket). A guard flag prevents repeated exit orders
until OnPositionChanged confirms the flat state.
Parameters
StopLossPips (default 50): pip distance used for the initial protective stop. Set to zero to disable.
TakeProfitPips (default 150): pip distance for profit taking. Set to zero to disable.
TrailingStopPips (default 15): trailing distance in pips. Must be greater than zero if trailing is enabled.
TrailingStepPips (default 5): minimal pip progress required before the trailing stop is updated. Trailing is rejected when
this value is zero.
Volume (default 1): market order volume used for both long and short trades.
KPeriod (default 5): lookback length for the %K stochastic line.
DPeriod (default 3): smoothing length for the %D line.
Slowing (default 3): final smoothing applied to the %K calculation.
UpperLevel (default 80): threshold used to validate long setups.
LowerLevel (default 20): threshold used to validate short setups.
ComparedBar (default 3): number of bars to look back when validating stochastic levels (minimum 1).
CandleType (default 15 minute candles): candle series subscribed by the strategy.
Implementation Notes
Pip size is approximated from Security.PriceStep. For instruments with fractional pips (typical FX pairs) steps smaller than
0.001 are automatically multiplied by 10, reproducing the MetaTrader digits_adjust logic.
Trailing stop configuration is validated on start to avoid the original EA error case (TrailingStop > 0 with zero trailing
step).
The StockSharp stochastic oscillator uses default smoothing and price modes (close/high/low), which maps to the EA settings of
simple moving average over high/low ranges.
The original EA provided both fixed lot and risk-percent position sizing. This port keeps the fixed Volume parameter and can be
extended if percentage-based sizing is required.
Chart output renders the processed candles, stochastic indicator, and executed trades for easier debugging.
Suggested Usage
Works on intraday or higher timeframes; adjust CandleType and stochastic periods to fit the instrument.
Tune UpperLevel, LowerLevel, and ComparedBar for different market regimes (range vs. trend).
Combine with broker-side risk controls in live trading because exits are simulated through market orders after candle
confirmation.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// EA Stochastic strategy (simplified). Uses RSI as oscillator proxy.
/// </summary>
public class EaStochasticStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _upperLevel;
private readonly StrategyParam<decimal> _lowerLevel;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public decimal UpperLevel
{
get => _upperLevel.Value;
set => _upperLevel.Value = value;
}
public decimal LowerLevel
{
get => _lowerLevel.Value;
set => _lowerLevel.Value = value;
}
public EaStochasticStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_upperLevel = Param(nameof(UpperLevel), 70m)
.SetDisplay("Upper Level", "Overbought level", "Levels");
_lowerLevel = Param(nameof(LowerLevel), 30m)
.SetDisplay("Lower Level", "Oversold level", "Levels");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiLength };
decimal prevRsi = 50;
bool hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, (ICandleMessage candle, decimal rsiValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevRsi = rsiValue;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevRsi = rsiValue;
return;
}
// Cross up from oversold
if (prevRsi < LowerLevel && rsiValue >= LowerLevel && Position <= 0)
{
BuyMarket();
}
// Cross down from overbought
else if (prevRsi > UpperLevel && rsiValue <= UpperLevel && Position >= 0)
{
SellMarket();
}
prevRsi = rsiValue;
})
.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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class ea_stochastic_strategy(Strategy):
def __init__(self):
super(ea_stochastic_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candles", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._upper_level = self.Param("UpperLevel", 70.0) \
.SetDisplay("Upper Level", "Overbought level", "Levels")
self._lower_level = self.Param("LowerLevel", 30.0) \
.SetDisplay("Lower Level", "Oversold level", "Levels")
self._prev_rsi = 50.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def UpperLevel(self):
return self._upper_level.Value
@property
def LowerLevel(self):
return self._lower_level.Value
def OnReseted(self):
super(ea_stochastic_strategy, self).OnReseted()
self._prev_rsi = 50.0
self._has_prev = False
def OnStarted2(self, time):
super(ea_stochastic_strategy, self).OnStarted2(time)
self._prev_rsi = 50.0
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self._has_prev:
self._prev_rsi = rv
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
ll = float(self.LowerLevel)
ul = float(self.UpperLevel)
if self._prev_rsi < ll and rv >= ll and self.Position <= 0:
self.BuyMarket()
elif self._prev_rsi > ul and rv <= ul and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rv
def CreateClone(self):
return ea_stochastic_strategy()