GitHub で見る
ボラティリティ・ピボット戦略
概要
ボラティリティ・ピボット戦略は、元のエキスパートアドバイザーExp_VolatilityPivot.mq5のStockSharp高レベルポートです。Average True Range(ATR)のボラティリティまたは固定価格偏差を使用して価格をトレールする2つの適応ストップラインを投影することで、カスタムのVolatility Pivotインジケーターを再現します。トレンドが反転すると、インジケーターはポジションの反転を引き起こす単一バーのブレイクアウト矢印を発します。この戦略はそれらのシグナルに従う(WithTrend)か、それらに逆らって取引する(CounterTrend)かを選択でき、ブレイクアウトや平均回帰スタイルの柔軟性を提供します。
MQL実装とは異なり、このバージョンはCandleTypeによって提供された完了ローソク足のみに依存します。ATRモードは平滑化ATR(ATRのEMA)をAtrMultiplierで乗算し、価格モードは生のDeltaPriceオフセットを使用します。結果として得られるピボットラインは、エントリーとエグジットを管理する強気と弱気のトレーリングレベルを定義します。
市場データとインジケーター
- 一次ローソク足(
CandleType) – すべての計算はこの時間軸で実行されます。デフォルトは元のエキスパートアドバイザーに合わせた4時間バーです。
- ATR + EMA平滑化 –
Atrモードでは、戦略は長さAtrPeriodのAverageTrueRangeを処理し、次に長さSmoothingPeriodのExponentialMovingAverageで平滑化します。
- 価格偏差モード –
PriceDeviationモードでは、トレーリングオフセットは固定のDeltaPrice量で、ボラティリティ平滑化が不要な場合に確定的なストップ距離を可能にします。
- ピボット状態追跡 – 戦略は最新の強気/弱気トレール値を保持し、トレールが価格の一方から他方に切り替わるバーにのみ「シグナル」を発し、MQLバージョンのインジケーターバッファを反映します。
トレードロジック
- ピボット計算 – 各完了ローソク足について、戦略はVolatility Pivotのルールに従ってトレーリングストップ価格を更新します。強気のトレールは価格が計算されたストップより上でクローズしたときアクティブです;弱気のトレールは下でクローズしたときアクティブです。
- シグナル検出 – 強気(弱気)のトレールが前のバーで非アクティブだった後にアクティブになったとき、新しい強気(弱気)シグナルが発せられます。
SignalBarパラメーターは要求された完了バー数だけ実行を遅延させ、MQLスクリプトのSignalBar入力を複製します。
- 方向フィルター(
TradeDirection) – WithTrendに設定すると戦略は強気シグナルで買い、弱気シグナルで売ります。CounterTrendに設定すると解釈が反転されます:強気の矢印がショートを決済して新しいショートを開き、その逆も同様です。
- エントリー許可 –
EnableBuyEntriesとEnableSellEntriesが新しいロングまたはショートポジションを開けるかどうかを制御します。
- エグジット許可 –
AllowLongExitsとAllowShortExitsが直接シグナルまたはアクティブのままの反対のトレールによって既存ポジションを決済できるかどうかを制御します。
- ポジション調整 – 戦略はロングに
+Volume、ショートに-Volume、フラット時に0のネットポジションを目標とします。注文は新しい方向を確立する前に反対のエクスポージャーを決済するよう自動的にサイズが決められます。
- 保護ストップ – オプションの
StopLossおよびTakeProfit距離(絶対価格単位で表現)は各完了ローソク足を監視します。バーの高値/安値がそれらのレベルを突破すると、戦略はすぐにポジションを終了します。
パラメーター
| パラメーター |
説明 |
デフォルト |
CandleType |
インジケーター処理と実行に使用されるローソク足シリーズ。 |
4時間ローソク足 |
AtrPeriod |
ATRコンポーネントの長さ。 |
100 |
SmoothingPeriod |
ATR値に適用されるEMA平滑化長さ。 |
10 |
AtrMultiplier |
平滑化されたATRに適用される乗数。 |
3.0 |
DeltaPrice |
PivotMode = PriceDeviationの場合に使用される固定価格オフセット。 |
0.002 |
PivotMode |
ATRベースまたは固定偏差ピボットを選択します。 |
Atr |
TradeDirection |
ピボットブレイクアウトに従う(WithTrend)かフェードアウト(CounterTrend)するかを選択します。 |
WithTrend |
SignalBar |
シグナルに対して行動する前に待つ完了バー数。 |
1 |
EnableBuyEntries |
新しいロングポジションの開設を許可します。 |
true |
EnableSellEntries |
新しいショートポジションの開設を許可します。 |
true |
AllowLongExits |
弱気状態が続くときに既存のロングポジションの決済を許可します。 |
true |
AllowShortExits |
強気状態が続くときに既存のショートポジションの決済を許可します。 |
true |
StopLoss |
オプションのストップロス距離(絶対価格単位)。無効にするには0に設定します。 |
0 |
TakeProfit |
オプションのテイクプロフィット距離(絶対価格単位)。無効にするには0に設定します。 |
0 |
注: StockSharpのStrategy.Volumeプロパティがポジションサイズを定義します。銘柄のコントラクトまたは株式サイズに合わせて戦略を開始する前に設定します。
使用ガイドライン
- 希望する
SecurityとPortfolioに戦略をアタッチし、意図するロットサイズにVolumeを設定します。
- データソースが選択した
CandleTypeを提供できることを確認します。完了ローソク足の継続的なフィードなしに、ATR平滑化とシグナル遅延ロジックは形成できません。
- 市場動向に基づいて
PivotModeを選択します:ATRモードはボラティリティに適応し、価格偏差モードはトレールを固定に保ちます。
- 元のエキスパートアドバイザーの正確なタイミングを再現するために
SignalBarを調整します(デフォルトで1バーのラグ)。0に設定すると最新の完了バーで実行されます。
StopLoss/TakeProfitを使用する場合、銘柄のボラティリティに距離を調整します(絶対価格であり、ポイントやパーセントではありません)。
- ピボットの変化によって引き起こされたエントリー、エグジット、保護ストップに関する情報メッセージのログを監視します。
元のエキスパートアドバイザーとの違い
- アカウント残高/空き証拠金に基づくマネー管理オプションは削除されました。ポジションサイズは
Strategy.Volumeのみで制御されます。
- StockSharpは完了ローソク足に成行注文を使用するため、注文価格の「偏差」とMQL補助ライブラリの手動時間同期は不要です。
- MQLスクリプトに存在する通知機能、グローバル変数、手動履歴ロードは省略されています。
- 保護ストップとテイクプロフィットの処理はローソク足ベースのチェックに簡素化されており、バー内注文配置はありません。
推奨される機能拡張
- 低流動性時間帯のトレーディングを一時停止するために、日次セッションフィルターやボラティリティフィルターを追加します。
- ピボットラインを反映するトレーリングストップ管理で戦略を拡張するか、視覚化のために計算されたラインをチャートにエクスポートします。
- 複数の銘柄が同じ戦略インスタンスを使用する場合は、ポートフォリオレベルのリスク管理を組み込みます。
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>
/// Volatility pivot strategy using ATR-based trailing stop.
/// Follows trend by flipping long/short when price crosses the ATR-based pivot line.
/// </summary>
public class VolatilityPivotStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _emaPeriod;
private AverageTrueRange _atr;
private ExponentialMovingAverage _ema;
private decimal _pivotStop;
private decimal _prevClose;
private bool _isLong;
private bool _initialized;
private int _cooldown;
/// <summary>
/// ATR calculation period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Multiplier applied to ATR for pivot distance.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// EMA period for trend confirmation.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public VolatilityPivotStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation period", "Indicator");
_atrMultiplier = Param(nameof(AtrMultiplier), 5m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Multiplier for pivot distance", "Indicator");
_emaPeriod = Param(nameof(EmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend filter period", "Indicator");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_atr = null;
_ema = null;
_pivotStop = 0;
_prevClose = 0;
_isLong = true;
_initialized = false;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_atr = new AverageTrueRange { Length = AtrPeriod };
_ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_atr, _ema, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_atr.IsFormed || !_ema.IsFormed)
{
_prevClose = candle.ClosePrice;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevClose = candle.ClosePrice;
return;
}
var close = candle.ClosePrice;
var delta = atrValue * AtrMultiplier;
if (!_initialized)
{
_pivotStop = close > emaValue ? close - delta : close + delta;
_isLong = close > emaValue;
_initialized = true;
_prevClose = close;
return;
}
// Update pivot stop
if (_isLong)
{
var newStop = close - delta;
if (newStop > _pivotStop)
_pivotStop = newStop;
// Check for reversal
if (close < _pivotStop)
{
_isLong = false;
_pivotStop = close + delta;
if (Position > 0)
SellMarket();
SellMarket();
_cooldown = 60;
}
}
else
{
var newStop = close + delta;
if (newStop < _pivotStop)
_pivotStop = newStop;
// Check for reversal
if (close > _pivotStop)
{
_isLong = true;
_pivotStop = close - delta;
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldown = 60;
}
}
_prevClose = close;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class volatility_pivot_strategy(Strategy):
"""ATR-based trailing pivot stop: flip long/short when price crosses pivot line."""
def __init__(self):
super(volatility_pivot_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "ATR period", "Indicator")
self._atr_mult = self.Param("AtrMultiplier", 5).SetGreaterThanZero().SetDisplay("ATR Multiplier", "Multiplier for pivot", "Indicator")
self._ema_period = self.Param("EmaPeriod", 100).SetGreaterThanZero().SetDisplay("EMA Period", "EMA trend filter", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(volatility_pivot_strategy, self).OnReseted()
self._pivot_stop = 0
self._is_long = True
self._initialized = False
self._cooldown = 0
def OnStarted2(self, time):
super(volatility_pivot_strategy, self).OnStarted2(time)
self._pivot_stop = 0
self._is_long = True
self._initialized = False
self._cooldown = 0
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(atr, ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, atr_val, ema_val):
if candle.State != CandleStates.Finished:
return
av = float(atr_val)
ev = float(ema_val)
close = float(candle.ClosePrice)
mult = self._atr_mult.Value
if av <= 0:
return
if self._cooldown > 0:
self._cooldown -= 1
return
delta = av * mult
if not self._initialized:
self._pivot_stop = close - delta if close > ev else close + delta
self._is_long = close > ev
self._initialized = True
return
if self._is_long:
new_stop = close - delta
if new_stop > self._pivot_stop:
self._pivot_stop = new_stop
if close < self._pivot_stop:
self._is_long = False
self._pivot_stop = close + delta
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown = 60
else:
new_stop = close + delta
if new_stop < self._pivot_stop:
self._pivot_stop = new_stop
if close > self._pivot_stop:
self._is_long = True
self._pivot_stop = close - delta
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown = 60
def CreateClone(self):
return volatility_pivot_strategy()