Esta estrategia replica el experto MetaTrader 5 "Señal universal" utilizando StockSharp API de alto nivel. Evalúa ocho patrones de mercado ponderados y los agrega en una única puntuación compuesta. Cuando la puntuación cruza umbrales configurables, la estrategia abre o cierra posiciones largas y cortas, utilizando opcionalmente órdenes de límite pendientes que vencen después de un número determinado de barras.
Parámetros de estrategia
CandleType: datos de velas utilizados para el análisis.
SignalThresholdOpen: puntuación compuesta mínima requerida para abrir una posición.
SignalThresholdClose: puntuación contraria necesaria para salir de una posición existente.
PriceLevel – compensación de precio para colocar entradas de límite pendientes (0 significa ejecución de mercado).
StopLevel / TakeLevel: distancias absolutas de stop-loss y take-profit utilizadas por el módulo de protección integrado.
SignalExpiration – número de barras después de las cuales se cancelan las entradas pendientes aún activas.
Pattern0Weight… Pattern7Weight: peso aplicado a cada patrón antes de la agregación.
UniversalWeight: multiplicador final aplicado a la suma de todas las contribuciones del patrón.
ShortMaPeriod, LongMaPeriod, RsiPeriod, BollingerPeriod, BollingerWidth, TrendSmaPeriod, VolumeSmaPeriod: configuraciones de indicadores utilizadas dentro de las comprobaciones de patrones.
Lógica de trading
Suscríbase al flujo de velas configurado y vincule EMA, RSI, MACD señal, Bollinger bandas y SMA de soporte.
Después de cada vela terminada, calcule ocho patrones booleanos (alineación de tendencia, RSI impulso, MACD histograma, Bollinger posicionamiento, dirección de la vela y expansión de volumen).
Multiplica cada patrón por su peso, suma las contribuciones y aplica el peso global para obtener la puntuación final.
Cierre las posiciones abiertas cuando la puntuación cruce el umbral de cierre en la dirección opuesta.
Abra nuevas posiciones largas o cortas cuando la puntuación supere el umbral de apertura. Si PriceLevel es positivo, envíe una orden limitada compensada por la distancia configurada y cancélela automáticamente después de SignalExpiration barras.
StartProtection establece niveles fijos de stop-loss y take-profit para todas las posiciones utilizando los asistentes de gestión de riesgos de StockSharp.
La conversión mantiene el flujo de trabajo de ponderación flexible del experto MQL5 original mientras sigue las convenciones de codificación StockSharp y el procesamiento basado en 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()