GitHub で見る

AeroSpine戦略

概要

AeroSpine戦略は、MetaTraderのエキスパートAEROSPINE.mq4の変換です。単一シンボルを取引し、日次始値からのブレイクアウトを捉えようとします。元のロボットはティクスを監視しながら日足チャート向けに設計されていましたが、このポートは日次始値ブレイクアウトのアイデアを維持しながら、StockSharpが提供する完成したローソク足に依存します。

取引ロジック

  • 毎取引日の開始時に、ストラテジーはその日の最初のローソク足から導出される日次始値を保存します。
  • 新しいポジションは、設定されたエントリー時間後にのみ評価されます。完成したローソク足は最小ボリュームフィルターを満たし、現在のスプレッドは設定された上限を下回っている必要があります。
  • ポジションが開いておらず、リカバリートレードが保留中でない場合:
    • ローソク足の高値がEntryOffsetPips分だけ日次始値を上回るとロングトレードが開かれます。
    • ローソク足の安値がEntryOffsetPips分だけ日次始値を下回るとショートトレードが開かれます。
  • 損失トレードの後、ストラテジーは反対方向のリカバリーエントリーを準備します。リカバリートレードはRecoveryOffsetPipsを使用し、ベースボリュームに損失トレードのサイズを加算することでボリュームを増やし、MQLエキスパートのマーチンゲール風サイジングを再現します。
  • オープンポジションは3つのメカニズムで管理されます:
    • エントリー価格からTakeProfitPipsの固定テイクプロフィット。
    • ポジションに有利な方向に動いた後、ブレイクイーブン距離まで価格が戻るとトレードを閉じるオプションのブレイクイーブントリガー。
    • 価格が日次始値に戻り、ExitOffsetPips分だけポジションに逆らってクロスした場合の保護的エグジット。

パラメーター

名前 説明
Candle Type シグナル評価に使用する作業ローソク足の時間軸。
Volume 最初のエントリーとリカバリーボリューム構築に使用するベース注文サイズ。
Entry Hour 新しいエントリーが可能な最低時間(取引所時間)。
Entry Offset その日の最初のトレードを開くために超える必要がある、日次始値からのpips距離。
Exit Offset 始値に戻ってクロスするポジションをクローズするための、日次始値を超えたpips距離。
Recovery Offset 損失後のリカバリートレードをトリガーするための、日次始値からのpips距離。
Take Profit エントリー価格からpips単位で測定された固定テイクプロフィット距離。
Break Even ブレイクイーブンエグジットを起動するために必要なpips距離。
Use Break Even ブレイクイーブン管理ブロックを有効または無効にします。
Volume Filter 新しいエントリーに必要な最小ローソク足ボリューム(元のVolume[0] > 10000チェックを反映)。
Max Spread 現在のスプレッドが許可値(pipsから変換)より広い場合、新しいエントリーを拒否します。
Enable Recovery 損失トレード後の反対方向リカバリーロジックを有効にします。

変換に関する注記

  • 元のEAはティクスで直接注文を配置しながら日足チャートを適用していました。ポートはイントラデイローソク足でこれを模倣します:日次始値は各日の最初のローソク足で更新され、ブレイクアウトチェックはローソク足の高値/安値を使用します。
  • MetaTraderのすべてのインターフェース要素(ラベル、複数シンボルにわたる資本計算など)は削除されました。現在のシンボルに関連する取引ロジックのみが保持されています。
  • ブレイクイーブンとストップ変更(OrderModify)は、計算されたしきい値に触れると明示的なClosePosition()呼び出しによってシミュレートされます。
  • スプレッドとボリュームフィルターは、元のMODE_SPREADVolume[0]チェックに直接マッピングされます。
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 AeroSpineStrategy : 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 AeroSpineStrategy()
	{
		_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;
	}
}