GitHub で見る
Spazm ボラティリティブレイクアウト戦略
概要
- MetaTrader 4 エキスパート アドバイザ Spazm (8683) を StockSharp の高レベル API に変換しました。
- 最新の終値と、直近のスイング高値およびスイング安値付近のボラティリティ サイズのエンベロープを比較することで、適応型ブレイクアウトを取引します。
- 元の MQL ビジュアライゼーションと同様に、連続する強気ピボットと弱気ピボットを結合するオプションのチャート注釈を維持します。
データの準備
- このストラテジーは、アクティブ証券の
CandleType パラメーターで指定されたローソク足シリーズにサブスクライブします。
- 完成したすべてのローソク足は、ボラティリティの推定に使用される生の範囲サンプルを提供します。
- デフォルトでは、範囲は
High - Low に等しくなります。
UseOpenCloseRange が有効な場合、代わりに絶対ボディ サイズ |Open - Close| が使用されます。
- 範囲サンプルは商品
PriceStep を使用して価格ステップに変換されるため、ロジックはシンボル間で不変のままです。
UseWeightedVolatility で定義されたインジケーターは、範囲サンプルのシーケンスを処理します。
- 無効 → 長さ
VolatilityPeriod の単純移動平均。
- 有効 → 線形加重移動平均 (最近のローソク足に重みを付けます)。
- 平滑化された範囲 (ステップで表される) に
VolatilityMultiplier が乗算され、最終的に価格単位にスケールバックされます。結果として得られる値は、市場の両側に適用される適応型ブレイクアウトしきい値です。
- ウォームアップ段階では、戦略はタイムスタンプとともに最新の極高値と極低値も記録します。
VolatilityPeriod * 3 ローソク足が処理されると、それらの極値の相対的なタイミングによって最初のトレンドの方向が決まります。
パラメーター
| 名前 |
デフォルト |
説明 |
Volume |
1 |
ストラテジーがポジションをオープンまたは反転するたびに送信される注文量。 |
VolatilityMultiplier |
5 |
ブレイクアウト距離を構築するために平均ボラティリティに適用される乗数。 |
VolatilityPeriod |
24 |
ボラティリティ推定と最初のスイングの極値のシードの両方に使用されるローソク足の数。 |
UseWeightedVolatility |
false |
ボラティリティ推定を単純な加重移動平均から線形加重移動平均に切り替えます。 |
UseOpenCloseRange |
false |
ボラティリティを測定する際に、高値と安値の範囲ではなく絶対的な開閉値を使用します。 |
StopLossMultiplier |
0 |
保護停止距離を計算するためにブレークアウトしきい値に適用される乗数。少なくとも 3 つの価格ステップが適用されます。停止を無効にするには、0 に設定します。 |
DrawSwingLines |
true |
有効にすると、戦略は最新の強気ピボットと弱気ピボットの間に線を引き、MQL オブジェクトを模倣します。 |
CandleType |
4 hour time frame |
計算に使用するローソク足のタイプ (タイムフレームまたはその他のデータタイプ)。 |
取引ロジック
- 初期化
- 最初の
VolatilityPeriod * 3 ローソク足が処理される間に、戦略は _highestPrice、_lowestPrice、_highestTime、および _lowestTime を更新して最新の極値を捕捉します。
- 十分なローソク足が到着した後、2 つの極端な値のうちの新しい方が初期トレンドを定義します。最後の安値が最後の高値よりも新しい場合、戦略は強気モードで開始され、そうでない場合は弱気で開始します。
- 極値はスイング アンカーの最初のペアとしても保存されるため、ウォームアップ直後にチャート ラインを描画できます。
- ボラティリティ追跡
- 完成した各ローソク足は、その範囲を選択された移動平均に押し込み、適応しきい値を生成します。
- ゼロ距離エンベロープを避けるために、しきい値は常に少なくとも 1 つの価格ステップです。
- スイングのメンテナンス
- ローソク足ごとに、新しい絶対高値または安値が出力されるたびに、アルゴリズムは保存されているスイング高値とスイング安値を更新します。
- トレンドが反転すると、関連する極値がピボットとして記録され、グラフ作成が有効な場合は、反対側のピボットと線で接続されます。
- ブレイクアウトのルール
- 強気体制 (
_isTrendUp == true): _highestPrice - threshold を下回る終値は空売りへの反転を引き起こします。注文サイズは Volume + |Position| に等しいため、既存のエクスポージャーはフラット化され、1 回のコールで新しいショート ポジションがオープンされます。
- 弱気体制 (
_isTrendUp == false): _lowestPrice + threshold を上回る終値はロジックを反映し、反転してロングになります。
- 管理を停止
StopLossMultiplier がゼロより大きい場合、エントリー価格は threshold * StopLossMultiplier によってオフセットされ(少なくとも 3 つの価格ステップに制限されます)、合成ストップレベルが導出されます。
- ローソク足が安値でロングストップを、または高値でショートストップを突き抜けた場合、ポジションは成行注文によってフラット化されます。
- インフラストラクチャ
StartProtection() は、戦略が開始されるとすぐに、組み込みの StockSharp 安全メカニズムを有効にします。
- すべてのアクションは、元のエキスパートアドバイザーによるバーごとの再計算サイクルをエミュレートするために、完成したローソク足によって駆動されます。
MQL バージョンとの違い
- MetaTrader エキスパートはティックごとに再計算しますが、ローソク足のサブスクリプションは高レベルの API の慣用的なデータ ソースであるため、このポートは完了したローソク足で動作します。
MODE_STOPLEVEL などのブローカー固有の制限は利用できません。代わりに、ストップオフセットは 3 つの価格ステップによって制限され、保守的なフォールバックが提供されます。
- 注文は、既存のポジションを反復処理するのではなく、終値と始値を単一の
BuyMarket/SellMarket 呼び出しに結合することによって逆転されます。
- ビジュアライゼーションはプラットフォーム オブジェクトではなく、StockSharp チャート プリミティブ (
DrawLine) に依存しますが、ピボット間のラインの配置は元のインジケーター出力と一致します。
使用上の注意
- 選択したセキュリティが有効な
PriceStep を公開していることを確認してください。欠落している場合、コードはデフォルトで 1 になりますが、特定の機器では調整が必要な場合があります。
- この戦略は完了したローソク足に依存するため、非常に短い時間枠ではボラティリティ推定の信頼性が低下します。
CandleType を最初に EA で使用されていたタイムフレーム (デフォルトでは H4) に合わせることを検討してください。
- 停止はオプションです。
StopLossMultiplier をゼロのままにすると、MQL スクリプトから上限なしのリスク管理が複製されます。
- このアルゴリズムは設計上トレンドに従うものであり、利益確定目標を課すものではありません。イグジットは、レジーム反転またはストップロスの有効化によってのみ発生します。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Volatility breakout strategy that tracks swing extremes and reverses
/// when price breaks beyond an ATR-based volatility band.
/// </summary>
public class SpazmVolatilityBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _multiplier;
private decimal _swingHigh;
private decimal _swingLow;
private bool _trendUp;
private bool _initialized;
public SpazmVolatilityBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "Period for ATR volatility.", "Indicators");
_multiplier = Param(nameof(Multiplier), 2.0m)
.SetDisplay("Multiplier", "ATR multiplier for breakout threshold.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_swingHigh = 0;
_swingLow = decimal.MaxValue;
_trendUp = true;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (atrValue <= 0)
return;
var close = candle.ClosePrice;
var high = candle.HighPrice;
var low = candle.LowPrice;
var threshold = atrValue * Multiplier;
if (!_initialized)
{
_swingHigh = high;
_swingLow = low;
_initialized = true;
return;
}
if (_trendUp)
{
// Track swing high
if (high > _swingHigh)
_swingHigh = high;
// Reversal: price breaks below swing high minus threshold
if (close < _swingHigh - threshold)
{
_trendUp = false;
_swingLow = low;
// Enter short on trend reversal
if (Position > 0)
SellMarket();
if (Position == 0)
SellMarket();
}
}
else
{
// Track swing low
if (low < _swingLow)
_swingLow = low;
// Reversal: price breaks above swing low plus threshold
if (close > _swingLow + threshold)
{
_trendUp = true;
_swingHigh = high;
// Enter long on trend reversal
if (Position < 0)
BuyMarket();
if (Position == 0)
BuyMarket();
}
}
}
}
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
from StockSharp.Algo.Strategies import Strategy
class spazm_volatility_breakout_strategy(Strategy):
def __init__(self):
super(spazm_volatility_breakout_strategy, self).__init__()
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "Period for ATR volatility", "Indicators")
self._multiplier = self.Param("Multiplier", 2.0).SetDisplay("Multiplier", "ATR multiplier for breakout threshold", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).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(spazm_volatility_breakout_strategy, self).OnReseted()
self._swing_high = 0
self._swing_low = 999999999
self._trend_up = True
self._initialized = False
def OnStarted2(self, time):
super(spazm_volatility_breakout_strategy, self).OnStarted2(time)
self._swing_high = 0
self._swing_low = 999999999
self._trend_up = True
self._initialized = False
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def OnProcess(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
if atr_val <= 0:
return
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
threshold = atr_val * self._multiplier.Value
if not self._initialized:
self._swing_high = high
self._swing_low = low
self._initialized = True
return
if self._trend_up:
if high > self._swing_high:
self._swing_high = high
if close < self._swing_high - threshold:
self._trend_up = False
self._swing_low = low
if self.Position > 0:
self.SellMarket()
if self.Position == 0:
self.SellMarket()
else:
if low < self._swing_low:
self._swing_low = low
if close > self._swing_low + threshold:
self._trend_up = True
self._swing_high = high
if self.Position < 0:
self.BuyMarket()
if self.Position == 0:
self.BuyMarket()
def CreateClone(self):
return spazm_volatility_breakout_strategy()