Hybrid-Scalper-Strategie
Überblick
Die Hybrid-Scalper-Strategie ist ein kurzfristiger Handelsalgorithmus, der aus dem MQL4-Skript hybrid_Scalper.mq4 konvertiert wurde. Er läuft auf der High-Level-API von StockSharp und ist für den 1-Minuten-Zeitrahmen ausgelegt. Die Strategie kombiniert mehrere technische Indikatoren, um schnelle Ausbruchsmöglichkeiten zu identifizieren und dabei Phasen übermäßiger oder unzureichender Volatilität zu vermeiden.
Strategielogik
- Trendfilter – Eine schnelle EMA (21) und eine langsame EMA (89) bestimmen die Marktrichtung. Long-Trades sind nur erlaubt, wenn die schnelle EMA über der langsamen EMA liegt; Short-Trades erfordern die umgekehrte Bedingung.
- Momentum-Filter – Der Stochastik-Oszillator (5,3,3) generiert Einstiegssignale. Ein Kaufsignal wird ausgelöst, wenn %K unter 20 und unter %D liegt. Ein Verkaufssignal wird ausgelöst, wenn %K über 80 liegt und weiterhin über %D bleibt.
- RSI-Bestätigung – Der Relative Strength Index mit Periode 7 bestätigt das Momentum. Long-Einstiege erfordern RSI unter 25, Short-Einstiege erfordern RSI über 85.
- Volatilitätsfilter – Bollinger Bänder (50, Abweichung 4) messen die aktuelle Marktbreite. Die Strategie handelt nur, wenn die Differenz zwischen dem oberen und unteren Band zwischen 0.00045 und 0.00262 liegt, und vermeidet damit sowohl ruhige als auch instabile Märkte.
- Handelstage – Parameter ermöglichen die individuelle Aktivierung oder Deaktivierung des Handels für jeden Wochentag (Montag–Freitag).
Parameter
| Name | Beschreibung |
|---|---|
RsiPeriod |
Periode des RSI-Indikators. |
EmaFastPeriod |
Schnelle EMA-Periode zur Trenderkennung. |
EmaSlowPeriod |
Langsame EMA-Periode zur Trenderkennung. |
BbPeriod |
Periode für Bollinger Bänder. |
BbDeviation |
Abweichungsmultiplikator für Bollinger Bänder. |
TradeMonday–TradeFriday |
Handel an bestimmten Wochentagen aktivieren. |
CandleType |
Kerzentyp/Zeitrahmen, Standard sind 1-Minuten-Kerzen. |
Hinweise
- Die Strategie nutzt die High-Level-API
BindEx, um mehrere Indikatoren in einem einzigen Abonnement zu verbinden. StartProtection()wird einmalig beim Start aufgerufen, um den integrierten Positionsschutz zu aktivieren (keine expliziten Stop-Loss- oder Take-Profit-Parameter).- Alle Kommentare im Code werden gemäß den Repository-Richtlinien auf Englisch bereitgestellt.
Ausführung
- Fügen Sie die Strategiedatei einem StockSharp-Projekt hinzu.
- Konfigurieren Sie die erforderlichen Marktdaten- und Ausführungsverbindungen.
- Kompilieren Sie die Strategie und starten Sie sie; stellen Sie sicher, dass das ausgewählte Instrument 1-Minuten-Kerzen bereitstellt.
- Passen Sie die Parameter bei Bedarf über die
StrategyParam-Schnittstelle an.
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()