The Wajdyss MA Expert Strategy is a C# port of the MetaTrader 4 expert advisor "wajdyss MA expert v3". It compares two moving averages configured with independent periods, calculation modes, shifts, and applied prices. A bullish crossover of the fast average above the slow average opens long exposure, while a bearish crossover opens short exposure. The conversion reproduces the original money-management rules, optional automatic closing of opposing trades, and end-of-day / end-of-week liquidation filters.
Trading Logic
Subscribe to the selected CandleType (15-minute candles by default) and calculate the fast and slow moving averages using the chosen MovingAverageMethod and PriceSource settings for each leg.
Store the indicator values for finished candles. Evaluate a bullish signal when the fast average (with its configured shift) is above the slow average on the last closed bar while it was below two bars ago. Evaluate a bearish signal with the inverse condition.
Enforce a cooldown between new entries of the same direction. The strategy must wait at least one full candle of the subscribed timeframe after the last trade of that side, mirroring the global-variable timing guard from the MT4 version.
When AutoCloseOpposite is enabled, cancel working orders and reverse exposure in a single market order: the new order volume includes any outstanding position in the opposite direction so the account flips immediately.
Apply daily and Friday closing filters. After the configured DailyCloseHour/DailyCloseMinute or FridayCloseHour/FridayCloseMinute, all positions are flattened and new trades are blocked until the next session.
Risk & Money Management
TakeProfitPips, StopLossPips, and TrailingStopPips are interpreted in whole pips. The implementation converts them to price steps using the security metadata and drives StockSharp's StartProtection engine with market exits for parity with the original trailing logic.
UseMoneyManagement emulates the MT4 lot calculation: volume = (account_balance / BalanceReference) * InitialVolume. Exchange limits are respected via volume step, minimum, and maximum checks.
If money management is disabled, orders use InitialVolume directly.
Parameters
Parameter
Type
Default
Description
FastPeriod
int
10
Period of the fast moving average.
FastShift
int
0
Bars to shift the fast average before comparing crossover values.
FastMethod
MovingAverageMethod
Ema
Moving average mode for the fast line (Sma, Ema, Smma, Lwma).
FastPriceType
PriceSource
Close
Candle price fed into the fast moving average (Close, Open, High, Low, Median, Typical, Weighted).
SlowPeriod
int
20
Period of the slow moving average.
SlowShift
int
0
Bars to shift the slow average before comparison.
SlowMethod
MovingAverageMethod
Ema
Moving average mode for the slow line.
SlowPriceType
PriceSource
Close
Candle price fed into the slow average.
TakeProfitPips
decimal
100
Distance to the profit target in pips (set to 0 to disable).
StopLossPips
decimal
50
Distance to the protective stop in pips (set to 0 to disable).
TrailingStopPips
decimal
0
Trailing stop distance in pips (set to 0 to disable).
AutoCloseOpposite
bool
true
Close opposing exposure before opening a new trade in the other direction.
InitialVolume
decimal
0.1
Base trade volume before applying money management.
UseMoneyManagement
bool
true
Enable balance-based position sizing.
BalanceReference
decimal
1000
Divisor used when scaling volume with the account balance.
DailyCloseHour
int
23
Hour (0-23) after which daily positions are closed.
DailyCloseMinute
int
45
Minute component of the daily close filter.
FridayCloseHour
int
22
Hour (0-23) after which Friday trading stops.
FridayCloseMinute
int
45
Minute component of the Friday close filter.
CandleType
DataType
15m time frame
Candle series used for calculations and cooldown timing.
Notes
The strategy relies exclusively on the high-level StockSharp API: candles are processed through SubscribeCandles, indicator bindings feed moving averages, and StartProtection manages stops/take-profit/trailing orders.
Position flattening uses market orders to mirror the MT4 expert's immediate closures of opposite tickets.
No Python translation is included in this folder; only the C# implementation is provided.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Wajdyss MA Expert: Fast/slow EMA crossover with ATR stops.
/// </summary>
public class WajdyssMaExpertStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public WajdyssMaExpertStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastLength = Param(nameof(FastLength), 10)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowLength = Param(nameof(SlowLength), 20)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
var fast = new ExponentialMovingAverage { Length = FastLength };
var slow = new ExponentialMovingAverage { Length = SlowLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, atr, 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, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var close = candle.ClosePrice;
// Exit
if (Position > 0)
{
if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m || fastVal < slowVal)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m || fastVal > slowVal)
{
BuyMarket();
_entryPrice = 0;
}
}
// Entry: EMA crossover
if (Position == 0)
{
if (_prevFast <= _prevSlow && fastVal > slowVal)
{
_entryPrice = close;
BuyMarket();
}
else if (_prevFast >= _prevSlow && fastVal < slowVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevFast = fastVal;
_prevSlow = slowVal;
}
}
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 ExponentialMovingAverage, AverageTrueRange
class wajdyss_ma_expert_strategy(Strategy):
def __init__(self):
super(wajdyss_ma_expert_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_length = self.Param("FastLength", 10) \
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators")
self._slow_length = self.Param("SlowLength", 20) \
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period for stops.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastLength(self):
return self._fast_length.Value
@property
def SlowLength(self):
return self._slow_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(wajdyss_ma_expert_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast = ExponentialMovingAverage()
self._fast.Length = self.FastLength
self._slow = ExponentialMovingAverage()
self._slow.Length = self.SlowLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast, self._slow, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
# Exit
if self.Position > 0:
if close <= self._entry_price - av * 2.0 or close >= self._entry_price + av * 3.0 or fv < sv:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close >= self._entry_price + av * 2.0 or close <= self._entry_price - av * 3.0 or fv > sv:
self.BuyMarket()
self._entry_price = 0.0
# Entry: EMA crossover
if self.Position == 0:
if self._prev_fast <= self._prev_slow and fv > sv:
self._entry_price = close
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fv < sv:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(wajdyss_ma_expert_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return wajdyss_ma_expert_strategy()