高値安値ブレイクアウト統計分析戦略
選択した時間軸の高値または安値レベルのブレイクアウトを取引します。設定オプションに基づいてロングまたはショートでエントリーし、固定バー数後にポジションを決済します。
詳細
- エントリー条件: 終値が選択した高値または安値レベルを越える。
- ロング/ショート: 両方向。
- エグジット条件: 逆シグナルまたはHoldingPeriodバー後。
- ストップ: なし。
- デフォルト値:
EntryOption= LongAtHighTimeframeOption= DailyHoldingPeriod= 5CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: 両方
- インジケーター: High, Low
- ストップ: いいえ
- 複雑さ: 基本
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// High Low Breakout Statistical Analysis strategy.
/// Tracks the high and low of the previous completed day, week or month (TimeframeOption). Depending on EntryOption it buys or
/// sells when the close crosses above the previous high, or buys or sells when the close crosses below the previous low. The
/// position is closed after HoldingPeriod candles.
/// </summary>
public class HighLowBreakoutStatisticalAnalysisStrategy : Strategy
{
/// <summary>
/// Which level to trade and in which direction.
/// </summary>
public enum EntryOptions
{
/// <summary>
/// Buy when the close crosses above the previous high.
/// </summary>
LongAtHigh,
/// <summary>
/// Sell when the close crosses above the previous high.
/// </summary>
ShortAtHigh,
/// <summary>
/// Buy when the close crosses below the previous low.
/// </summary>
LongAtLow,
/// <summary>
/// Sell when the close crosses below the previous low.
/// </summary>
ShortAtLow,
}
/// <summary>
/// Period the reference high and low are taken from.
/// </summary>
public enum TimeframeOptions
{
/// <summary>
/// Previous day.
/// </summary>
Daily,
/// <summary>
/// Previous week.
/// </summary>
Weekly,
/// <summary>
/// Previous month.
/// </summary>
Monthly,
}
private readonly StrategyParam<EntryOptions> _entryOption;
private readonly StrategyParam<TimeframeOptions> _timeframeOption;
private readonly StrategyParam<int> _holdingPeriod;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _periodStart;
private decimal _periodHigh;
private decimal _periodLow;
private decimal? _prevHigh;
private decimal? _prevLow;
private decimal? _prevClose;
private int _barsInPosition;
/// <summary>
/// Which level to trade and in which direction.
/// </summary>
public EntryOptions EntryOption
{
get => _entryOption.Value;
set => _entryOption.Value = value;
}
/// <summary>
/// Period the reference high and low are taken from.
/// </summary>
public TimeframeOptions TimeframeOption
{
get => _timeframeOption.Value;
set => _timeframeOption.Value = value;
}
/// <summary>
/// Candles to hold a position.
/// </summary>
public int HoldingPeriod
{
get => _holdingPeriod.Value;
set => _holdingPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HighLowBreakoutStatisticalAnalysisStrategy()
{
_entryOption = Param(nameof(EntryOption), EntryOptions.LongAtHigh)
.SetDisplay("Entry Option", "Which level to trade and in which direction", "Trading");
_timeframeOption = Param(nameof(TimeframeOption), TimeframeOptions.Daily)
.SetDisplay("Timeframe Option", "Period the reference high and low are taken from", "Trading");
_holdingPeriod = Param(nameof(HoldingPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Holding Period", "Candles to hold a position", "Trading");
_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()
{
_periodStart = null;
_periodHigh = 0m;
_periodLow = 0m;
_prevHigh = null;
_prevLow = null;
_prevClose = null;
_barsInPosition = 0;
}
/// <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 DateTime GetPeriodStart(DateTime time)
{
var date = time.Date;
return TimeframeOption switch
{
TimeframeOptions.Weekly => date.AddDays(-(((int)date.DayOfWeek + 6) % 7)),
TimeframeOptions.Monthly => new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind),
_ => date,
};
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var periodStart = GetPeriodStart(candle.OpenTime);
if (_periodStart != periodStart)
{
if (_periodStart != null)
{
_prevHigh = _periodHigh;
_prevLow = _periodLow;
}
_periodStart = periodStart;
_periodHigh = candle.HighPrice;
_periodLow = candle.LowPrice;
}
else
{
_periodHigh = Math.Max(_periodHigh, candle.HighPrice);
_periodLow = Math.Min(_periodLow, candle.LowPrice);
}
var close = candle.ClosePrice;
var prevClose = _prevClose;
_prevClose = close;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
_barsInPosition++;
if (_barsInPosition >= HoldingPeriod)
{
if (Position > 0)
SellMarket(Position);
else
BuyMarket(-Position);
}
return;
}
if (prevClose is not decimal lastClose || _prevHigh is not decimal high || _prevLow is not decimal low)
return;
var crossHigh = lastClose <= high && close > high;
var crossLow = lastClose >= low && close < low;
switch (EntryOption)
{
case EntryOptions.LongAtHigh when crossHigh:
case EntryOptions.LongAtLow when crossLow:
BuyMarket(Volume);
_barsInPosition = 0;
break;
case EntryOptions.ShortAtHigh when crossHigh:
case EntryOptions.ShortAtLow when crossLow:
SellMarket(Volume);
_barsInPosition = 0;
break;
}
}
}
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
# EntryOption values, matching the C# EntryOptions enum.
ENTRY_LONG_AT_HIGH = 0
ENTRY_SHORT_AT_HIGH = 1
ENTRY_LONG_AT_LOW = 2
ENTRY_SHORT_AT_LOW = 3
# TimeframeOption values, matching the C# TimeframeOptions enum.
TIMEFRAME_DAILY = 0
TIMEFRAME_WEEKLY = 1
TIMEFRAME_MONTHLY = 2
class high_low_breakout_statistical_analysis_strategy(Strategy):
"""
High Low Breakout Statistical Analysis strategy.
Tracks the high and low of the previous completed day, week or month (TimeframeOption). Depending on EntryOption it buys or
sells when the close crosses above the previous high, or buys or sells when the close crosses below the previous low. The
position is closed after HoldingPeriod candles.
"""
def __init__(self):
super(high_low_breakout_statistical_analysis_strategy, self).__init__()
self._entry_option = self.Param("EntryOption", ENTRY_LONG_AT_HIGH).SetDisplay("Entry Option", "0 LongAtHigh, 1 ShortAtHigh, 2 LongAtLow, 3 ShortAtLow", "Trading")
self._timeframe_option = self.Param("TimeframeOption", TIMEFRAME_DAILY).SetDisplay("Timeframe Option", "0 Daily, 1 Weekly, 2 Monthly", "Trading")
self._holding_period = self.Param("HoldingPeriod", 5).SetGreaterThanZero().SetDisplay("Holding Period", "Candles to hold a position", "Trading")
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._period_start = None
self._period_high = 0.0
self._period_low = 0.0
self._prev_high = None
self._prev_low = None
self._prev_close = None
self._bars_in_position = 0
def OnReseted(self):
super(high_low_breakout_statistical_analysis_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(high_low_breakout_statistical_analysis_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 _get_period_start(self, time):
date = time.Date
option = int(self._timeframe_option.Value)
if option == TIMEFRAME_WEEKLY:
return date.AddDays(-((int(date.DayOfWeek) + 6) % 7))
if option == TIMEFRAME_MONTHLY:
return date.AddDays(1 - date.Day)
return date
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
high_price = float(candle.HighPrice)
low_price = float(candle.LowPrice)
period_start = self._get_period_start(candle.OpenTime)
if self._period_start is None or self._period_start != period_start:
if self._period_start is not None:
self._prev_high = self._period_high
self._prev_low = self._period_low
self._period_start = period_start
self._period_high = high_price
self._period_low = low_price
else:
self._period_high = max(self._period_high, high_price)
self._period_low = min(self._period_low, low_price)
close = float(candle.ClosePrice)
prev_close = self._prev_close
self._prev_close = close
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0:
self._bars_in_position += 1
if self._bars_in_position >= self._holding_period.Value:
if self.Position > 0:
self.SellMarket(self.Position)
else:
self.BuyMarket(-self.Position)
return
if prev_close is None or self._prev_high is None or self._prev_low is None:
return
cross_high = prev_close <= self._prev_high and close > self._prev_high
cross_low = prev_close >= self._prev_low and close < self._prev_low
option = int(self._entry_option.Value)
if (option == ENTRY_LONG_AT_HIGH and cross_high) or (option == ENTRY_LONG_AT_LOW and cross_low):
self.BuyMarket(self.Volume)
self._bars_in_position = 0
elif (option == ENTRY_SHORT_AT_HIGH and cross_high) or (option == ENTRY_SHORT_AT_LOW and cross_low):
self.SellMarket(self.Volume)
self._bars_in_position = 0
def CreateClone(self):
return high_low_breakout_statistical_analysis_strategy()