The strategy reproduces the MetaTrader 5 expert Exp_XFisher_org_v1. It trades reversals detected on the Fisher transform of
price that is additionally smoothed with a configurable moving average. The StockSharp port keeps the counter-trend nature of
the original robot: when the Fisher curve turns downward after an upswing a long position is opened, and when the curve turns
upward after a downswing a short position is opened. Existing positions are closed once the indicator reverses in the opposite
direction.
The helper indicator XFisherOrgIndicator implemented in CS/ExpXFisherOrgV1Strategy.cs follows the MT5 logic:
Take the highest high and lowest low over Length finished candles.
Convert the selected price source (see Applied Price below) into the 0–1 range using those extremes.
Apply the recursive filter value = (wpr - 0.5) + 0.67 * value[prev] followed by the Fisher transform
fish = 0.5 * ln((1 + value) / (1 - value)) + 0.5 * fish[prev].
Smooth the result with one of the supported moving averages. The smoothed Fisher value forms the main line; the signal line
is simply the previous bar’s smoothed value, exactly as in the MQL version where buffer #1 stores a one-bar shift.
The conversion keeps the original defaults (Length = 7, Jurik smoothing of length 5, phase 15, H4 candles) and exposes the
same enable/disable switches for opening and closing long/short trades.
Trading rules
Long entry – when the Fisher value from SignalBar + 1 bars ago was rising (Fisher[SignalBar+1] > Fisher[SignalBar+2])
but the value at SignalBar crosses below or touches its delayed copy (Fisher[SignalBar] <= Fisher[SignalBar+1]).
Short entry – when the Fisher value from SignalBar + 1 bars ago was falling but the value at SignalBar crosses above
its delayed copy.
Position exit – the opposite reversal closes an existing position before considering a new trade. A long exit is triggered
by the same condition that opens a short, and vice versa.
Volume – controlled by OrderVolume. When a flip from short to long (or long to short) is required the strategy sends a
single market order with enough volume to close the old position and open the new one in the same transaction, mimicking the
behaviour of the original BuyPositionOpen/SellPositionOpen helpers.
All calculations use finished candles only. If SignalBar is zero the current closed candle is used for signal evaluation;
positive values shift the signal back in time exactly like the MT5 SignalBar input.
Parameters
Name
Description
Default
OrderVolume
Volume of every market order.
1
BuyOpenAllowed / SellOpenAllowed
Permit opening long/short trades.
true
BuyCloseAllowed / SellCloseAllowed
Permit closing existing long/short trades.
true
SignalBar
Shift (in closed candles) used to read the Fisher buffers.
1
Length
Lookback for highest/lowest price extremes.
7
SmoothingLength
Period of the smoothing average.
5
Phase
Jurik phase (ignored by other methods).
15
SmoothingMethod
Moving average applied to the Fisher output.
Jjma
PriceType
Applied price forwarded to the indicator (close, open, median, etc.).
Close
CandleType
Candle series used for the calculation (default: 4 hour candles).
H4
Smoothing method mapping
The original indicator exposes a large set of smoothing kernels. The StockSharp port maps them to reliable built-in
implementations:
Jjma, Jurx, T3 → JurikMovingAverage (phase parameter applied when the property is available).
Sma, Ema, Smma, Lwma → respective StockSharp moving averages.
Parabolic → approximated by ExponentialMovingAverage (closest behaviour under StockSharp).
Vidya, Ama → KaufmanAdaptiveMovingAverage (the adaptive VIDYA behaviour is modelled with Kaufman AMA).
This mapping mirrors the approach used in other Kositsin conversions inside the repository and keeps the response of the
smoothed Fisher line comparable to the MT5 implementation.
Differences from the MT5 expert
Money management – StockSharp strategies operate on explicit volumes. The MM/MarginMode inputs from MT5 are replaced
with a single OrderVolume parameter so the trader can define the lot size directly.
Execution model – trades are generated once per finished candle via the high-level subscription API instead of on every
tick. This avoids duplicate orders and removes the need for the original IsNewBar helper.
Applied price options – all price modes from SmoothAlgorithms.mqh are supported, including TrendFollow and Demark
variants.
Charting – the strategy draws candles, the smoothed Fisher transform and the executed trades in the default chart area.
Files
CS/ExpXFisherOrgV1Strategy.cs – strategy class, indicator implementation and value container.
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>
/// Exp XFisher org v1 strategy using EMA crossover as trend filter.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class ExpXFisherOrgV1Strategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExpXFisherOrgV1Strategy"/> class.
/// </summary>
public ExpXFisherOrgV1Strategy()
{
_fastPeriod = Param(nameof(FastPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// EMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 exp_x_fisher_org_v1_strategy(Strategy):
"""
Exp XFisher org v1: EMA crossover with SL/TP and cooldown.
Buys when fast EMA crosses above slow EMA, sells on reverse.
"""
def __init__(self):
super(exp_x_fisher_org_v1_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 7) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Trading timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_x_fisher_org_v1_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(exp_x_fisher_org_v1_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self._fast_period.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast_val)
self._prev_slow = float(slow_val)
return
fast_val = float(fast_val)
slow_val = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_x_fisher_org_v1_strategy()