HMA クロスオーバー RSI Stochastic トレーリングストップ戦略
高速・低速HMAのクロスオーバーとRSIおよびスムーズStochasticフィルターを使用した戦略です。高速HMAが低速HMAを上抜けし、RSIとStochasticが閾値を下回るときにロングをオープンし、逆の条件でショートをオープンします。トレーリングストップがエグジットを管理します。
詳細
- エントリー条件: RSIとStochasticが閾値未満の状態で高速HMAが低速HMAを上抜け。
- ロング/ショート: 両方向。
- エグジット条件: トレーリングストップまたは逆シグナル。
- ストップ: 割合トレーリング。
- デフォルト値:
FastHmaLength= 5SlowHmaLength= 20RsiPeriod= 14RsiBuyLevel= 45RsiSellLevel= 60StochLength= 14StochSmooth= 3StochBuyLevel= 39StochSellLevel= 63TrailingPercent= 5CandleType= TimeSpan.FromHours(1)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: HMA, RSI, Stochastic
- ストップ: トレーリング
- 複雑さ: 基本
- 時間軸: 1h
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// HMA Crossover RSI Stochastic Trailing Stop strategy.
/// Goes long when the fast HMA crosses above the slow HMA while RSI is below RsiBuyLevel and the smoothed Stochastic is below
/// StochBuyLevel. Goes short when the fast HMA crosses below the slow HMA while RSI is above RsiSellLevel and the smoothed
/// Stochastic is above StochSellLevel. An opposite signal reverses the position and a percent trailing stop manages exits.
/// </summary>
public class HmaCrossoverRsiStochasticTrailingStopStrategy : Strategy
{
private readonly StrategyParam<int> _fastHmaLength;
private readonly StrategyParam<int> _slowHmaLength;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiBuyLevel;
private readonly StrategyParam<decimal> _rsiSellLevel;
private readonly StrategyParam<int> _stochLength;
private readonly StrategyParam<int> _stochSmooth;
private readonly StrategyParam<decimal> _stochBuyLevel;
private readonly StrategyParam<decimal> _stochSellLevel;
private readonly StrategyParam<decimal> _trailingPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
/// <summary>
/// Fast HMA length.
/// </summary>
public int FastHmaLength
{
get => _fastHmaLength.Value;
set => _fastHmaLength.Value = value;
}
/// <summary>
/// Slow HMA length.
/// </summary>
public int SlowHmaLength
{
get => _slowHmaLength.Value;
set => _slowHmaLength.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI must be below this level for a long.
/// </summary>
public decimal RsiBuyLevel
{
get => _rsiBuyLevel.Value;
set => _rsiBuyLevel.Value = value;
}
/// <summary>
/// RSI must be above this level for a short.
/// </summary>
public decimal RsiSellLevel
{
get => _rsiSellLevel.Value;
set => _rsiSellLevel.Value = value;
}
/// <summary>
/// Stochastic %K length.
/// </summary>
public int StochLength
{
get => _stochLength.Value;
set => _stochLength.Value = value;
}
/// <summary>
/// Smoothing period of the Stochastic.
/// </summary>
public int StochSmooth
{
get => _stochSmooth.Value;
set => _stochSmooth.Value = value;
}
/// <summary>
/// Smoothed Stochastic must be below this level for a long.
/// </summary>
public decimal StochBuyLevel
{
get => _stochBuyLevel.Value;
set => _stochBuyLevel.Value = value;
}
/// <summary>
/// Smoothed Stochastic must be above this level for a short.
/// </summary>
public decimal StochSellLevel
{
get => _stochSellLevel.Value;
set => _stochSellLevel.Value = value;
}
/// <summary>
/// Trailing stop distance in percent.
/// </summary>
public decimal TrailingPercent
{
get => _trailingPercent.Value;
set => _trailingPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HmaCrossoverRsiStochasticTrailingStopStrategy()
{
_fastHmaLength = Param(nameof(FastHmaLength), 5)
.SetGreaterThanZero()
.SetDisplay("Fast HMA", "Fast HMA length", "Indicators");
_slowHmaLength = Param(nameof(SlowHmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Slow HMA", "Slow HMA length", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_rsiBuyLevel = Param(nameof(RsiBuyLevel), 45m)
.SetDisplay("RSI Buy Level", "RSI must be below this level for a long", "Signals");
_rsiSellLevel = Param(nameof(RsiSellLevel), 60m)
.SetDisplay("RSI Sell Level", "RSI must be above this level for a short", "Signals");
_stochLength = Param(nameof(StochLength), 14)
.SetGreaterThanZero()
.SetDisplay("Stoch Length", "Stochastic %K length", "Indicators");
_stochSmooth = Param(nameof(StochSmooth), 3)
.SetGreaterThanZero()
.SetDisplay("Stoch Smooth", "Smoothing period of the Stochastic", "Indicators");
_stochBuyLevel = Param(nameof(StochBuyLevel), 39m)
.SetDisplay("Stoch Buy Level", "Smoothed Stochastic must be below this level for a long", "Signals");
_stochSellLevel = Param(nameof(StochSellLevel), 63m)
.SetDisplay("Stoch Sell Level", "Smoothed Stochastic must be above this level for a short", "Signals");
_trailingPercent = Param(nameof(TrailingPercent), 5m)
.SetNotNegative()
.SetDisplay("Trailing %", "Trailing stop distance in percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fastHma = new HullMovingAverage { Length = FastHmaLength };
var slowHma = new HullMovingAverage { Length = SlowHmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var stochastic = new StochasticOscillator();
stochastic.K.Length = StochLength;
stochastic.D.Length = StochSmooth;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(fastHma, slowHma, rsi, stochastic, ProcessCandle)
.Start();
StartProtection(new Unit(), TrailingPercent > 0 ? new Unit(TrailingPercent, UnitTypes.Percent) : new Unit(), isStopTrailing: true, useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastHma);
DrawIndicator(area, slowHma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsi);
DrawIndicator(oscillators, stochastic);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue fastValue, IIndicatorValue slowValue, IIndicatorValue rsiValue, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!fastValue.IsFormed || !slowValue.IsFormed)
return;
var fast = fastValue.GetValue<decimal>();
var slow = slowValue.GetValue<decimal>();
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevFast = fast;
_prevSlow = slow;
if (prevFast is not decimal pf || prevSlow is not decimal ps)
return;
if (!rsiValue.IsFormed || !stochValue.IsFormed || stochValue is not IStochasticOscillatorValue { D: decimal stoch })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsi = rsiValue.GetValue<decimal>();
var crossUp = pf <= ps && fast > slow;
var crossDown = pf >= ps && fast < slow;
if (crossUp && rsi < RsiBuyLevel && stoch < StochBuyLevel && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (crossDown && rsi > RsiSellLevel && stoch > StochSellLevel && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import HullMovingAverage, RelativeStrengthIndex, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class hma_crossover_rsi_stochastic_trailing_stop_strategy(Strategy):
"""
HMA Crossover RSI Stochastic Trailing Stop strategy.
Goes long when the fast HMA crosses above the slow HMA while RSI is below RsiBuyLevel and the smoothed Stochastic is below
StochBuyLevel. Goes short when the fast HMA crosses below the slow HMA while RSI is above RsiSellLevel and the smoothed
Stochastic is above StochSellLevel. An opposite signal reverses the position and a percent trailing stop manages exits.
"""
def __init__(self):
super(hma_crossover_rsi_stochastic_trailing_stop_strategy, self).__init__()
self._fast_hma_length = self.Param("FastHmaLength", 5).SetGreaterThanZero().SetDisplay("Fast HMA", "Fast HMA length", "Indicators")
self._slow_hma_length = self.Param("SlowHmaLength", 20).SetGreaterThanZero().SetDisplay("Slow HMA", "Slow HMA length", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14).SetGreaterThanZero().SetDisplay("RSI Period", "RSI period", "Indicators")
self._rsi_buy_level = self.Param("RsiBuyLevel", 45.0).SetDisplay("RSI Buy Level", "RSI must be below this level for a long", "Signals")
self._rsi_sell_level = self.Param("RsiSellLevel", 60.0).SetDisplay("RSI Sell Level", "RSI must be above this level for a short", "Signals")
self._stoch_length = self.Param("StochLength", 14).SetGreaterThanZero().SetDisplay("Stoch Length", "Stochastic %K length", "Indicators")
self._stoch_smooth = self.Param("StochSmooth", 3).SetGreaterThanZero().SetDisplay("Stoch Smooth", "Smoothing period of the Stochastic", "Indicators")
self._stoch_buy_level = self.Param("StochBuyLevel", 39.0).SetDisplay("Stoch Buy Level", "Smoothed Stochastic must be below this level for a long", "Signals")
self._stoch_sell_level = self.Param("StochSellLevel", 63.0).SetDisplay("Stoch Sell Level", "Smoothed Stochastic must be above this level for a short", "Signals")
self._trailing_percent = self.Param("TrailingPercent", 5.0).SetNotNegative().SetDisplay("Trailing %", "Trailing stop distance in percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_fast = None
self._prev_slow = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hma_crossover_rsi_stochastic_trailing_stop_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(hma_crossover_rsi_stochastic_trailing_stop_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast_hma = HullMovingAverage()
fast_hma.Length = self._fast_hma_length.Value
slow_hma = HullMovingAverage()
slow_hma.Length = self._slow_hma_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
stochastic = StochasticOscillator()
stochastic.K.Length = self._stoch_length.Value
stochastic.D.Length = self._stoch_smooth.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(fast_hma, slow_hma, rsi, stochastic, self._process_candle).Start()
trailing = float(self._trailing_percent.Value)
self.StartProtection(Unit(), Unit(Decimal(trailing), UnitTypes.Percent) if trailing > 0 else Unit(), isStopTrailing=True, useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_hma)
self.DrawIndicator(area, slow_hma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
self.DrawIndicator(oscillators, stochastic)
def _process_candle(self, candle, fast_value, slow_value, rsi_value, stoch_value):
if candle.State != CandleStates.Finished:
return
if not fast_value.IsFormed or not slow_value.IsFormed:
return
fast = float(fast_value.GetValue[Decimal](None))
slow = float(slow_value.GetValue[Decimal](None))
prev_fast = self._prev_fast
prev_slow = self._prev_slow
self._prev_fast = fast
self._prev_slow = slow
if prev_fast is None or prev_slow is None:
return
if not rsi_value.IsFormed or not stoch_value.IsFormed or stoch_value.D is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
rsi = float(rsi_value.GetValue[Decimal](None))
stoch = float(stoch_value.D)
cross_up = prev_fast <= prev_slow and fast > slow
cross_down = prev_fast >= prev_slow and fast < slow
if cross_up and rsi < float(self._rsi_buy_level.Value) and stoch < float(self._stoch_buy_level.Value) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif cross_down and rsi > float(self._rsi_sell_level.Value) and stoch > float(self._stoch_sell_level.Value) and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return hma_crossover_rsi_stochastic_trailing_stop_strategy()