Esta estratégia replica o especialista MetaTrader 5 "Universal Signal" usando StockSharp APIs de alto nível. Avalia oito padrões de mercado ponderados e os agrega em uma única pontuação composta. Quando a pontuação ultrapassa limites configuráveis, a estratégia abre ou fecha posições longas e curtas, opcionalmente usando ordens de limite pendentes que expiram após um determinado número de barras.
Parâmetros de Estratégia
CandleType – dados de velas usados para a análise.
SignalThresholdOpen – pontuação composta mínima necessária para abrir uma posição.
SignalThresholdClose – pontuação adversária necessária para sair de uma posição existente.
PriceLevel – compensação de preço para colocação de entradas de limite pendentes (0 significa execução de mercado).
StopLevel / TakeLevel – distâncias absolutas de stop-loss e take-profit usadas pelo módulo de proteção integrado.
SignalExpiration – número de barras após as quais as entradas pendentes ainda ativas são canceladas.
Pattern0Weight… Pattern7Weight – peso aplicado a cada padrão antes da agregação.
UniversalWeight – multiplicador final aplicado à soma de todas as contribuições do padrão.
ShortMaPeriod, LongMaPeriod, RsiPeriod, BollingerPeriod, BollingerWidth, TrendSmaPeriod, VolumeSmaPeriod – configurações do indicador usadas nas verificações de padrão.
Lógica de negociação
Assine o fluxo de vela configurado e vincule EMA, RSI, MACD sinal, Bollinger bandas e SMAs de suporte.
Após cada vela concluída, calcule oito padrões booleanos (alinhamento de tendência, RSI impulso, MACD histograma, Bollinger posicionamento, direção da vela e expansão de volume).
Multiplique cada padrão pelo seu peso, some as contribuições e aplique o peso global para obter a pontuação final.
Feche as posições abertas quando a pontuação ultrapassar o limite de fechamento na direção oposta.
Abra novas posições longas ou curtas quando a pontuação exceder o limite de abertura. Se PriceLevel for positivo, envie uma ordem de limite compensada pela distância configurada e cancele-a automaticamente após SignalExpiration barras.
StartProtection define níveis fixos de stop-loss e take-profit para todas as posições usando os auxiliares de gerenciamento de risco de StockSharp.
A conversão mantém o fluxo de trabalho de ponderação flexível do especialista MQL5 original, ao mesmo tempo que segue as convenções de codificação StockSharp e o processamento baseado em indicadores.
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()