ナローレンジ戦略
インサイドバーの後、直近のローソク足のレンジがLength期間前の参照バーより狭い場合にブレイクアウトを取引する戦略です。参照の高値と安値にストップ注文を置き、テイクプロフィットは参照レンジ、ストップロスはそのレンジのパーセンテージとして設定します。
詳細
- エントリー条件:
- ロング: ナローレンジバーの後、価格が参照高値を上抜ける
- ショート: ナローレンジバーの後、価格が参照安値を下抜ける
- ロング/ショート: 両方
- エグジット条件:
- 参照レンジでテイクプロフィット
- レンジのパーセンテージでストップロス
- ストップ: あり
- デフォルト値:
Length= 4StopLossPercent= 0.35mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: 両方
- インジケーター: なし
- ストップ: あり
- 複雑さ: 基本
- 時間軸: 短期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Narrow range breakout strategy.
/// A setup appears on an inside bar whose range is narrower than the range of the reference bar Length periods ago.
/// The reference bar's high and low become breakout levels: a close above the high buys, a close below the low sells.
/// The take profit is the reference range away from the entry level and the stop loss is StopLossPercent of that range.
/// </summary>
public class NarrowRangeStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<ICandleMessage> _history = [];
private decimal? _breakoutHigh;
private decimal? _breakoutLow;
private decimal _setupRange;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Bars back to the reference bar.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Stop loss as a fraction of the reference range.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public NarrowRangeStrategy()
{
_length = Param(nameof(Length), 4)
.SetGreaterThanZero()
.SetDisplay("Length", "Bars back to the reference bar", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 0.35m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss as a fraction of the reference range", "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();
}
private void ResetState()
{
_history.Clear();
_breakoutHigh = null;
_breakoutLow = null;
_setupRange = 0m;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (IsFormedAndOnlineAndAllowTrading())
Trade(candle);
_history.Add(candle);
if (_history.Count > Length + 1)
_history.RemoveAt(0);
if (Position != 0 || _history.Count <= Length)
return;
var previous = _history[^2];
var reference = _history[0];
var range = candle.HighPrice - candle.LowPrice;
var referenceRange = reference.HighPrice - reference.LowPrice;
var insideBar = candle.HighPrice < previous.HighPrice && candle.LowPrice > previous.LowPrice;
if (insideBar && range < referenceRange && referenceRange > 0)
{
_breakoutHigh = reference.HighPrice;
_breakoutLow = reference.LowPrice;
_setupRange = referenceRange;
}
}
private void Trade(ICandleMessage candle)
{
if (Position > 0)
{
if ((StopLossPercent > 0 && candle.LowPrice <= _stopPrice) || candle.HighPrice >= _takePrice)
SellMarket(Position);
return;
}
if (Position < 0)
{
if ((StopLossPercent > 0 && candle.HighPrice >= _stopPrice) || candle.LowPrice <= _takePrice)
BuyMarket(-Position);
return;
}
if (_breakoutHigh is not decimal high || _breakoutLow is not decimal low)
return;
if (candle.ClosePrice > high)
{
BuyMarket(Volume);
_takePrice = high + _setupRange;
_stopPrice = high - _setupRange * StopLossPercent;
_breakoutHigh = _breakoutLow = null;
}
else if (candle.ClosePrice < low)
{
SellMarket(Volume);
_takePrice = low - _setupRange;
_stopPrice = low + _setupRange * StopLossPercent;
_breakoutHigh = _breakoutLow = null;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class narrow_range_strategy(Strategy):
"""
Narrow range breakout strategy.
A setup appears on an inside bar whose range is narrower than the range of the reference bar Length periods ago.
The reference bar's high and low become breakout levels: a close above the high buys, a close below the low sells.
The take profit is the reference range away from the entry level and the stop loss is StopLossPercent of that range.
"""
def __init__(self):
super(narrow_range_strategy, self).__init__()
self._length = self.Param("Length", 4).SetGreaterThanZero().SetDisplay("Length", "Bars back to the reference bar", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 0.35).SetNotNegative().SetDisplay("Stop Loss", "Stop loss as a fraction of the reference range", "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 GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def _reset_state(self):
self._history = []
self._breakout_high = None
self._breakout_low = None
self._setup_range = 0.0
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(narrow_range_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(narrow_range_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self.IsFormedAndOnlineAndAllowTrading():
self._trade(candle)
length = self._length.Value
high = float(candle.HighPrice)
low = float(candle.LowPrice)
self._history.append((high, low))
if len(self._history) > length + 1:
self._history.pop(0)
if self.Position != 0 or len(self._history) <= length:
return
prev_high, prev_low = self._history[-2]
ref_high, ref_low = self._history[0]
rng = high - low
ref_range = ref_high - ref_low
inside_bar = high < prev_high and low > prev_low
if inside_bar and rng < ref_range and ref_range > 0:
self._breakout_high = ref_high
self._breakout_low = ref_low
self._setup_range = ref_range
def _trade(self, candle):
sl = float(self._stop_loss_percent.Value)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self.Position > 0:
if (sl > 0 and low <= self._stop_price) or high >= self._take_price:
self.SellMarket(self.Position)
return
if self.Position < 0:
if (sl > 0 and high >= self._stop_price) or low <= self._take_price:
self.BuyMarket(-self.Position)
return
if self._breakout_high is None or self._breakout_low is None:
return
close = float(candle.ClosePrice)
if close > self._breakout_high:
self.BuyMarket(self.Volume)
self._take_price = self._breakout_high + self._setup_range
self._stop_price = self._breakout_high - self._setup_range * sl
self._breakout_high = None
self._breakout_low = None
elif close < self._breakout_low:
self.SellMarket(self.Volume)
self._take_price = self._breakout_low - self._setup_range
self._stop_price = self._breakout_low + self._setup_range * sl
self._breakout_high = None
self._breakout_low = None
def CreateClone(self):
return narrow_range_strategy()