ハイブリッド・スキャルパー戦略
概要
ハイブリッド・スキャルパー戦略は、MQL4スクリプト hybrid_Scalper.mq4 から変換された短期取引アルゴリズムです。StockSharpの高レベルAPIで動作し、1分足の時間軸向けに設計されています。この戦略は複数のテクニカル指標を組み合わせて、過剰または不足したボラティリティの時期を避けながら、素早いブレイクアウトの機会を特定します。
戦略ロジック
- トレンドフィルター – 速いEMA(21)と遅いEMA(89)が市場の方向性を決定します。速いEMAが遅いEMAを上回っている場合のみロング取引が許可され、ショート取引はその逆の条件が必要です。
- モメンタムフィルター – ストキャスティクス・オシレーター(5,3,3)がエントリーシグナルを生成します。%Kが20を下回り、%Dを下回っている場合に買いが発動します。%Kが80を上回り、%Dを上回り続けている場合に売りが発動します。
- RSI確認 – 期間7のRSI(相対力指数)がモメンタムを確認します。ロングエントリーにはRSIが25未満、ショートエントリーにはRSIが85超が必要です。
- ボラティリティフィルター – ボリンジャーバンド(50、偏差4)が現在の市場幅を測定します。戦略は上限バンドと下限バンドの差が0.00045から0.00262の間にある場合のみ取引し、静かな市場と不安定な市場の両方を避けます。
- 取引日 – パラメーターにより、各曜日(月曜~金曜)の取引を個別に有効または無効にできます。
パラメーター
| 名前 | 説明 |
|---|---|
RsiPeriod |
RSIインジケーターの期間。 |
EmaFastPeriod |
トレンド検出のための速いEMAの期間。 |
EmaSlowPeriod |
トレンド検出のための遅いEMAの期間。 |
BbPeriod |
ボリンジャーバンドで使用する期間。 |
BbDeviation |
ボリンジャーバンドの偏差乗数。 |
TradeMonday–TradeFriday |
特定の曜日の取引を有効にする。 |
CandleType |
ローソク足のタイプ/時間軸、デフォルトは1分足。 |
注意事項
- この戦略は高レベルAPIの
BindExを使用して、単一のサブスクリプションで複数のインジケーターを接続します。 StartProtection()は起動時に一度呼び出され、組み込みのポジション保護を有効にします(明示的なストップロスやテイクプロフィットのパラメーターはありません)。- コード内のすべてのコメントはリポジトリのガイドラインに従い英語で提供されています。
実行方法
- 戦略ファイルをStockSharpプロジェクトに追加します。
- 必要な市場データと約定コネクターを設定します。
- 戦略をコンパイルして起動します。選択した銘柄が1分足のローソク足データを提供していることを確認してください。
- 必要に応じて
StrategyParamインターフェースからパラメーターを調整します。
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()