GitHub で見る
Spasm戦略
概要
- MetaTrader 5エキスパートアドバイザー Spasm (barabashkakvn's edition) をStockSharpの高レベルAPIに変換したものです。
- 最近のボラティリティによってサイズが決まる適応型チャネルのブレイクアウトを取引し、強気と弱気のレジームを切り替えます。
CandleTypeパラメーターによって提供される任意のインストゥルメントと時間軸で動作し、デフォルトは1時間足です。
データ準備
- 戦略証券のために
CandleTypeで定義されたローソク足シリーズを購読します。
- 最後の
VolatilityPeriod本のローソク足からボラティリティ推定器を構築します:
UseWeightedVolatilityが無効の場合、推定器はローソク足ごとのレンジの単純移動平均です。
UseWeightedVolatilityが有効の場合、推定器は最新のバーを強調する線形加重移動平均になります。
- ローソク足ごとのレンジはデフォルトで
High - Lowです。UseOpenCloseRangeが有効の場合、オリジナルEAのモード切り替えを再現するために、代わりに始値と終値の絶対差が使用されます。
- 生の平均レンジは価格ステップに変換され、
VolatilityMultiplierで乗算されます。結果はステップの整数に切り捨てられ、最終的にブレイクアウトしきい値を形成するためにインストゥルメントのティックサイズで乗算されます。
- 最初の
VolatilityPeriod * 3本の完了したローソク足の間、戦略はどのスイングがより最近のものかを判断するためにタイムスタンプとともに最新の最高値と最低値を収集します。その情報は、十分なローソク足が処理されると、初期トレンド状態と参照価格をシードします。
パラメーター
| 名前 |
デフォルト |
説明 |
Volume |
1 |
各市場エントリーに適用される注文ボリューム。 |
VolatilityMultiplier |
5 |
ブレイクアウトバッファーをサイズ調整するために平均ボラティリティに適用する乗数。 |
VolatilityPeriod |
24 |
ボラティリティ平均ルーチンと初期スイングスキャンに使用するローソク足の数。 |
UseWeightedVolatility |
false |
ボラティリティ平均を単純移動平均から線形加重移動平均に切り替えます。 |
UseOpenCloseRange |
false |
高値-安値レンジの代わりに絶対的な始値-終値の動きをボラティリティソースとして使用します。 |
StopLossFraction |
0.5 |
ストップロス距離を計算するために使用されるボラティリティしきい値の分数。最低3価格ステップが適用されます。 |
CandleType |
1時間時間軸 |
すべての計算に使用するローソク足タイプと時間軸。 |
トレーディングロジック
- トレンド追跡
- 戦略は現在のスイングのアンカーとして
_highestPriceと_lowestPriceを保持します。
- 価格が保存された高値から現在のしきい値を超えて上昇するたびに、
_highestPriceはローソク足の高値に更新されます。同様に、しきい値を超えた下落は_lowestPriceをローソク足の安値に更新します。
- ブール値
_isTrendUpは、戦略が現在強気(true)または弱気(false)のレジームにあるかどうかを格納します。
- エントリールール
_isTrendUpがfalse(弱気レジーム)でローソク足の終値が_lowestPrice + thresholdを超えると、戦略は強気モードに切り替わり、BuyMarket(Volume + Math.Abs(Position))を送信します。これはすべてのショートエクスポージャーを閉じ、Volumeと等しいロングポジションを開きます。
_isTrendUpがtrue(強気レジーム)でローソク足の終値が_highestPrice - thresholdを下回ると、戦略は弱気モードに切り替わり、ショートポジションに転換するためにSellMarket(Volume + Math.Abs(Position))を送信します。
- ストップ管理
- ロングポジションに入るとき、ストップ価格は
entry - max(threshold * StopLossFraction, 3 * priceStep)に配置されます。
- ショートポジションに入るとき、ストップ価格は
entry + max(threshold * StopLossFraction, 3 * priceStep)に配置されます。
- ローソク足の安値がロングストップに達するか、高値がショートストップに達すると、対応するポジションは成行注文を送信することで閉じられます。
StopLossFractionがゼロに設定されるとストップは無効になります。
- リスク管理とインフラ
StartProtection()は起動時に呼び出され、戦略が開始するとすぐに組み込みのリスク保護がアクティブになります。
- 戦略はインタバーノイズを避けるために完了したローソク足にのみ反応し、オリジナルEAのバーごとの再計算を反映します。
- すべてのコメントとパラメーター名は要件に従って英語で保持されます。
MQLバージョンとの違い
- オリジナルのEAはすべてのティックでしきい値を再計算していました。このポートでは、高レベルAPIがローソク足サブスクリプションで動作するため、ロジックは完了したローソク足で実行されます。
- ストップロスの適用はローソク足データで行われます。同じバー内で反転するインタバーのストップヒットは、したがってローソク足の境界で評価されます。
- スプレッドやブローカー固有のストップレベルなどのシンボルプロパティは、StockSharpでは同じ形式では利用できません。計算されたストップ距離が小さすぎる場合、MetaTrader実装のフォールバックを再現するために3価格ステップの保守的な最小値が使用されます。
使用上のノート
- 戦略証券が有効な
PriceStepを公開していることを確認してください。提供されない場合、コードはデフォルトでステップを1に設定します。
- 戦略は方向に依存せず、フィードが設定されたローソク足を配信する限り、スポット、先物、またはCFDインストゥルメントで使用できます。
- テイクプロフィットターゲットは定義されていません。エグジットはレジームの切り替えまたはストップロストリガーによってのみ発生します。
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Volatility breakout strategy converted from the MetaTrader Spasm expert advisor.
/// Tracks directional swings using adaptive thresholds derived from ATR.
/// Buys when price breaks above recent high + ATR*multiplier, sells when price breaks below recent low - ATR*multiplier.
/// </summary>
public class SpasmStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _volatilityMultiplier;
private decimal _highestPrice;
private decimal _lowestPrice;
private decimal _prevRange;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal VolatilityMultiplier
{
get => _volatilityMultiplier.Value;
set => _volatilityMultiplier.Value = value;
}
public SpasmStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis", "General");
_volatilityMultiplier = Param(nameof(VolatilityMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Volatility Multiplier", "Multiplier applied to ATR for breakout bands", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highestPrice = 0m;
_lowestPrice = decimal.MaxValue;
_prevRange = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highestPrice = 0m;
_lowestPrice = decimal.MaxValue;
_prevRange = 0m;
_initialized = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_initialized)
{
_highestPrice = candle.HighPrice;
_lowestPrice = candle.LowPrice;
_prevRange = candle.HighPrice - candle.LowPrice;
_initialized = true;
return;
}
// Update extremes
if (candle.HighPrice > _highestPrice)
_highestPrice = candle.HighPrice;
if (candle.LowPrice < _lowestPrice)
_lowestPrice = candle.LowPrice;
var threshold = _prevRange * VolatilityMultiplier;
if (threshold <= 0)
return;
// Breakout above lowest + threshold => buy
if (candle.ClosePrice > _lowestPrice + threshold && Position <= 0)
{
BuyMarket();
// Reset extremes after entry
_highestPrice = candle.HighPrice;
_lowestPrice = candle.LowPrice;
}
// Breakout below highest - threshold => sell
else if (candle.ClosePrice < _highestPrice - threshold && Position >= 0)
{
SellMarket();
// Reset extremes after entry
_highestPrice = candle.HighPrice;
_lowestPrice = candle.LowPrice;
}
_prevRange = candle.HighPrice - candle.LowPrice;
}
}
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.Strategies import Strategy
class spasm_strategy(Strategy):
def __init__(self):
super(spasm_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for analysis", "General")
self._volatility_multiplier = self.Param("VolatilityMultiplier", 2.0) \
.SetDisplay("Volatility Multiplier", "Multiplier applied to ATR for breakout bands", "Trading")
self._highest_price = 0.0
self._lowest_price = float('inf')
self._prev_range = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def VolatilityMultiplier(self):
return self._volatility_multiplier.Value
def OnReseted(self):
super(spasm_strategy, self).OnReseted()
self._highest_price = 0.0
self._lowest_price = float('inf')
self._prev_range = 0.0
self._initialized = False
def OnStarted2(self, time):
super(spasm_strategy, self).OnStarted2(time)
self._highest_price = 0.0
self._lowest_price = float('inf')
self._prev_range = 0.0
self._initialized = False
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if not self._initialized:
self._highest_price = high
self._lowest_price = low
self._prev_range = high - low
self._initialized = True
return
if high > self._highest_price:
self._highest_price = high
if low < self._lowest_price:
self._lowest_price = low
threshold = self._prev_range * float(self.VolatilityMultiplier)
if threshold <= 0:
self._prev_range = high - low
return
if close > self._lowest_price + threshold and self.Position <= 0:
self.BuyMarket()
self._highest_price = high
self._lowest_price = low
elif close < self._highest_price - threshold and self.Position >= 0:
self.SellMarket()
self._highest_price = high
self._lowest_price = low
self._prev_range = high - low
def CreateClone(self):
return spasm_strategy()