Estrategia Color Ma RSI Trigger Duplex
Esta estrategia porta el asesor experto Exp_ColorMaRsi-Trigger_Duplex.mq5 a la API de alto nivel de StockSharp.
Maneja dos detectores MaRsi-Trigger independientes: el bloque largo decide cuándo se deben abrir o cerrar posiciones largas, mientras que el bloque corto realiza la misma tarea para posiciones cortas. Cada detector evalúa si un indicador personalizado reporta presión de mercado alcista (+1), neutral (0) o bajista (-1). La lógica original de MetaTrader se conserva, incluyendo la confirmación retardada que espera dos barras completadas antes de reaccionar y la configuración de gestión de dinero separada por dirección.
Idea de trading
- Calcular dos medias móviles exponenciales (rápida y lenta) y dos osciladores RSI (rápido y lento) en una serie de velas seleccionable para cada bloque.
- En cada vela finalizada el indicador devuelve
+1 cuando ambos estudios rápidos dominan a sus contrapartes lentas, -1 cuando ambos son más débiles y 0 en caso contrario. El valor bruto se limita al rango [-1, 1] como en el indicador MT5.
- La estrategia almacena un historial continuo de valores del indicador. Para un desplazamiento
SignalBar configurado, compara el valor de la barra SignalBar + 1 períodos atrás (llamado older) con el valor de la barra SignalBar períodos atrás (llamado recent).
- Lógica larga:
- Si
older < 0 el bloque largo cierra cualquier posición larga activa (siempre que las salidas largas estén habilitadas).
- Si
older > 0 y recent <= 0 el bloque largo prepara una nueva entrada larga (siempre que las entradas largas estén habilitadas).
- La lógica corta refleja el bloque largo:
- Si
older > 0 el bloque corto sale de posiciones cortas existentes (cuando las salidas cortas están habilitadas).
- Si
older < 0 y recent >= 0 el bloque abre una nueva posición corta (cuando las entradas cortas están habilitadas).
- Niveles opcionales de stop-loss y take-profit, expresados en pasos de precio del instrumento, cierran posiciones cuando el precio cruza los niveles configurados.
Los dos bloques pueden suscribirse a diferentes marcos temporales de velas y fuentes de precio, permitiendo al usuario replicar el comportamiento dual de marcos temporales original o experimentar con combinaciones alternativas.
Parámetros
| Parámetro |
Descripción |
LongCandleType, ShortCandleType |
Series de datos de velas usadas por los bloques largo y corto. Por defecto velas de 4 horas. |
LongVolume, ShortVolume |
Volumen de mercado negociado cuando el bloque correspondiente abre una nueva posición. |
LongAllowOpen, ShortAllowOpen |
Habilitar o deshabilitar la apertura de nuevas posiciones para cada bloque. |
LongAllowClose, ShortAllowClose |
Habilitar o deshabilitar las señales de cierre para cada bloque. |
LongStopLossPoints, ShortStopLossPoints |
Distancia de stop-loss medida en pasos de precio. Establecer en 0 para deshabilitar. |
LongTakeProfitPoints, ShortTakeProfitPoints |
Distancia de take-profit medida en pasos de precio. Establecer en 0 para deshabilitar. |
LongSignalBar, ShortSignalBar |
Número de barras completadas entre la vela actual y la usada para la lógica de decisión. |
LongRsiPeriod, LongRsiLongPeriod, ShortRsiPeriod, ShortRsiLongPeriod |
Longitudes de los osciladores RSI rápido y lento. |
LongMaPeriod, LongMaLongPeriod, ShortMaPeriod, ShortMaLongPeriod |
Longitudes de las medias móviles rápida y lenta. |
LongRsiPrice, ShortRsiPrice |
Fuente de precio alimentada al RSI rápido (cierre, apertura, máximo, mínimo, mediana, típico o ponderado). |
LongRsiLongPrice, ShortRsiLongPrice |
Fuente de precio alimentada al RSI lento. |
LongMaPrice, ShortMaPrice |
Fuente de precio alimentada a la media móvil rápida. |
LongMaLongPrice, ShortMaLongPrice |
Fuente de precio alimentada a la media móvil lenta. |
LongMaType, ShortMaType |
Método de media móvil para la línea rápida (simple, exponencial, suavizada o ponderada). |
LongMaLongType, ShortMaLongType |
Método de media móvil para la línea lenta. |
Reglas de trading
- Esperar hasta que la serie de velas seleccionada produzca barras terminadas y todos los indicadores estén completamente calentados.
- Para cada bloque calcular el valor MaRsi-Trigger y actualizar el búfer de historial.
- Cuando el historial contiene al menos
SignalBar + 2 entradas, evaluar las condiciones largas y cortas descritas en la sección de idea de trading.
- Antes de abrir una posición la estrategia neutralizará cualquier exposición opuesta (si la bandera de cierre correspondiente está habilitada). Por ejemplo, una nueva entrada larga comprará suficiente volumen para cerrar una posición corta y solo entonces añadirá el volumen largo.
- Después de que se abre una posición, los niveles opcionales de stop-loss y take-profit se monitorean en cada vela finalizada.
- Las órdenes de apertura y cierre se envían como órdenes de mercado a través de los helpers de alto nivel
BuyMarket y SellMarket.
Gestión de riesgo
- Los stops y objetivos se miden usando
Security.PriceStep. Cuando el instrumento no expone un paso de precio, se asume un valor predeterminado de 1, coincidiendo con el comportamiento de muchas estrategias existentes en este repositorio.
- Los bloques largo y corto mantienen configuraciones independientes de stop y take.
- La estrategia no coloca órdenes protectoras adicionales (como stops de seguimiento); el comportamiento refleja el experto MT5, que solo cierra operaciones cuando el indicador se activa o cuando se alcanza el stop/objetivo duro.
Notas
- El port de StockSharp emite órdenes de mercado inmediatamente después de que la vela de evaluación finaliza. En MetaTrader el experto programaba sus órdenes para el tiempo de apertura de la siguiente barra mediante desplazamientos de marca de tiempo; ambos comportamientos se alinean efectivamente porque StockSharp procesa la señal en cuanto el vela se cierra.
- El EA original exponía varios modos de gestión de dinero (
LOT, BALANCE, etc.). Las estrategias de StockSharp trabajan con valores de volumen directos, por lo tanto el port mantiene el volumen como parámetro directo (LongVolume/ShortVolume).
- El slippage y la lógica específica del número mágico de la librería auxiliar MT5 no son necesarios en StockSharp y han sido omitidos.
- Los cálculos del indicador aprovechan las implementaciones integradas de StockSharp de medias móviles y RSI; la salida se limita a
[-1, 1] para coincidir con el indicador original ColorMaRsi-Trigger.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Recreates the Exp_ColorMaRsi-Trigger_Duplex expert advisor.
/// Combines fast/slow MA and fast/slow RSI comparisons into a color code (+1/0/-1).
/// Trades based on color code transitions.
/// </summary>
public class ColorMaRsiTriggerDuplexStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<int> _fastRsiPeriod;
private readonly StrategyParam<int> _slowRsiPeriod;
private readonly StrategyParam<int> _signalBar;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly List<decimal> _colorHistory = new();
private int _cooldownRemaining;
/// <summary>Candle type.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>Fast MA period.</summary>
public int FastMaPeriod { get => _fastMaPeriod.Value; set => _fastMaPeriod.Value = value; }
/// <summary>Slow MA period.</summary>
public int SlowMaPeriod { get => _slowMaPeriod.Value; set => _slowMaPeriod.Value = value; }
/// <summary>Fast RSI period.</summary>
public int FastRsiPeriod { get => _fastRsiPeriod.Value; set => _fastRsiPeriod.Value = value; }
/// <summary>Slow RSI period.</summary>
public int SlowRsiPeriod { get => _slowRsiPeriod.Value; set => _slowRsiPeriod.Value = value; }
/// <summary>Signal bar shift.</summary>
public int SignalBar { get => _signalBar.Value; set => _signalBar.Value = value; }
/// <summary>Bars to wait between reversals.</summary>
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public ColorMaRsiTriggerDuplexStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
_fastMaPeriod = Param(nameof(FastMaPeriod), 5)
.SetDisplay("Fast MA Period", "Fast moving average length", "Indicators");
_slowMaPeriod = Param(nameof(SlowMaPeriod), 10)
.SetDisplay("Slow MA Period", "Slow moving average length", "Indicators");
_fastRsiPeriod = Param(nameof(FastRsiPeriod), 3)
.SetDisplay("Fast RSI Period", "Fast RSI length", "Indicators");
_slowRsiPeriod = Param(nameof(SlowRsiPeriod), 13)
.SetDisplay("Slow RSI Period", "Slow RSI length", "Indicators");
_signalBar = Param(nameof(SignalBar), 1)
.SetDisplay("Signal Bar", "History shift for signal evaluation", "Strategy");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 8)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between reversals", "Strategy");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
lock (_colorHistory)
_colorHistory.Clear();
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
lock (_colorHistory)
_colorHistory.Clear();
_cooldownRemaining = 0;
var fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
var slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
var fastRsi = new RelativeStrengthIndex { Length = FastRsiPeriod };
var slowRsi = new RelativeStrengthIndex { Length = SlowRsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastMa, slowMa, fastRsi, slowRsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastMaVal, decimal slowMaVal, decimal fastRsiVal, decimal slowRsiVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
// Calculate color code from MA and RSI comparisons
var score = 0m;
if (fastMaVal > slowMaVal)
score = 1m;
else if (fastMaVal < slowMaVal)
score = -1m;
if (fastRsiVal > slowRsiVal)
score += 1m;
else if (fastRsiVal < slowRsiVal)
score -= 1m;
// Clamp to [-1, 1]
if (score > 1m) score = 1m;
else if (score < -1m) score = -1m;
decimal recent;
decimal older;
lock (_colorHistory)
{
_colorHistory.Insert(0, score);
var maxHistory = Math.Max(2, SignalBar + 2);
while (_colorHistory.Count > maxHistory)
_colorHistory.RemoveAt(_colorHistory.Count - 1);
if (_colorHistory.Count <= SignalBar + 1)
return;
recent = _colorHistory[SignalBar];
older = _colorHistory[SignalBar + 1];
}
var longOpen = older == -1m && recent == 1m;
var shortOpen = older == 1m && recent == -1m;
var longExit = Position > 0 && recent < 0m;
var shortExit = Position < 0 && recent > 0m;
if (longExit)
{
SellMarket(Position);
_cooldownRemaining = SignalCooldownBars;
}
else if (shortExit)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = SignalCooldownBars;
}
else if (_cooldownRemaining == 0 && longOpen && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_cooldownRemaining = SignalCooldownBars;
}
else if (_cooldownRemaining == 0 && shortOpen && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_cooldownRemaining = SignalCooldownBars;
}
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class color_ma_rsi_trigger_duplex_strategy(Strategy):
def __init__(self):
super(color_ma_rsi_trigger_duplex_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._fast_ma_period = self.Param("FastMaPeriod", 5) \
.SetDisplay("Fast MA Period", "Fast moving average length", "Indicators")
self._slow_ma_period = self.Param("SlowMaPeriod", 10) \
.SetDisplay("Slow MA Period", "Slow moving average length", "Indicators")
self._fast_rsi_period = self.Param("FastRsiPeriod", 3) \
.SetDisplay("Fast RSI Period", "Fast RSI length", "Indicators")
self._slow_rsi_period = self.Param("SlowRsiPeriod", 13) \
.SetDisplay("Slow RSI Period", "Slow RSI length", "Indicators")
self._signal_bar = self.Param("SignalBar", 1) \
.SetDisplay("Signal Bar", "History shift for signal evaluation", "Strategy")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 8) \
.SetDisplay("Signal Cooldown", "Bars to wait between reversals", "Strategy")
self._color_history = []
self._cooldown_remaining = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastMaPeriod(self):
return self._fast_ma_period.Value
@property
def SlowMaPeriod(self):
return self._slow_ma_period.Value
@property
def FastRsiPeriod(self):
return self._fast_rsi_period.Value
@property
def SlowRsiPeriod(self):
return self._slow_rsi_period.Value
@property
def SignalBar(self):
return self._signal_bar.Value
@property
def SignalCooldownBars(self):
return self._signal_cooldown_bars.Value
def OnReseted(self):
super(color_ma_rsi_trigger_duplex_strategy, self).OnReseted()
self._color_history = []
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(color_ma_rsi_trigger_duplex_strategy, self).OnStarted2(time)
self._color_history = []
self._cooldown_remaining = 0
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self.FastMaPeriod
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self.SlowMaPeriod
fast_rsi = RelativeStrengthIndex()
fast_rsi.Length = self.FastRsiPeriod
slow_rsi = RelativeStrengthIndex()
slow_rsi.Length = self.SlowRsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ma, slow_ma, fast_rsi, slow_rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_ma_val, slow_ma_val, fast_rsi_val, slow_rsi_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
fmv = float(fast_ma_val)
smv = float(slow_ma_val)
frv = float(fast_rsi_val)
srv = float(slow_rsi_val)
score = 0.0
if fmv > smv:
score = 1.0
elif fmv < smv:
score = -1.0
if frv > srv:
score += 1.0
elif frv < srv:
score -= 1.0
if score > 1.0:
score = 1.0
elif score < -1.0:
score = -1.0
self._color_history.insert(0, score)
max_history = max(2, self.SignalBar + 2)
while len(self._color_history) > max_history:
self._color_history.pop()
if len(self._color_history) <= self.SignalBar + 1:
return
recent = self._color_history[self.SignalBar]
older = self._color_history[self.SignalBar + 1]
long_open = older == -1.0 and recent == 1.0
short_open = older == 1.0 and recent == -1.0
long_exit = self.Position > 0 and recent < 0.0
short_exit = self.Position < 0 and recent > 0.0
if long_exit:
self.SellMarket(self.Position)
self._cooldown_remaining = self.SignalCooldownBars
elif short_exit:
self.BuyMarket(abs(self.Position))
self._cooldown_remaining = self.SignalCooldownBars
elif self._cooldown_remaining == 0 and long_open and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._cooldown_remaining = self.SignalCooldownBars
elif self._cooldown_remaining == 0 and short_open and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._cooldown_remaining = self.SignalCooldownBars
def CreateClone(self):
return color_ma_rsi_trigger_duplex_strategy()