高値安値戦略
最高値と最低値のレンジに対するローソク足の中値に基づいて取引する戦略です。
現在のローソク足の中値が最高値と最低値の平均を下回り、正規化距離がLowThresholdを下回る場合に買いを入れます。中値が平均を上回り、正規化距離がHighThresholdを上回った場合にロングポジションを決済します。
詳細
- エントリー条件: 中値が平均を下回り、正規化距離がLowThresholdを下回る。
- ロング/ショート: ロングのみ。
- エグジット条件: 中値が平均を上回り、正規化距離がHighThresholdを上回る。
- ストップ: なし。
- デフォルト値:
Range= 100LowThreshold= 15mHighThreshold= 85mCandleType= TimeSpan.FromMinutes(240)
- フィルター:
- カテゴリ: レンジ
- 方向: ロング
- インジケーター: Highest, Lowest
- ストップ: いいえ
- 複雑さ: 基本
- 時間軸: イントラデイ (240m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Highs Lows strategy.
/// Tracks the highest high and lowest low of the last Range candles. The candle midpoint is compared with the average of
/// these extremes, and their distance is normalized by half the range (0 at the average, 100 at an extreme). A long opens when the midpoint is
/// below the average and the normalized distance is below LowThreshold, and closes when the midpoint is above the average
/// and the normalized distance is above HighThreshold.
/// </summary>
public class HighsLowsStrategy : Strategy
{
private readonly StrategyParam<int> _range;
private readonly StrategyParam<decimal> _lowThreshold;
private readonly StrategyParam<decimal> _highThreshold;
private readonly StrategyParam<DataType> _candleType;
private Highest _highest;
private Lowest _lowest;
/// <summary>
/// Number of candles for the highest and lowest values.
/// </summary>
public int Range
{
get => _range.Value;
set => _range.Value = value;
}
/// <summary>
/// Normalized distance below which a long opens.
/// </summary>
public decimal LowThreshold
{
get => _lowThreshold.Value;
set => _lowThreshold.Value = value;
}
/// <summary>
/// Normalized distance above which the long closes.
/// </summary>
public decimal HighThreshold
{
get => _highThreshold.Value;
set => _highThreshold.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HighsLowsStrategy()
{
_range = Param(nameof(Range), 100)
.SetGreaterThanZero()
.SetDisplay("Range", "Number of candles for highest and lowest values", "Indicators");
_lowThreshold = Param(nameof(LowThreshold), 15m)
.SetDisplay("Low Threshold", "Normalized distance below which a long opens", "Signals");
_highThreshold = Param(nameof(HighThreshold), 85m)
.SetDisplay("High Threshold", "Normalized distance above which the long closes", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(240).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();
_highest = null;
_lowest = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highest = new Highest { Length = Range };
_lowest = new Lowest { Length = Range };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _highest);
DrawIndicator(area, _lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var highest = _highest.Process(candle.HighPrice, candle.OpenTime, true).ToDecimal();
var lowest = _lowest.Process(candle.LowPrice, candle.OpenTime, true).ToDecimal();
if (!_highest.IsFormed || !_lowest.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var width = highest - lowest;
if (width <= 0)
return;
var midpoint = (candle.HighPrice + candle.LowPrice) / 2;
var average = (highest + lowest) / 2;
// Distance from the average as a percentage of the half range: 0 at the average, 100 at an extreme.
var distance = Math.Abs(midpoint - average) / (width / 2) * 100m;
if (Position <= 0 && midpoint < average && distance < LowThreshold)
BuyMarket(Volume + Math.Abs(Position));
else if (Position > 0 && midpoint > average && distance > HighThreshold)
SellMarket(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
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class highs_lows_strategy(Strategy):
"""
Highs Lows strategy.
Tracks the highest high and lowest low of the last Range candles. The candle midpoint is compared with the average of
these extremes, and their distance is normalized by half the range (0 at the average, 100 at an extreme). A long opens when the midpoint is
below the average and the normalized distance is below LowThreshold, and closes when the midpoint is above the average
and the normalized distance is above HighThreshold.
"""
def __init__(self):
super(highs_lows_strategy, self).__init__()
self._range = self.Param("Range", 100).SetGreaterThanZero().SetDisplay("Range", "Number of candles for highest and lowest values", "Indicators")
self._low_threshold = self.Param("LowThreshold", 15.0).SetDisplay("Low Threshold", "Normalized distance below which a long opens", "Signals")
self._high_threshold = self.Param("HighThreshold", 85.0).SetDisplay("High Threshold", "Normalized distance above which the long closes", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(240))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._highest = None
self._lowest = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(highs_lows_strategy, self).OnReseted()
self._highest = None
self._lowest = None
def OnStarted2(self, time):
super(highs_lows_strategy, self).OnStarted2(time)
self._highest = Highest()
self._highest.Length = self._range.Value
self._lowest = Lowest()
self._lowest.Length = self._range.Value
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.DrawIndicator(area, self._highest)
self.DrawIndicator(area, self._lowest)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
highest = process_float(self._highest, candle.HighPrice, candle.OpenTime, True).GetValue[Decimal](None)
lowest = process_float(self._lowest, candle.LowPrice, candle.OpenTime, True).GetValue[Decimal](None)
if not self._highest.IsFormed or not self._lowest.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
width = highest - lowest
if width <= 0:
return
midpoint = (candle.HighPrice + candle.LowPrice) / Decimal(2)
average = (highest + lowest) / Decimal(2)
# Distance from the average as a percentage of the half range: 0 at the average, 100 at an extreme.
distance = abs(midpoint - average) / (width / Decimal(2)) * Decimal(100)
if self.Position <= 0 and midpoint < average and distance < Decimal(self._low_threshold.Value):
self.BuyMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and midpoint > average and distance > Decimal(self._high_threshold.Value):
self.SellMarket(self.Position)
def CreateClone(self):
return highs_lows_strategy()