Estrategia Hybrid Scalper
Descripción general
La Estrategia Hybrid Scalper es un algoritmo de trading a corto plazo convertido desde el script MQL4 hybrid_Scalper.mq4. Opera sobre la API de alto nivel de StockSharp y está diseñada para el marco temporal de 1 minuto. La estrategia combina múltiples indicadores técnicos para identificar oportunidades de ruptura rápida, evitando al mismo tiempo periodos de volatilidad excesiva o insuficiente.
Lógica de la estrategia
- Filtro de tendencia – Una EMA rápida (21) y una EMA lenta (89) determinan la dirección del mercado. Las operaciones largas solo se permiten cuando la EMA rápida está por encima de la EMA lenta; las operaciones cortas requieren la condición opuesta.
- Filtro de momentum – El Oscilador Estocástico (5,3,3) genera señales de entrada. Se activa una compra cuando %K está por debajo de 20 y por debajo de %D. Se activa una venta cuando %K está por encima de 80 y sigue por encima de %D.
- Confirmación RSI – El Índice de Fuerza Relativa con período 7 confirma el momentum. Las entradas largas requieren RSI por debajo de 25, mientras que las entradas cortas requieren RSI por encima de 85.
- Filtro de volatilidad – Las Bandas de Bollinger (50, desviación 4) miden el ancho actual del mercado. La estrategia opera solo cuando la diferencia entre las bandas superior e inferior está entre 0.00045 y 0.00262, evitando tanto mercados tranquilos como inestables.
- Días de trading – Los parámetros permiten habilitar o deshabilitar el trading para cada día de la semana de forma individual (lunes–viernes).
Parámetros
| Nombre | Descripción |
|---|---|
RsiPeriod |
Período del indicador RSI. |
EmaFastPeriod |
Período de la EMA rápida para la detección de tendencia. |
EmaSlowPeriod |
Período de la EMA lenta para la detección de tendencia. |
BbPeriod |
Período utilizado en las Bandas de Bollinger. |
BbDeviation |
Multiplicador de desviación para las Bandas de Bollinger. |
TradeMonday–TradeFriday |
Habilitar el trading en días de la semana específicos. |
CandleType |
Tipo de vela/marco temporal, por defecto velas de 1 minuto. |
Notas
- La estrategia utiliza la API de alto nivel
BindExpara conectar múltiples indicadores en una sola suscripción. StartProtection()se invoca una vez al inicio para activar la protección de posición integrada (sin parámetros explícitos de stop-loss o take-profit).- Todos los comentarios en el código se proporcionan en inglés de acuerdo con las directrices del repositorio.
Cómo ejecutar
- Agregue el archivo de estrategia a un proyecto StockSharp.
- Configure los conectores de datos de mercado y ejecución requeridos.
- Compile y lance la estrategia; asegúrese de que el instrumento seleccionado proporcione velas de 1 minuto.
- Ajuste los parámetros a través de la interfaz
StrategyParamsegún sea necesario.
using System;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Hybrid scalping strategy based on Stochastic Oscillator, RSI and Bollinger Bands.
/// </summary>
public class HybridScalperStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _emaFastPeriod;
private readonly StrategyParam<int> _emaSlowPeriod;
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<decimal> _bbDeviation;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<bool> _tradeMonday;
private readonly StrategyParam<bool> _tradeTuesday;
private readonly StrategyParam<bool> _tradeWednesday;
private readonly StrategyParam<bool> _tradeThursday;
private readonly StrategyParam<bool> _tradeFriday;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevStochK;
private decimal _prevStochD;
private bool _isInitialized;
private int _barsSinceTrade;
/// <summary>
/// RSI calculation period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Fast EMA period for trend detection.
/// </summary>
public int EmaFastPeriod
{
get => _emaFastPeriod.Value;
set => _emaFastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period for trend detection.
/// </summary>
public int EmaSlowPeriod
{
get => _emaSlowPeriod.Value;
set => _emaSlowPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BbPeriod
{
get => _bbPeriod.Value;
set => _bbPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands deviation.
/// </summary>
public decimal BbDeviation
{
get => _bbDeviation.Value;
set => _bbDeviation.Value = value;
}
/// <summary>
/// Bars to wait after a completed position.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Allow trading on Monday.
/// </summary>
public bool TradeMonday
{
get => _tradeMonday.Value;
set => _tradeMonday.Value = value;
}
/// <summary>
/// Allow trading on Tuesday.
/// </summary>
public bool TradeTuesday
{
get => _tradeTuesday.Value;
set => _tradeTuesday.Value = value;
}
/// <summary>
/// Allow trading on Wednesday.
/// </summary>
public bool TradeWednesday
{
get => _tradeWednesday.Value;
set => _tradeWednesday.Value = value;
}
/// <summary>
/// Allow trading on Thursday.
/// </summary>
public bool TradeThursday
{
get => _tradeThursday.Value;
set => _tradeThursday.Value = value;
}
/// <summary>
/// Allow trading on Friday.
/// </summary>
public bool TradeFriday
{
get => _tradeFriday.Value;
set => _tradeFriday.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HybridScalperStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 7)
.SetDisplay("RSI Period", "RSI calculation period", "Indicators");
_emaFastPeriod = Param(nameof(EmaFastPeriod), 21)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_emaSlowPeriod = Param(nameof(EmaSlowPeriod), 89)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_bbPeriod = Param(nameof(BbPeriod), 50)
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_bbDeviation = Param(nameof(BbDeviation), 4m)
.SetDisplay("BB Deviation", "Bollinger Bands deviation", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 2)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk");
_tradeMonday = Param(nameof(TradeMonday), true)
.SetDisplay("Trade Monday", "Allow trading on Monday", "Schedule");
_tradeTuesday = Param(nameof(TradeTuesday), true)
.SetDisplay("Trade Tuesday", "Allow trading on Tuesday", "Schedule");
_tradeWednesday = Param(nameof(TradeWednesday), true)
.SetDisplay("Trade Wednesday", "Allow trading on Wednesday", "Schedule");
_tradeThursday = Param(nameof(TradeThursday), true)
.SetDisplay("Trade Thursday", "Allow trading on Thursday", "Schedule");
_tradeFriday = Param(nameof(TradeFriday), true)
.SetDisplay("Trade Friday", "Allow trading on Friday", "Schedule");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle type for the strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevStochK = 0m;
_prevStochD = 0m;
_isInitialized = false;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var stochastic = new StochasticOscillator();
var emaFast = new ExponentialMovingAverage { Length = EmaFastPeriod };
var emaSlow = new ExponentialMovingAverage { Length = EmaSlowPeriod };
var bollinger = new BollingerBands
{
Length = BbPeriod,
Width = BbDeviation,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, stochastic, emaFast, emaSlow, bollinger, ProcessIndicators)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawIndicator(area, stochastic);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessIndicators(
ICandleMessage candle,
IIndicatorValue rsiValue,
IIndicatorValue stochValue,
IIndicatorValue emaFastValue,
IIndicatorValue emaSlowValue,
IIndicatorValue bollingerValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading() || !IsTradingDay(candle.OpenTime.DayOfWeek))
return;
var rsi = rsiValue.ToDecimal();
var stochastic = (StochasticOscillatorValue)stochValue;
if (stochastic.K is not decimal stochK || stochastic.D is not decimal stochD)
return;
var emaFast = emaFastValue.ToDecimal();
var emaSlow = emaSlowValue.ToDecimal();
var bands = (BollingerBandsValue)bollingerValue;
if (bands.UpBand is not decimal upperBand ||
bands.LowBand is not decimal lowerBand ||
bands.MovingAverage is not decimal middleBand ||
middleBand == 0m)
return;
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
var relativeWidth = (upperBand - lowerBand) / middleBand;
if (!_isInitialized)
{
_prevStochK = stochK;
_prevStochD = stochD;
_isInitialized = true;
return;
}
var longSignal =
_prevStochK <= _prevStochD &&
stochK > stochD &&
stochK < 30m &&
rsi < 40m &&
emaFast > emaSlow &&
relativeWidth is >= 0.005m and <= 0.12m;
var shortSignal =
_prevStochK >= _prevStochD &&
stochK < stochD &&
stochK > 70m &&
rsi > 60m &&
emaFast < emaSlow &&
relativeWidth is >= 0.005m and <= 0.12m;
if (_barsSinceTrade >= CooldownBars)
{
if (longSignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
else if (shortSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
}
_prevStochK = stochK;
_prevStochD = stochD;
}
private bool IsTradingDay(DayOfWeek day)
{
return day switch
{
DayOfWeek.Monday => TradeMonday,
DayOfWeek.Tuesday => TradeTuesday,
DayOfWeek.Wednesday => TradeWednesday,
DayOfWeek.Thursday => TradeThursday,
DayOfWeek.Friday => TradeFriday,
_ => false,
};
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, DayOfWeek
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import (
RelativeStrengthIndex, StochasticOscillator,
ExponentialMovingAverage, BollingerBands,
)
from StockSharp.Algo.Strategies import Strategy
class hybrid_scalper_strategy(Strategy):
def __init__(self):
super(hybrid_scalper_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 7) \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
self._ema_fast_period = self.Param("EmaFastPeriod", 21) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._ema_slow_period = self.Param("EmaSlowPeriod", 89) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._bb_period = self.Param("BbPeriod", 50) \
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._bb_deviation = self.Param("BbDeviation", 4.0) \
.SetDisplay("BB Deviation", "Bollinger Bands deviation", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 2) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk")
self._trade_monday = self.Param("TradeMonday", True) \
.SetDisplay("Trade Monday", "Allow trading on Monday", "Schedule")
self._trade_tuesday = self.Param("TradeTuesday", True) \
.SetDisplay("Trade Tuesday", "Allow trading on Tuesday", "Schedule")
self._trade_wednesday = self.Param("TradeWednesday", True) \
.SetDisplay("Trade Wednesday", "Allow trading on Wednesday", "Schedule")
self._trade_thursday = self.Param("TradeThursday", True) \
.SetDisplay("Trade Thursday", "Allow trading on Thursday", "Schedule")
self._trade_friday = self.Param("TradeFriday", True) \
.SetDisplay("Trade Friday", "Allow trading on Friday", "Schedule")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candle type for the strategy", "General")
self._prev_stoch_k = 0.0
self._prev_stoch_d = 0.0
self._is_initialized = False
self._bars_since_trade = 0
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def EmaFastPeriod(self):
return self._ema_fast_period.Value
@EmaFastPeriod.setter
def EmaFastPeriod(self, value):
self._ema_fast_period.Value = value
@property
def EmaSlowPeriod(self):
return self._ema_slow_period.Value
@EmaSlowPeriod.setter
def EmaSlowPeriod(self, value):
self._ema_slow_period.Value = value
@property
def BbPeriod(self):
return self._bb_period.Value
@BbPeriod.setter
def BbPeriod(self, value):
self._bb_period.Value = value
@property
def BbDeviation(self):
return self._bb_deviation.Value
@BbDeviation.setter
def BbDeviation(self, value):
self._bb_deviation.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def TradeMonday(self):
return self._trade_monday.Value
@TradeMonday.setter
def TradeMonday(self, value):
self._trade_monday.Value = value
@property
def TradeTuesday(self):
return self._trade_tuesday.Value
@TradeTuesday.setter
def TradeTuesday(self, value):
self._trade_tuesday.Value = value
@property
def TradeWednesday(self):
return self._trade_wednesday.Value
@TradeWednesday.setter
def TradeWednesday(self, value):
self._trade_wednesday.Value = value
@property
def TradeThursday(self):
return self._trade_thursday.Value
@TradeThursday.setter
def TradeThursday(self, value):
self._trade_thursday.Value = value
@property
def TradeFriday(self):
return self._trade_friday.Value
@TradeFriday.setter
def TradeFriday(self, value):
self._trade_friday.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _is_trading_day(self, day):
if day == DayOfWeek.Monday:
return self.TradeMonday
elif day == DayOfWeek.Tuesday:
return self.TradeTuesday
elif day == DayOfWeek.Wednesday:
return self.TradeWednesday
elif day == DayOfWeek.Thursday:
return self.TradeThursday
elif day == DayOfWeek.Friday:
return self.TradeFriday
return False
def OnStarted2(self, time):
super(hybrid_scalper_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
stochastic = StochasticOscillator()
ema_fast = ExponentialMovingAverage()
ema_fast.Length = self.EmaFastPeriod
ema_slow = ExponentialMovingAverage()
ema_slow.Length = self.EmaSlowPeriod
bollinger = BollingerBands()
bollinger.Length = self.BbPeriod
bollinger.Width = self.BbDeviation
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.BindEx(rsi, stochastic, ema_fast, ema_slow, bollinger, self.ProcessIndicators) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawIndicator(area, stochastic)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
def ProcessIndicators(self, candle, rsi_value, stoch_value, ema_fast_value,
ema_slow_value, bollinger_value):
if candle.State != CandleStates.Finished:
return
if not self._is_trading_day(candle.OpenTime.DayOfWeek):
return
rsi = float(rsi_value)
stoch_k_raw = stoch_value.K
stoch_d_raw = stoch_value.D
if stoch_k_raw is None or stoch_d_raw is None:
return
stoch_k = float(stoch_k_raw)
stoch_d = float(stoch_d_raw)
ema_fast = float(ema_fast_value)
ema_slow = float(ema_slow_value)
upper_raw = bollinger_value.UpBand
lower_raw = bollinger_value.LowBand
middle_raw = bollinger_value.MovingAverage
if upper_raw is None or lower_raw is None or middle_raw is None:
return
upper_band = float(upper_raw)
lower_band = float(lower_raw)
middle_band = float(middle_raw)
if middle_band == 0.0:
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
relative_width = (upper_band - lower_band) / middle_band
if not self._is_initialized:
self._prev_stoch_k = stoch_k
self._prev_stoch_d = stoch_d
self._is_initialized = True
return
long_signal = (self._prev_stoch_k <= self._prev_stoch_d
and stoch_k > stoch_d
and stoch_k < 30.0
and rsi < 40.0
and ema_fast > ema_slow
and 0.005 <= relative_width <= 0.12)
short_signal = (self._prev_stoch_k >= self._prev_stoch_d
and stoch_k < stoch_d
and stoch_k > 70.0
and rsi > 60.0
and ema_fast < ema_slow
and 0.005 <= relative_width <= 0.12)
pos = self.Position
if self._bars_since_trade >= self.CooldownBars:
if long_signal and pos <= 0:
self.BuyMarket(self.Volume + abs(pos))
self._bars_since_trade = 0
elif short_signal and pos >= 0:
self.SellMarket(self.Volume + abs(pos))
self._bars_since_trade = 0
self._prev_stoch_k = stoch_k
self._prev_stoch_d = stoch_d
def OnReseted(self):
super(hybrid_scalper_strategy, self).OnReseted()
self._prev_stoch_k = 0.0
self._prev_stoch_d = 0.0
self._is_initialized = False
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return hybrid_scalper_strategy()