大陽線・大陰線RSIダイバージェンス戦略
直近5本のバーと比較して異常に大きな陽線・陰線を識別し、高速・低速RSIの値を比較します。取引はローソク足の方向に従い、価格が設定したティック数だけ利益方向に動いた後にのみ発動する遅延トレーリングストップを使用します。
トレーリングストップは利益しきい値に達した時点で開始し、その後一定の距離で価格を追跡します。一方、初期固定ストップはエントリーから取引を保護します。
詳細
- エントリー条件:
- ロング: 現在のローソク足の実体が直近5本より大きく、上昇して終値を形成。
- ショート: 現在のローソク足の実体が直近5本より大きく、下落して終値を形成。
- ロング/ショート: 両方向。
- エグジット条件: 初期ストップまたはトレーリングストップに到達。
- ストップ: はい、遅延トレーリングストップ。
- デフォルト値:
TrailStartTicks= 200TrailDistanceTicks= 150InitialStopLossTicks= 200CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: リバーサル
- 方向: 両方
- インジケーター: RSI
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: はい
- リスクレベル: 中
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Big Candle RSI Divergence strategy.
/// When flat, a candle whose body is bigger than each of the previous five bodies opens a trade in its direction. An initial stop
/// InitialStopLossTicks price steps away protects the trade; once price has moved TrailStartTicks steps in profit a trailing stop
/// follows the best price at TrailDistanceTicks steps. Fast RSI(5) and slow RSI(14) are plotted for comparison.
/// </summary>
public class BigCandleRsiDivergenceStrategy : Strategy
{
private const int _bodyLookback = 5;
private readonly StrategyParam<int> _trailStartTicks;
private readonly StrategyParam<int> _trailDistanceTicks;
private readonly StrategyParam<int> _initialStopLossTicks;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _bodies = new();
private decimal _entryPrice;
private decimal _bestPrice;
private bool _trailingActive;
/// <summary>
/// Profit in price steps that activates the trailing stop.
/// </summary>
public int TrailStartTicks
{
get => _trailStartTicks.Value;
set => _trailStartTicks.Value = value;
}
/// <summary>
/// Trailing stop distance in price steps.
/// </summary>
public int TrailDistanceTicks
{
get => _trailDistanceTicks.Value;
set => _trailDistanceTicks.Value = value;
}
/// <summary>
/// Initial stop loss in price steps.
/// </summary>
public int InitialStopLossTicks
{
get => _initialStopLossTicks.Value;
set => _initialStopLossTicks.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BigCandleRsiDivergenceStrategy()
{
_trailStartTicks = Param(nameof(TrailStartTicks), 200)
.SetNotNegative()
.SetDisplay("Trail Start Ticks", "Profit in price steps that activates the trailing stop", "Risk");
_trailDistanceTicks = Param(nameof(TrailDistanceTicks), 150)
.SetNotNegative()
.SetDisplay("Trail Distance Ticks", "Trailing stop distance in price steps", "Risk");
_initialStopLossTicks = Param(nameof(InitialStopLossTicks), 200)
.SetNotNegative()
.SetDisplay("Initial Stop Loss Ticks", "Initial stop loss in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsiFast = new RelativeStrengthIndex { Length = 5 };
var rsiSlow = new RelativeStrengthIndex { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsiFast, rsiSlow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsiFast);
DrawIndicator(oscillators, rsiSlow);
}
}
}
private void ResetState()
{
_bodies.Clear();
_entryPrice = 0;
_bestPrice = 0;
_trailingActive = false;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiFastValue, IIndicatorValue rsiSlowValue)
{
if (candle.State != CandleStates.Finished)
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var isBig = _bodies.Count == _bodyLookback && _bodies.All(b => body > b);
_bodies.Enqueue(body);
while (_bodies.Count > _bodyLookback)
_bodies.Dequeue();
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
ManageStops(candle);
return;
}
if (!isBig)
return;
if (candle.ClosePrice > candle.OpenPrice)
{
BuyMarket(Volume);
StartTrade(candle.ClosePrice);
}
else if (candle.ClosePrice < candle.OpenPrice)
{
SellMarket(Volume);
StartTrade(candle.ClosePrice);
}
}
private void StartTrade(decimal price)
{
_entryPrice = price;
_bestPrice = price;
_trailingActive = false;
}
private void ManageStops(ICandleMessage candle)
{
if (_entryPrice <= 0)
return;
var step = Security?.PriceStep ?? 1m;
var initialStop = InitialStopLossTicks * step;
var trailStart = TrailStartTicks * step;
var trailDistance = TrailDistanceTicks * step;
if (Position > 0)
{
decimal? stop = InitialStopLossTicks > 0 ? _entryPrice - initialStop : null;
if (_trailingActive)
stop = Math.Max(stop ?? decimal.MinValue, _bestPrice - trailDistance);
if (stop is decimal s && candle.LowPrice <= s)
{
SellMarket(Position);
_entryPrice = 0;
return;
}
_bestPrice = Math.Max(_bestPrice, candle.HighPrice);
if (TrailDistanceTicks > 0 && _bestPrice - _entryPrice >= trailStart)
_trailingActive = true;
}
else if (Position < 0)
{
decimal? stop = InitialStopLossTicks > 0 ? _entryPrice + initialStop : null;
if (_trailingActive)
stop = Math.Min(stop ?? decimal.MaxValue, _bestPrice + trailDistance);
if (stop is decimal s && candle.HighPrice >= s)
{
BuyMarket(-Position);
_entryPrice = 0;
return;
}
_bestPrice = Math.Min(_bestPrice, candle.LowPrice);
if (TrailDistanceTicks > 0 && _entryPrice - _bestPrice >= trailStart)
_trailingActive = true;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from collections import deque
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
BODY_LOOKBACK = 5
class big_candle_rsi_divergence_strategy(Strategy):
"""
Big Candle RSI Divergence strategy.
When flat, a candle whose body is bigger than each of the previous five bodies opens a trade in its direction. An initial stop
InitialStopLossTicks price steps away protects the trade; once price has moved TrailStartTicks steps in profit a trailing stop
follows the best price at TrailDistanceTicks steps. Fast RSI(5) and slow RSI(14) are plotted for comparison.
"""
def __init__(self):
super(big_candle_rsi_divergence_strategy, self).__init__()
self._trail_start_ticks = self.Param("TrailStartTicks", 200).SetNotNegative().SetDisplay("Trail Start Ticks", "Profit in price steps that activates the trailing stop", "Risk")
self._trail_distance_ticks = self.Param("TrailDistanceTicks", 150).SetNotNegative().SetDisplay("Trail Distance Ticks", "Trailing stop distance in price steps", "Risk")
self._initial_stop_loss_ticks = self.Param("InitialStopLossTicks", 200).SetNotNegative().SetDisplay("Initial Stop Loss Ticks", "Initial stop loss in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._bodies = deque()
self._entry_price = Decimal(0)
self._best_price = Decimal(0)
self._trailing_active = False
def OnReseted(self):
super(big_candle_rsi_divergence_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(big_candle_rsi_divergence_strategy, self).OnStarted2(time)
self._reset_state()
rsi_fast = RelativeStrengthIndex()
rsi_fast.Length = 5
rsi_slow = RelativeStrengthIndex()
rsi_slow.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rsi_fast, rsi_slow, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi_fast)
self.DrawIndicator(oscillators, rsi_slow)
def _process_candle(self, candle, rsi_fast_value, rsi_slow_value):
if candle.State != CandleStates.Finished:
return
body = abs(candle.ClosePrice - candle.OpenPrice)
is_big = len(self._bodies) == BODY_LOOKBACK and all(body > b for b in self._bodies)
self._bodies.append(body)
while len(self._bodies) > BODY_LOOKBACK:
self._bodies.popleft()
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0:
self._manage_stops(candle)
return
if not is_big:
return
if candle.ClosePrice > candle.OpenPrice:
self.BuyMarket(self.Volume)
self._start_trade(candle.ClosePrice)
elif candle.ClosePrice < candle.OpenPrice:
self.SellMarket(self.Volume)
self._start_trade(candle.ClosePrice)
def _start_trade(self, price):
self._entry_price = price
self._best_price = price
self._trailing_active = False
def _manage_stops(self, candle):
if self._entry_price <= 0:
return
step = self.Security.PriceStep if self.Security is not None and self.Security.PriceStep is not None else Decimal(1)
stop_ticks = self._initial_stop_loss_ticks.Value
trail_ticks = self._trail_distance_ticks.Value
initial_stop = Decimal(stop_ticks) * step
trail_start = Decimal(self._trail_start_ticks.Value) * step
trail_distance = Decimal(trail_ticks) * step
if self.Position > 0:
stop = self._entry_price - initial_stop if stop_ticks > 0 else None
if self._trailing_active:
trail = self._best_price - trail_distance
stop = trail if stop is None else max(stop, trail)
if stop is not None and candle.LowPrice <= stop:
self.SellMarket(self.Position)
self._entry_price = Decimal(0)
return
self._best_price = max(self._best_price, candle.HighPrice)
if trail_ticks > 0 and self._best_price - self._entry_price >= trail_start:
self._trailing_active = True
elif self.Position < 0:
stop = self._entry_price + initial_stop if stop_ticks > 0 else None
if self._trailing_active:
trail = self._best_price + trail_distance
stop = trail if stop is None else min(stop, trail)
if stop is not None and candle.HighPrice >= stop:
self.BuyMarket(-self.Position)
self._entry_price = Decimal(0)
return
self._best_price = min(self._best_price, candle.LowPrice)
if trail_ticks > 0 and self._entry_price - self._best_price >= trail_start:
self._trailing_active = True
def CreateClone(self):
return big_candle_rsi_divergence_strategy()