GitHub で見る

オージー・サーファー・リミテッドの戦略

概要

Aussie Surfer Ltd 戦略は、MetaTrader 5 エキスパート アドバイザー「Aussie Surfer Ltd」(MQL フォルダ 43278)の StockSharp の高レベル API 移植です。この戦略では、高速な Bollinger バンド反転と Alligator トレンド フィルターを組み合わせて、元の EA で使用されていた任意の設定を自動化します。取引は、戦略用に設定された主要商品で行われ、デフォルトでは 15 分間のローソク足シリーズで評価されます。

指標とデータ

  • Bollinger バンド (終値、デフォルトの長さ 5、幅 2.5) – 市場が一時的にバンドの外側に伸び、内側にスナップバックするタイミングを検出します。
  • 平滑移動平均 (長さ 21) – Alligator の「歯」ラインを再現してトレンドの減速を判断します。
  • 各ローソク足の中央価格 ((高値 + 安値) / 2) – 傾きが元の実装と一致するように、Alligator の計算にフィードします。

この戦略は、単一のローソク足ストリームをサブスクライブします。インジケーターの値は完成したローソク足のみによって駆動され、確認されたデータに基づいてシグナルが生成されることが保証されます。

取引ロジック

  1. エントリーセットアップ
    • 前のローソク足が下側の Bollinger バンドの上でオープンし、現在のローソク足が 2 バー前に観測されたバンド値の下でオープンすると、ロング ポジションがオープンします (ショート エクスポージャーを平坦化した後)。これにより、価格が下のバンドを突き抜けてすぐに内側に跳ね返る EA ロジックが再現されます。
    • 前のローソク足が上部 Bollinger バンドの下で開き、現在のローソク足が 2 バー前に観察されたバンド値の上で開いた場合、ショート ポジションがオープンされます (長時間露光を平坦化した後)。
  2. Alligator ベースの出口
    • Alligator の歯列は 1 バーと 2 バー後方で監視されます。ロングポジションは、傾きが下向きになるたびに清算されます(2 バー前の値が 1 バー前の値より大きくなります)。ショートポジションは、傾斜が上向きになったときに決済されます。
  3. リスク層
    • エントリー時に固定ピップのストップロスとテイクプロフィットが適用されます。どちらもオプションであり、ピップ距離をゼロに設定することで無効にできます。
    • オプションのトレーリングストップは、以前に完了したローソク足の高値 (ロングの場合) または安値 (ショートの場合) から設定されたピップ距離を引いた値またはプラスした値にストップロスを再調整します。トレーリング ロジックは、ストップロスが有効で、EnableTrailingStoptrue に設定されている場合にのみアクティブになります。

リスク管理

  • ストップロス – 証券価格ステップを使用して、設定されたピップ距離を価格単位に変換します。
  • テイクプロフィット – エントリー時に一度計算され、到達するか別のルールによってポジションがクローズされるまで静的に保持されます。
  • トレーリングストップ – より好ましい高値(ロングの場合)または安値(ショートの場合)が前のローソク足に現れたときに、ストップロスを進めます。
  • リバーサル処理 – 反対側のポジションがオープンしているときにシグナルが到着した場合、ストラテジーは 1 回の取引で完全に反転して新しいエクスポージャーを確立するサイズの成行注文を送信します。

パラメーター

パラメータ 説明 デフォルト
OrderVolume ロットまたは契約における基本取引サイズ。 0.30
StopLossPips ピップ単位の保護停止距離。 0 は停止を無効にします。 46
TakeProfitPips 利益目標距離 (pips)。 0 はターゲットを無効にします。 0
EnableTrailingStop ストップロスがアクティブな場合、pip ベースのトレーリングを有効にします。 true
BollingerPeriod Bollinger バンド ウィンドウの長さ。 5
BollingerDeviation バンドの標準偏差乗数。 2.5
TeethPeriod Alligator 歯列の平滑化移動平均長。 21
CandleType 計算に使用されるローソク足シリーズ (デフォルトでは 15 分の時間枠)。 15m キャンドル

すべての数値パラメータには最適化メタデータが含まれているため、Strategy Analyzer を介して調整できます。

実装メモ

  • 完成したキャンドルのみが処理されます。新しい各バーの開始時に実行される MetaTrader タイマー駆動の実行を模倣するために、未完了のデータは無視されます。
  • トレーリング ロジックには、正のストップロス距離が必要です。末尾のオプションが停止なしで有効になっている場合、初期化中に例外がスローされます。
  • チャート領域が利用可能になるとインジケーター インスタンスが自動的に描画され、StockSharp ポートが MetaTrader テンプレートと一致することを検証するのに役立ちます。

使用法

  1. 戦略を StockSharp 端末またはバックテスト環境に読み込みます。
  2. 取引セキュリティを設定し、ブローカーの契約仕様に一致するようにパラメーター (特にピップ距離) を調整します。
  3. 戦略を開始します。設定されたローソク足シリーズをサブスクライブし、完成した各ローソク足のエントリを評価し、記述されたルールを使用してポジションを管理します。

ライブ取引の場合、ブローカーが成行注文をサポートしていること、およびピップ変換が正確であるようにシンボルの PriceStep が利用可能であることを確認してください。

using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "Aussie Surfer Ltd" MetaTrader expert.
/// Uses Bollinger Band reversals with an SMA slope filter for entries.
/// </summary>
public class AussieSurferLtdStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerWidth;
	private readonly StrategyParam<int> _smaPeriod;

	private ExponentialMovingAverage _bandEma;
	private ExponentialMovingAverage _slopeEma;
	private decimal? _prevSma;
	private decimal? _prevClose;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	public decimal BollingerWidth
	{
		get => _bollingerWidth.Value;
		set => _bollingerWidth.Value = value;
	}

	public int SmaPeriod
	{
		get => _smaPeriod.Value;
		set => _smaPeriod.Value = value;
	}

	public AussieSurferLtdStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(120).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe", "General");

		_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Period", "Bollinger Bands window", "Indicators");

		_bollingerWidth = Param(nameof(BollingerWidth), 2.5m)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Width", "Standard deviation multiplier", "Indicators");

		_smaPeriod = Param(nameof(SmaPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("SMA Period", "SMA period for slope filter", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_bandEma = new ExponentialMovingAverage { Length = BollingerPeriod };
		_slopeEma = new ExponentialMovingAverage { Length = SmaPeriod };
		_prevSma = null;
		_prevClose = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_bandEma, _slopeEma, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _bandEma);
			DrawIndicator(area, _slopeEma);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal bandValue, decimal smaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_bandEma.IsFormed || !_slopeEma.IsFormed)
		{
			_prevClose = candle.ClosePrice;
			_prevSma = smaValue;
			return;
		}

		var close = candle.ClosePrice;
		var bandOffset = bandValue * (BollingerWidth / 100m);
		var upperBand = bandValue + bandOffset;
		var lowerBand = bandValue - bandOffset;

		if (_prevSma is null || _prevClose is null)
		{
			_prevSma = smaValue;
			_prevClose = close;
			return;
		}

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// SMA slope (uptrend or downtrend)
		var smaRising = smaValue > _prevSma.Value;
		var smaFalling = smaValue < _prevSma.Value;

		// Long: price was below lower band and crosses back above, SMA falling (reversal)
		var longSignal = _prevClose.Value < lowerBand && close >= lowerBand && smaFalling;
		// Short: price was above upper band and crosses back below, SMA rising (reversal)
		var shortSignal = _prevClose.Value > upperBand && close <= upperBand && smaRising;

		if (longSignal)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (shortSignal)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		// Exit at opposite band or SMA reversal
		if (Position > 0 && (close >= upperBand || (smaFalling && _prevSma.Value > smaValue)))
		{
			SellMarket(Position);
		}
		else if (Position < 0 && (close <= lowerBand || (smaRising && _prevSma.Value < smaValue)))
		{
			BuyMarket(Math.Abs(Position));
		}

		_prevSma = smaValue;
		_prevClose = close;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_bandEma = null;
		_slopeEma = null;
		_prevSma = null;
		_prevClose = null;

		base.OnReseted();
	}
}