GitHub で見る

通貨強度 v1.1戦略

概要

通貨強度 v1.1戦略はMetaTraderのエキスパートアドバイザーCurrency Strength v1.1を再現します。26の流動性の高いFXペアの日次パーセンテージ変化を使用して、8つの主要通貨(USD、EUR、JPY、CAD、AUD、NZD、GBP、CHF)の相対的な強さを測定します。2つの通貨の強さが設定可能な閾値を超えて乖離するたびに、戦略は対応する通貨ペアに強い通貨の方向でポジションを開きます。

市場とデータ

  • インストゥルメントユニバース: 26の主要およびクロスFXペア(USDJPY、USDCAD、AUDUSD、USDCHF、GBPUSD、EURUSD、NZDUSD、EURJPY、EURCAD、EURGBP、EURCHF、EURAUD、EURNZD、AUDNZD、AUDCAD、AUDCHF、AUDJPY、CHFJPY、GBPCHF、GBPAUD、GBPCAD、GBPJPY、CADJPY、NZDJPY、GBPNZD、CADCHF)。
  • データ頻度: 日足ローソク足(D1)。一貫した計算を維持するために、完成したローソク足のみが処理されます。
  • 必須フィールド: 各ローソク足の始値、高値、安値、終値。

通貨強度の計算

各ペアの日次パーセンテージ変化は以下のように計算されます:

(change) = (Close − Open) / Open × 100

これらのペア固有の変化はその後、通貨強度インデックスに結合されます:

  • EUR強度 = EURJPY、EURCAD、EURGBP、EURCHF、EURAUD、EURUSD、EURNZDの平均
  • USD強度 = USDJPY、USDCAD、–AUDUSD、USDCHF、–GBPUSD、–EURUSD、–NZDUSDの平均
  • JPY強度 = USDJPY、EURJPY、AUDJPY、CHFJPY、GBPJPY、CADJPY、NZDJPYの負の平均
  • CAD強度 = CADCHF、CADJPY、–GBPCAD、–AUDCAD、–EURCAD、–USDCADの平均
  • AUD強度 = AUDUSD、AUDNZD、AUDCAD、AUDCHF、AUDJPY、–EURAUD、–GBPAUDの平均
  • NZD強度 = NZDUSD、NZDJPY、–EURNZD、–AUDNZD、–GBPNZDの平均
  • GBP強度 = GBPUSD、–EURGBP、GBPCHF、GBPAUD、GBPCAD、GBPJPY、GBPNZDの平均
  • CHF強度 = CHFJPY、–USDCHF、–EURCHF、–AUDCHF、–GBPCHF、–CADCHFの平均

各平均は元のエキスパートアドバイザーと同じ数のコンポーネントを使用して、重み付けスキームを保持します。

トレードロジック

  1. 26のすべてのペアが新しい完成した日次ローソク足を報告した後、強度が再計算されます。
  2. 各ペアに対して、戦略は2つの関連する通貨強度を比較します。絶対差がDifferenceThresholdパラメーターを超えると、トレードシグナルが生成されます。
  3. シグナルの方向は強い通貨に従います:
    • 基準通貨強度 > 決済通貨強度 → ペアを買う。
    • 基準通貨強度 < 決済通貨強度 → ペアを売る。
  4. 取引はペアの日足ローソク足がシグナルと一致する場合(買いでは終値が始値より上、売りでは終値が始値より下)にのみ許可され、元のEAのトレンドフィルターを反映します。
  5. 既存のネットポジションが尊重されます。反対のポジションがオープンな状態で反転シグナルが現れると、戦略は現在のポジションを閉じ、1つの成行注文で新しい方向に切り替えます。
  6. TradeOncePerDayが有効な場合、各ペアは取引日ごとに最大1回のロングエントリーと最大1回のショートエントリーが可能です。

リスク管理と決済

  • オプションのUseSlTpフラグは各ペアの日次ローソク足で実行されるストップロスとテイクプロフィットロジックを有効にします。距離はpipで定義されます(StopLossPipsTakeProfitPips)。
  • 保護ロジックは最新のローソク足の日次高値/安値を評価します。これらの極値がそれぞれの目標に達すると、ポジションは次の評価ステップで市場価格で閉じられます。
  • SL/TPなしでは、反対のシグナルが反転を強制するか戦略が手動で停止されるまでポジションはオープンのままで、ソースEAの動作を反映します。

戦略パラメーター

パラメーター 説明
CandleType ローソク足の時間軸(デフォルト:日足)。
DifferenceThreshold トレードをトリガーするために必要な最小強度差(パーセンテージポイント)。
TradeOncePerDay trueの場合、各ペアを1日1回のロングエントリーと1回のショートエントリーに制限します。
UseSlTp ストップロスとテイクプロフィットレベルの日次評価を有効にします。
TakeProfitPips pipで測定したテイクプロフィット距離。
StopLossPips pipで測定したストップロス距離。
ペアパラメーター 26のFXペアの個別Security入力。戦略を開始する前に各々を割り当てる必要があります。
Volume トレードサイズを定義する基底クラスプロパティ(デフォルト0.01ロット)。

実装メモ

  • 戦略は高レベルのローソク足サブスクリプションAPI(SubscribeCandles)を使用して各ペアに個別にサブスクライブします。
  • ローソク足の処理は不完全なローソク足を厳密に無視し、StockSharpの変換ガイドラインを満たします。
  • 強度計算とシグナル生成は、すべてのペアが同じ取引日を報告したときにのみ実行され、同期された通貨バスケットを保証します。
  • 内部辞書は方向ごとの最後の取引日を追跡し、保護的な決済のためにエントリー情報を保存します。

使用のヒント

  1. 戦略を開始する前にすべての26のインストゥルメントを割り当ててください。入力が欠けると部分的な計算を防ぐために例外がスローされます。
  2. データプロバイダーが設定されたすべてのペアに日次ローソク足を供給することを確認し、通貨強度を同期させてください。
  3. DifferenceThresholdを調整してシグナル頻度を制御します。小さい閾値は頻繁なトレードにつながりますが、反転も多くなります。
  4. pipベースのストップをブローカーの見積もり精度に合わせて調整してください。デフォルトはフラクショナルpip価格設定を想定しています。
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>
/// Strategy that trades based on percentage change momentum of candles.
/// Simplified from the original multi-pair currency strength approach to single-security.
/// </summary>
public class CurrencyStrengthV11Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _differenceThreshold;

	private decimal? _prevChange;
	private decimal? _prevMomentum;
	private decimal _entryPrice;
	private DateTimeOffset? _lastTradeTime;

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

	/// <summary>
	/// Minimum percentage change to trigger trades.
	/// </summary>
	public decimal DifferenceThreshold
	{
		get => _differenceThreshold.Value;
		set => _differenceThreshold.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public CurrencyStrengthV11Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for strength calculation", "General");

		_differenceThreshold = Param(nameof(DifferenceThreshold), 0.2m)
			.SetDisplay("Threshold", "Minimum percentage change to trigger trade", "Parameters")
			.SetOptimize(0.05m, 1m, 0.05m);
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevChange = null;
		_prevMomentum = null;
		_entryPrice = 0m;
		_lastTradeTime = null;
	}

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

		SubscribeCandles(CandleType)
			.Bind(ProcessCandle)
			.Start();
	}

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

		if (!IsFormed)
			return;

		var change = candle.OpenPrice != 0m
			? (candle.ClosePrice - candle.OpenPrice) / candle.OpenPrice * 100m
			: 0m;

		if (_prevChange == null)
		{
			_prevChange = change;
			return;
		}

		var momentum = change - _prevChange.Value;
		var cooldownPassed = _lastTradeTime is null || candle.CloseTime - _lastTradeTime >= TimeSpan.FromHours(24);
		var longSignal = _prevMomentum is decimal prevMomentum && prevMomentum <= DifferenceThreshold && momentum > DifferenceThreshold;
		var shortSignal = _prevMomentum is decimal prevMomentum2 && prevMomentum2 >= -DifferenceThreshold && momentum < -DifferenceThreshold;

		if (cooldownPassed && longSignal && Position <= 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			BuyMarket(Volume > 0m ? Volume : 1m);
			_entryPrice = candle.ClosePrice;
			_lastTradeTime = candle.CloseTime;
		}
		else if (cooldownPassed && shortSignal && Position >= 0)
		{
			if (Position > 0)
				SellMarket(Position);
			SellMarket(Volume > 0m ? Volume : 1m);
			_entryPrice = candle.ClosePrice;
			_lastTradeTime = candle.CloseTime;
		}

		_prevChange = change;
		_prevMomentum = momentum;
	}
}