EMA LWMA RSI Strategy переносит советник MetaTrader «EMA LWMA RSI» на платформу StockSharp. Стратегия сравнивает две скользящие средние, использующие одинаковый тип цены и при необходимости смещение вперёд, а также подтверждает импульс фильтром RSI. Алгоритм реагирует только на полностью сформированные свечи выбранного таймфрейма и работает с одной совокупной позицией: перед открытием новой сделки в противоположную сторону он закрывает текущую позицию. Расстояния стоп-лосса и тейк-профита задаются в пунктах и автоматически масштабируются относительно шага цены инструмента.
Логика торговли
Рассчитываются экспоненциальная (EMA) и линейно-взвешенная (LWMA) скользящие средние с отдельными периодами, но общим типом цены. Если MaShift больше нуля, обе средние сдвигаются вперёд на указанное количество баров, полностью повторяя параметр shift из MetaTrader.
RSI рассчитывается по собственному типу цены. Классический порог 50 отделяет бычий импульс от медвежьего.
После закрытия свечи:
Покупка активируется, когда EMA переходит выше LWMA (предыдущее значение EMA было больше LWMA, а текущее стало ниже) и значение RSI находится выше 50.
Продажа активируется, когда EMA переходит ниже LWMA (предыдущее значение EMA было ниже LWMA, а текущее стало выше) и RSI опускается ниже 50.
Сигналы выставляют внутренние флаги ожидания. Перед разворотом стратегия сначала вызывает ClosePosition(), чтобы ликвидировать открытую позицию. После подтверждения сделки незамедлительно отправляется рыночный ордер в требуемом направлении. Такой механизм соответствует оригинальному советнику, который ждал подтверждения транзакции.
Защитные ордера запускаются через StartProtection. Если стоп-лосс или тейк-профит отключён (значение равно нулю), соответствующая нога пропускается — поведение полностью повторяет MQL.
Особенности реализации
Выбор типа цены повторяет перечисление MetaTrader (Close, Open, High, Low, Median, Typical, Weighted, Average). Взвешенная цена вычисляется как (High + Low + 2 * Close) / 4, что соответствует PRICE_WEIGHTED.
Размер «пункта» определяется автоматически: при 3- и 5-знаковых форекс-символах шаг цены умножается на 10, чтобы один pip соответствовал 10 пунктам котировки.
Для подписки на свечи используется высокоуровневый API StockSharp. Смещение реализовано через индикаторы Shift, без ручного доступа к буферам.
Логика удерживает булевы флаги ожидающих покупок/продаж. Они предотвращают повторные заявки, пока предыдущий ордер в работе, и очищаются после сделок или когда позиция уже соответствует сигналу.
На ценовой панели отображаются свечи и обе скользящие, а RSI рисуется на отдельной области графика.
Параметры
Параметр
Тип
Значение по умолчанию
Описание
CandleType
DataType
Таймфрейм 1 час
Серия свечей, которую анализирует стратегия.
StopLossPips
int
150
Дистанция стоп-лосса в пунктах. 0 — отключить.
TakeProfitPips
int
150
Дистанция тейк-профита в пунктах. 0 — отключить.
EmaPeriod
int
28
Период экспоненциальной средней.
LwmaPeriod
int
8
Период линейно-взвешенной средней.
MaShift
int
0
Смещение (в барах), применяемое к обеим средним.
RsiPeriod
int
14
Период усреднения RSI.
MaAppliedPrice
AppliedPriceType
Weighted
Тип цены для EMA и LWMA.
RsiAppliedPrice
AppliedPriceType
Weighted
Тип цены для RSI.
Использование
Привяжите стратегию к нужному инструменту и задайте CandleType, совпадающий с таймфреймом оригинального советника.
При необходимости скорректируйте защитные дистанции в пунктах и параметры индикаторов под условия брокера.
После запуска подписки включите торговлю. Стратегия всегда поддерживает только одну позицию и использует ClosePosition() перед сменой направления.
Python-версия стратегии пока отсутствует.
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;
public class EmaLwmaRsiStrategy : 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;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public EmaLwmaRsiStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).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");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
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;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_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 ema_lwma_rsi_strategy(Strategy):
def __init__(self):
super(ema_lwma_rsi_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA 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._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(ema_lwma_rsi_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(ema_lwma_rsi_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
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 = 100
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 = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return ema_lwma_rsi_strategy()