GitHub で見る

Standard Deviation Channel戦略

概要

この戦略は、MetaTraderのエキスパートStandard Deviation ChannelのStockSharpポートです。LWMA(線形加重移動平均)ベースのボラティリティチャネルを描画し、主要トレンドに沿ったブレイクアウトを取引します。エントリーはモメンタムの強さとMACDの確認によってフィルタリングされ、エグジットは固定ターゲット、ブレイクイーブンジャンプ、トレーリング保護を組み合わせます。

インジケーターとシグナル

  • 標準偏差チャネル:LWMAベースラインと設定可能な偏差乗数から構築されます。ロングセットアップは上部バンドが上向きであることを要求し、ショートセットアップは下部バンドが下向きであることを要求します。
  • トレンドフィルター: 同じローソク足で計算された高速・低速LWMA。ロングはLWMA_fast > LWMA_slowを要求し、ショートはその逆を要求します。
  • モメンタムフィルター: 14期間のモメンタムインジケーター。直近3回の読み取りのうち少なくとも1つが、設定されたしきい値によってニュートラルレベル100から逸脱している必要があります。
  • MACDフィルター: クラシックな12/26/9設定。ロングエントリーにはMACD ≥ signalが必要で、ショートエントリーにはMACD ≤ signalが必要です。

トレード管理

  • ポジションサイジング: TradeVolumeパラメーターを使用します。リバーサルは、新しい方向を開く前に反対のエクスポージャーを自動的にクローズします。
  • テイクプロフィットとストップロス: pips単位で表現され、インストゥルメントのPriceStepに対して評価されます。ローソク足の値幅がターゲットまたはストップ価格に触れると、ストラテジーはマーケット決済を発行します。
  • ブレイクイーブンジャンプ: 未実現利益がBreakEvenTriggerPipsに達すると、ストップはエントリー価格プラスBreakEvenOffsetPips(ショートの場合はマイナス)に移動します。
  • トレーリングストップ: TrailingStartPipsに達した後、ストップはTrailingStepPips分だけ価格を追跡し、両側で利益を確定します。
  • チャネル拒否エグジット: 価格がチャネル内に戻ってクローズし、傾きがポジションに対して平坦になると、トレードは早期クローズされます。

パラメーター

名前 説明
CandleType すべての計算に使用されるプライマリ時間軸。
TradeVolume ベース注文サイズ。
TrendLength チャネルベースラインを定義するLWMAルックバック。
DeviationMultiplier チャネル幅の標準偏差乗数。
FastMaLength / SlowMaLength トレンドフィルター用のLWMA長。
MomentumPeriod モメンタムフィルターのルックバック。
MomentumThreshold 直近3つのモメンタム値のいずれかで100からの最小偏差。
TakeProfitPips / StopLossPips 固定エグジットレベルの距離(PriceStepを使用して変換)。
BreakEvenTriggerPips / BreakEvenOffsetPips ブレイクイーブンストップのアクティベーション条件と方法を制御。
TrailingStartPips / TrailingStepPips トレーリングストップを有効化してサイズ設定。
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod MACD設定。
MaxPositionUnits 最大絶対ネットポジション;過剰レバレッジを防止。

使用上の注意

  1. 有効なPriceStepを公開するセキュリティにストラテジーをアタッチします。Pipsはこのステップ値を乗算することで変換されます。
  2. TrendLengthDeviationMultiplierを使用して、チャネルをさまざまな市場に適応させます。
  3. モメンタムとMACDフィルターを緩和(低いしきい値、短い期間)することで取引頻度を上げることができます。
  4. トレーリングロジックはローソク足のクローズで機能します;しきい値を超えないインバーバースパイクは無視されます。

オリジナルExpert Advisorとの違い

  • MetaTraderバージョンはグラフィックオブジェクトに依存してチャネルの傾きを読み取り、複数のマネーマネジメントブランチ(マーチンゲールサイジング、資本保護)を使用します。このポートは傾きチェックを維持しますが、リスクコントロールをMaxPositionUnitsで上限を設けた固定サイズ取引に簡略化します。
  • StockSharpストラテジーはMT4注文変更APIを直接ミラーしないため、すべてのエグジットはローソク足完了時のマーケット注文で処理されます。
  • メールとプッシュ通知は変換を自己完結させるためにAddInfoLogメッセージに置き換えられます。
  • 資本ベースのアカウントストップアウトは省略されました;代わりに、ポジションごとの保護機能に焦点が当てられています。

免責事項

このサンプルは教育目的を意図しています。ライブアカウントに展開する前に、常にフォワードテストを行い、設定を検証してください。

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;

public class StandardDeviationChannelStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public StandardDeviationChannelStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}