GitHub で見る

Vector戦略

概要

Vector戦略は、MetaTrader 5の「Vector」エキスパートから変換されたマルチ通貨トレンドフォロー システムです。EURUSD、GBPUSD、USDCHF、USDJPYの4つの主要フォレックスペアを同時に取引します。各ペアの中央値価格に基づいて平滑移動平均を計算し、複合トレンドが同じ方向を向いているときに同期ポジションをオープンします。4時間ボラティリティに基づく動的pipsターゲットとポートフォリオレベルの利益・損失閾値が出口を制御します。

中心的なアイデア

  • 各通貨ペアの方向を測定するために、中央値価格に基づいた平滑移動平均(SMMA)を使用します。
  • すべてのインストゥルメントの速い平均と遅い平均を合計して、共通の強気または弱気バイアスを決定します。
  • グローバルバイアスとローカルの速い/遅いクロスオーバーが一致したとき、ペアごとに単一の成行注文を入力します。
  • EURUSDの50本の完成した4時間ロウソク足の平均レンジから導出された浮動pipsターゲットでポジションを管理します。
  • 浮動利益または損失が開始残高の設定割合に達したとき、すべての取引を同時にクローズします。

パラメーター

パラメーター 説明
Fast MA 各ペアの速いトレンドに使用する平滑移動平均の長さ。
Slow MA 各ペアの遅いトレンドに使用する平滑移動平均の長さ。
MA Shift シグナルを評価する前に必要な追加の完成ロウソク足の数。元のEAのシフト設定を反映。
Equity Take Profit % すべてのオープンポジションのクローズをトリガーする浮動利益割合。
Equity Stop Loss % すべての取引の緊急終了をトリガーする浮動損失割合。
Signal Timeframe 平滑移動平均に使用するロウソク足時間軸(デフォルト15分)。
Range Timeframe ボラティリティ平均化に使用するロウソク足時間軸(デフォルト4時間)。
Range Period 平均pipsターゲット計算に使用する上位時間軸ロウソク足の数。
EURUSD / GBPUSD / USDCHF / USDJPY 各取引インストゥルメントに対応する証券。

すべてのパラメーターは、該当する場合は元のエキスパートアドバイザーと同一の最適化範囲をサポートします。

トレードロジック

  1. インジケーター更新 — 取引時間軸の完成した各ロウソク足が、対応するペアの速い・遅い平滑移動平均を更新します。設定されたウォームアップ(MA Shift)が完了した後にのみ値が考慮されます。
  2. バイアス計算 — 戦略はすべてのペアの最新の速い平均を合計し、遅い平均の合計を引きます。正の結果は強気圧力を示し、負の結果は弱気圧力を示します。
  3. エントリー条件 — あるペアにポジションが存在しない場合、グローバルバイアスが強気でそのペアの速い平均が遅い平均より上であれば買い注文を入力します。逆の場合は売り注文をオープンします。
  4. pipsターゲット出口 — EURUSDの4時間サブスクリプションは設定期間にわたる平均ロウソク足レンジを計算します。現在のpipsターゲットはこの平均と13pipsの大きい方です。ロングは価格がターゲット数のpipsを少なくとも獲得したときにクローズし、ショートは同等の有利な動きの後にクローズします。
  5. 資金保護 — 浮動利益がテイクプロフィット割合を超えるか、浮動損失がストップロス割合を超えるたびに、戦略はすべての管理対象ポジションを即座にクローズします。

使用上の注記

  • 4つのフォレックスインストゥルメントすべてへのアクセスを提供するポートフォリオに戦略をアタッチし、各証券パラメーターを明示的に設定します。
  • デフォルトのシグナル時間軸は15分です。各通貨ペアに一致するロウソク足が利用可能であることを確認してください。
  • いつでもペアごとに1つのオープンポジションのみが維持されます。ベース戦略のボリュームパラメーターがすべてのエントリーに使用されます。
  • 出口が浮動損益に依拠するため、この戦略はバーバイバーのバックテストだけでなく継続的な運用を目的としています。
  • 動的pipsターゲットは元の実装に沿ってEURUSDのボラティリティを使用します。異なる市場環境にターゲットを適応させたい場合は、レンジ時間軸またはピリオドを調整してください。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Trend strategy using smoothed moving averages. Simplified from multi-currency Vector.
/// </summary>
public class VectorStrategy : Strategy
{
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _maShift;
	private readonly StrategyParam<decimal> _profitPercent;
	private readonly StrategyParam<decimal> _lossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private SmoothedMovingAverage _fastMa;
	private SmoothedMovingAverage _slowMa;
	private decimal _entryPrice;
	private decimal _initialBalance;
	private int _processedBars;

	/// <summary>
	/// Fast smoothed moving average period.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastMaPeriod.Value;
		set => _fastMaPeriod.Value = value;
	}

	/// <summary>
	/// Slow smoothed moving average period.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

	/// <summary>
	/// Additional warm-up shift in bars.
	/// </summary>
	public int MaShift
	{
		get => _maShift.Value;
		set => _maShift.Value = value;
	}

	/// <summary>
	/// Floating profit target percent of balance.
	/// </summary>
	public decimal ProfitPercent
	{
		get => _profitPercent.Value;
		set => _profitPercent.Value = value;
	}

	/// <summary>
	/// Floating loss limit percent of balance.
	/// </summary>
	public decimal LossPercent
	{
		get => _lossPercent.Value;
		set => _lossPercent.Value = value;
	}

	/// <summary>
	/// Candle type for signals.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public VectorStrategy()
	{
		_fastMaPeriod = Param(nameof(FastMaPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA", "Fast smoothed moving average period", "Indicators")
			.SetOptimize(3, 15, 1);

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA", "Slow smoothed moving average period", "Indicators")
			.SetOptimize(5, 25, 1);

		_maShift = Param(nameof(MaShift), 8)
			.SetNotNegative()
			.SetDisplay("MA Shift", "Additional warm-up bars before signals", "Indicators");

		_profitPercent = Param(nameof(ProfitPercent), 0.5m)
			.SetNotNegative()
			.SetDisplay("Equity TP %", "Close all when floating profit reaches this percent", "Risk");

		_lossPercent = Param(nameof(LossPercent), 30m)
			.SetNotNegative()
			.SetDisplay("Equity SL %", "Close all when floating loss reaches this percent", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Signal Timeframe", "Timeframe for moving averages", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, CandleType);
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_fastMa = null;
		_slowMa = null;
		_entryPrice = 0m;
		_initialBalance = 0m;
		_processedBars = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_fastMa = new SmoothedMovingAverage { Length = FastMaPeriod };
		_slowMa = new SmoothedMovingAverage { Length = SlowMaPeriod };
		_initialBalance = Portfolio?.CurrentValue ?? 0m;

		SubscribeCandles(CandleType)
			.Bind(_fastMa, _slowMa, ProcessCandle)
			.Start();
	}

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

		_processedBars++;

		if (!IsFormed)
			return;

		if (_processedBars <= MaShift)
			return;

		// Check equity thresholds
		if (Position != 0 && _initialBalance > 0m)
		{
			var equity = Portfolio?.CurrentValue ?? 0m;
			var floating = equity - _initialBalance;
			var profitThreshold = _initialBalance * ProfitPercent / 100m;
			var lossThreshold = _initialBalance * LossPercent / 100m;

			if ((profitThreshold > 0m && floating >= profitThreshold) ||
				(lossThreshold > 0m && floating <= -lossThreshold))
			{
				if (Position > 0) SellMarket();
				else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
				return;
			}
		}

		// Entry/exit logic
		if (fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
			_entryPrice = candle.ClosePrice;
		}
		else if (fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
			_entryPrice = candle.ClosePrice;
		}
	}
}