Grover Llorens Activator戦略
ATRベースの適応型トレーリング戦略で、価格が内部アクティベーターラインを越えると方向を切り替えます。
価格とトレーリングラインの差がゼロを上回るときに買い。ゼロを下回るときに売り。
詳細
- エントリー条件: ATRから計算されたトレーリングストップをPrice が超えたとき。
- ロング/ショート: 両方。
- エグジット条件: 反対のシグナル。
- ストップ: なし。
- デフォルト値:
Length= 480Multiplier= 14CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: ATR
- ストップ: なし
- 複雑さ: 基本
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Grover Llorens Activator strategy.
/// The activator line restarts Multiplier ATRs away from price whenever the close crosses it: below price after an upward cross,
/// above price after a downward one. Between crosses it moves towards price by ATR / Length times the number of candles since the
/// cross, so it accelerates the longer the trend lasts. The strategy buys when close minus the line crosses above zero and sells
/// when it crosses below zero, reversing an opposite position.
/// </summary>
public class GroverLlorensActivatorStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal? _ts;
private decimal _prevDiff;
private decimal _step;
private int _barsSinceCross;
private int _direction;
/// <summary>
/// ATR period, also the divisor of the per-candle step.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// ATR multiplier for the restart distance.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GroverLlorensActivatorStrategy()
{
_length = Param(nameof(Length), 480)
.SetGreaterThanZero()
.SetDisplay("Length", "ATR period and step divisor", "Indicators");
_multiplier = Param(nameof(Multiplier), 14m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "ATR multiplier for the restart distance", "Indicators");
_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()
{
_ts = null;
_prevDiff = 0m;
_step = 0m;
_barsSinceCross = 0;
_direction = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var atr = new AverageTrueRange { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!atrValue.IsFormed)
return;
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
var prevTs = _ts ?? close;
var diff = close - prevTs;
var crossUp = diff > 0 && _prevDiff <= 0;
var crossDown = diff < 0 && _prevDiff >= 0;
if (crossUp || crossDown)
{
_direction = crossUp ? 1 : -1;
_step = atr / Length;
_barsSinceCross = 0;
_ts = crossUp ? close - atr * Multiplier : close + atr * Multiplier;
}
else
{
_barsSinceCross++;
_ts = prevTs + _direction * _step * _barsSinceCross;
}
_prevDiff = diff;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (crossUp && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (crossDown && 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
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class grover_llorens_activator_strategy(Strategy):
"""
Grover Llorens Activator strategy.
The activator line restarts Multiplier ATRs away from price whenever the close crosses it: below price after an upward cross,
above price after a downward one. Between crosses it moves towards price by ATR / Length times the number of candles since the
cross, so it accelerates the longer the trend lasts. The strategy buys when close minus the line crosses above zero and sells
when it crosses below zero, reversing an opposite position.
"""
def __init__(self):
super(grover_llorens_activator_strategy, self).__init__()
self._length = self.Param("Length", 480).SetGreaterThanZero().SetDisplay("Length", "ATR period and step divisor", "Indicators")
self._multiplier = self.Param("Multiplier", 14.0).SetGreaterThanZero().SetDisplay("Multiplier", "ATR multiplier for the restart distance", "Indicators")
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._ts = None
self._prev_diff = 0.0
self._step = 0.0
self._bars_since_cross = 0
self._direction = 0
def OnReseted(self):
super(grover_llorens_activator_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(grover_llorens_activator_strategy, self).OnStarted2(time)
self._reset_state()
atr = AverageTrueRange()
atr.Length = self._length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
if not atr_value.IsFormed:
return
atr = float(atr_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
prev_ts = self._ts if self._ts is not None else close
diff = close - prev_ts
cross_up = diff > 0 and self._prev_diff <= 0
cross_down = diff < 0 and self._prev_diff >= 0
multiplier = float(self._multiplier.Value)
if cross_up or cross_down:
self._direction = 1 if cross_up else -1
self._step = atr / self._length.Value
self._bars_since_cross = 0
self._ts = close - atr * multiplier if cross_up else close + atr * multiplier
else:
self._bars_since_cross += 1
self._ts = prev_ts + self._direction * self._step * self._bars_since_cross
self._prev_diff = diff
if not self.IsFormedAndOnlineAndAllowTrading():
return
if cross_up and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif cross_down and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return grover_llorens_activator_strategy()