Wajdyss MA Expert — это порт на C# советника MetaTrader 4 «wajdyss MA expert v3». Стратегия сравнивает две скользящие средние с независимыми периодами, методами расчёта, сдвигами и типами цен. Пересечение быстрой средней над медленной открывает длинную позицию, обратное пересечение — короткую. Реализация повторяет исходное управление капиталом, опциональное автоматическое закрытие встречных сделок и фильтры принудительного закрытия в конце дня и недели.
Логика торговли
Подписаться на выбранный CandleType (по умолчанию 15-минутные свечи) и рассчитать быстрый и медленный мувинги с указанными параметрами MovingAverageMethod и PriceSource для каждой линии.
Хранить значения индикаторов только для завершённых свечей. Формировать бычий сигнал, когда быстрая средняя (с учётом её Shift) находится выше медленной на последней закрытой свече и ниже две свечи назад; для медвежьего сигнала условия обратные.
Между сделками одного направления выдерживать паузу не менее одной свечи выбранного таймфрейма — аналог MT4-версии, где использовались глобальные переменные времени.
При включённом AutoCloseOpposite сначала отменять активные заявки и разворачивать позицию одной рыночной сделкой. Новый объём включает текущую противоположную позицию, что обеспечивает мгновенный разворот.
Применять дневной и пятничный фильтры. После наступления DailyCloseHour/DailyCloseMinute или FridayCloseHour/FridayCloseMinute все позиции закрываются, новые сделки не открываются до следующей сессии.
Риск-менеджмент и объём
Параметры TakeProfitPips, StopLossPips и TrailingStopPips задаются в целых пунктах. Они автоматически переводятся в ценовые шаги инструмента и передаются в StartProtection, чтобы повторить поведение стопов и трейлинга оригинального советника.
Включённый UseMoneyManagement воспроизводит формулу MT4: объём = (баланс / BalanceReference) * InitialVolume. Далее объём приводится к шагу и попадает в диапазон между минимально и максимально допустимыми значениями инструмента.
При отключённом управлении капиталом используется фиксированный объём InitialVolume.
Параметры
Параметр
Тип
Значение по умолчанию
Описание
FastPeriod
int
10
Период быстрой скользящей средней.
FastShift
int
0
Сдвиг быстрой средней при сравнении значений.
FastMethod
MovingAverageMethod
Ema
Метод сглаживания быстрой средней (Sma, Ema, Smma, Lwma).
FastPriceType
PriceSource
Close
Тип цены для быстрой средней (Close, Open, High, Low, Median, Typical, Weighted).
SlowPeriod
int
20
Период медленной скользящей средней.
SlowShift
int
0
Сдвиг медленной средней.
SlowMethod
MovingAverageMethod
Ema
Метод сглаживания медленной средней.
SlowPriceType
PriceSource
Close
Тип цены для медленной средней.
TakeProfitPips
decimal
100
Дистанция до тейк-профита в пунктах (0 — отключено).
StopLossPips
decimal
50
Дистанция до стоп-лосса в пунктах (0 — отключено).
TrailingStopPips
decimal
0
Ширина трейлинг-стопа в пунктах (0 — отключено).
AutoCloseOpposite
bool
true
Закрывать противоположные позиции перед открытием новой.
InitialVolume
decimal
0.1
Базовый объём сделки до применения мани-менеджмента.
UseMoneyManagement
bool
true
Включить динамический расчёт объёма от баланса.
BalanceReference
decimal
1000
Делитель баланса при расчёте динамического объёма.
DailyCloseHour
int
23
Час (0-23), после которого позиции закрываются каждый день.
DailyCloseMinute
int
45
Минута ежедневного закрытия.
FridayCloseHour
int
22
Час окончания торговли в пятницу (0-23).
FridayCloseMinute
int
45
Минута окончания торговли в пятницу.
CandleType
DataType
Таймфрейм 15 минут
Свечи, используемые для расчётов и контроля паузы между сделками.
Примечания
Используется только высокоуровневый API StockSharp: свечи приходят через SubscribeCandles, индикаторы связаны напрямую, а StartProtection управляет стопами, тейками и трейлингом.
Позиции закрываются рыночными заявками, что повторяет немедленное закрытие ордеров в версии MT4.
Python-реализация отсутствует; в каталоге представлена только C# стратегия.
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()