Стратегия повторяет эксперта MetaTrader 5 «Universal Signal», используя высокоуровневые API StockSharp. Восемь взвешенных рыночных паттернов объединяются в единый интегральный балл. Когда балл превышает заданные пороги, стратегия открывает или закрывает длинные и короткие позиции, при необходимости размещая отложенные лимитные заявки с ограниченным временем жизни.
Параметры стратегии
CandleType – тип свечей, используемых в расчётах.
SignalThresholdOpen – минимальный интегральный балл для открытия позиции.
SignalThresholdClose – противоположный балл, необходимый для закрытия позиции.
PriceLevel – смещение цены для отложенных лимитных заявок (0 – исполнение по рынку).
Подписка на поток выбранных свечей и привязка EMA, RSI, MACD Signal, полос Боллинджера и дополнительных SMA.
После формирования каждой свечи вычисляются восемь булевых паттернов: направленность тренда, импульс RSI, гистограмма MACD, расположение относительно средней полосы, направление свечи и расширение объёма.
Каждый паттерн умножается на свой вес, затем суммы масштабируются глобальным весом.
Позиции закрываются, когда балл пересекает порог закрытия в противоположную сторону.
Новые позиции открываются при превышении порога открытия. Если PriceLevel больше нуля, подаётся лимитная заявка со смещением и автоматической отменой через SignalExpiration баров.
Метод StartProtection устанавливает фиксированные стоп-лосс и тейк-профит для всех сделок, используя стандартные инструменты управления рисками StockSharp.
Такой перенос сохраняет гибкость оригинального эксперта MQL5 и при этом соответствует соглашениям StockSharp и индикаторному подходу.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Universal Signal Demo strategy: Multi-indicator scoring.
/// Combines RSI and EMA signals to generate aggregate score for entries.
/// </summary>
public class UniversalSignalDemoStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _signalCooldownCandles;
private int _prevScore;
private int _candlesSinceTrade;
private bool _hasPrevScore;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public UniversalSignalDemoStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevScore = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrevScore = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevScore = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrevScore = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
var close = candle.ClosePrice;
var score = 0;
// RSI signal
if (rsiValue < 30) score += 2;
else if (rsiValue < 45) score += 1;
else if (rsiValue > 70) score -= 2;
else if (rsiValue > 55) score -= 1;
// EMA signal
if (close > emaValue) score += 1;
else if (close < emaValue) score -= 1;
// Candle direction
if (candle.ClosePrice > candle.OpenPrice) score += 1;
else if (candle.ClosePrice < candle.OpenPrice) score -= 1;
var crossedUp = (!_hasPrevScore || _prevScore < 2) && score >= 2;
var crossedDown = (!_hasPrevScore || _prevScore > -2) && score <= -2;
if (crossedUp && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (crossedDown && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
_prevScore = score;
_hasPrevScore = true;
}
}
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, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class universal_signal_demo_strategy(Strategy):
def __init__(self):
super(universal_signal_demo_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._rsi_period = self.Param("RsiPeriod", 21)
self._ema_period = self.Param("EmaPeriod", 50)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._prev_score = 0
self._candles_since_trade = 4
self._has_prev_score = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(universal_signal_demo_strategy, self).OnReseted()
self._prev_score = 0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev_score = False
def OnStarted2(self, time):
super(universal_signal_demo_strategy, self).OnStarted2(time)
self._prev_score = 0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev_score = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, ema, self._process_candle).Start()
def _process_candle(self, candle, rsi_value, ema_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
close = float(candle.ClosePrice)
rsi_val = float(rsi_value)
ema_val = float(ema_value)
score = 0
# RSI signal
if rsi_val < 30:
score += 2
elif rsi_val < 45:
score += 1
elif rsi_val > 70:
score -= 2
elif rsi_val > 55:
score -= 1
# EMA signal
if close > ema_val:
score += 1
elif close < ema_val:
score -= 1
# Candle direction
if float(candle.ClosePrice) > float(candle.OpenPrice):
score += 1
elif float(candle.ClosePrice) < float(candle.OpenPrice):
score -= 1
crossed_up = (not self._has_prev_score or self._prev_score < 2) and score >= 2
crossed_down = (not self._has_prev_score or self._prev_score > -2) and score <= -2
if crossed_up and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif crossed_down and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_score = score
self._has_prev_score = True
def CreateClone(self):
return universal_signal_demo_strategy()