GitHub で見る

FatPanel ビジュアルビルダー戦略

FatPanel ビジュアルビルダー戦略は、MetaTraderのレガシーFAT Panel エキスパートアドバイザーのStockSharp変換版です。元のMQL実装は、ユーザーがインジケーター、ロジック、状態、および注文ブロックをリンクできるドラッグアンドドロップのキャンバスを公開していました。このC#ポートは同じモジュール哲学を維持しますが、全てのブロック接続を戦略が起動時に読み込む単一のJSONドキュメントを通じて表現します。

変換のしくみ

  • MQLパネルはボタン、タブ、タイマーベースのディスパッチャーを作成しました。これらのUI関連は完全に削除されます。代わりに、戦略はConfigurationパラメーター(JSON文字列)を解析し、対応するシグナルとロジックブロックを内部的にインスタンス化します。
  • ブロックは設定されたCandleTypeの完成した各ローソク足で評価されます。インジケーターブロックはStockSharpのインジケーター(SMAEMASMMAWMA)を使用し、手動バッファには依存しません。
  • 元の注文ブロックはシンボル選択、ストップロス、テイクプロフィットを「ポイント」で許可していました。StockSharpでは、デフォルトのセキュリティはStrategy.Securityから取得され、ストップロスとテイクプロフィットは戦略パラメーターStopLossPointsTakeProfitPointsを通じて再導入され、Security.PriceStepを使用して絶対価格距離に変換されます。
  • 時間と曜日の状態フィルターはMQLロジックを反映します。最善の買い値シグナルは、少なくとも1つのルールがそれを要求する場合にのみLevel1データにサブスクライブし、パネルディスパッチャーのオンデマンド更新動作を再現します。

パラメーター

パラメーター 説明
CandleType 各シグナルを供給するデータタイプと時間軸。
Configuration ルール、条件、アクションを説明するJSONドキュメント。デフォルト値はパネルのEMA/SMAクロス戦略のサンプルを再現します。
Volume ルールがそれを上書きしない限りアクションで使用されるデフォルトの注文サイズ。
StopLossPoints 組み込みリスク保護のための価格ステップでの距離。ストップロスを無効にするには0に設定。
TakeProfitPoints 組み込みテイクプロフィットのための価格ステップでの距離。無効にするには0に設定。

StopLossPointsTakeProfitPoints は、正の値が供給されかつセキュリティが有効なPriceStepを公開している場合にのみ起動されます。

設定構造

JSONスキーマはFAT Panelブロック言語に近いように設計されています:

{
  "rules": [
    {
      "name": "ルール名(オプション)",
      "all": [ /* 全て真でなければならない条件 */ ],
      "any": [ /* オプション条件、少なくとも1つが真でなければならない */ ],
      "none": [ /* オプション条件、全て偽でなければならない */ ],
      "action": { "type": "Buy" | "SellShort" | "Close", "volume": 1.0 }
    }
  ]
}

各条件アイテムには次のいずれかの値を持つtypeフィールドがあります:

タイプ JSONフィールド 目的
comparison operator, left, right, threshold 論理演算子(GreaterLessEqualCrossAboveCrossBelow)を通じて2つのシグナルブロックを接続します。閾値は絶対価格差として解釈されます。クロス演算子は前のローソク足が反対側にあり、現在の差が閾値を超えると発火します。
position required FAT Panelの状態ブロックを反映します(AnyFlatOnlyFlatOrShortFlatOrLongLongOnlyShortOnly)。
time start, end HH:mm形式のイントラデイセッションフィルター。開始 > 終了はMQLパネルの夜間動作を維持します。
dayOfWeek days 曜日名のリスト。省略すると条件はデフォルトで月曜から金曜となり、パネルのデフォルトに一致します。

シグナル(left / right)は次のように定義されます:

{ "type": "MovingAverage", "period": 20, "method": "Exponential", "price": "Close" }
{ "type": "Bid" }
{ "type": "Constant", "level": 1.2345 }
  • MovingAverageSimpleExponentialSmoothedLinearWeightedの手法をOHLC価格ソースのいずれかで支援します。インジケーターはパネルがチャートで選択した時間軸を使用したのと同様に、戦略のローソク足ストリームを共有します。
  • Bid はLevel1更新からの最新の最良の買い値を使用します(相場が到着するまでローソク足のクローズにフォールバックします)。
  • Constant はHLINEブロックを再現し、静的レベルを生成します。

ルールアクションは注文ブロックを再現します:

  • Buy – 現在のポジションがフラットまたはショートの場合にロングポジションを開くか反転します。
  • SellShort – ポジションがフラットまたはロングの場合にショートポジションを開くか反転します。
  • CloseClosePosition() を使用してオープンポジションをエグジットします。

アクションごとのvolumeはデフォルトのVolumeパラメーターを上書きできます。

実行フロー

  1. 戦略が開始すると設定JSONを解析します。無効なドキュメントは戦略を停止し、エラーログを発行します。
  2. インジケーターはインスタンス化されてキャッシュされるため、複数のルールが重複計算なしに同じシグナル定義を再利用できます。
  3. 完成した各ローソク足について戦略はシグナル値を更新し、次に各ルールを順に評価します。all条件は全て通過する必要があり、anyは少なくとも1回通過する必要があり(提供された場合)、noneは完全に失敗する必要があります。
  4. アクションがトリガーされると戦略はルール名をログに記録し、リクエストされた成行注文を実行します。
  5. オプションのストップロスとテイクプロフィットの保護は、供給されたポイント距離を使用してOnStarted中に一度起動されます。

制限と注意事項

  • 主要なStrategy.Securityのみがサポートされています。元のパネルからのクロスシンボルルーティングは複数の戦略インスタンスを必要とします。
  • MQLディスパッチャーはロジックブロックの深いネスト(例:OR内のAND)を許可していました。JSON構造はall/any/none配列を通じて同様の制御を提供しますが、非常に複雑なグラフはまだ手動の適応が必要な場合があります。
  • Cross演算子は最後のローソク足のみを使用します。MQLブロックはルックバックバッファと「ポイント」のデルタを公開していました。必要な感度を模倣するためにthresholdフィールドを適応させてください。
  • ドラッグ位置、ダイアログウィンドウ、ツールバーアイコンなどのUI機能はStockSharpに直接相当するものがなく、意図的に省略されています。

サンプル設定

戦略に埋め込まれたデフォルト設定は便宜のために以下に再現されます:

{
  "rules": [
    {
      "name": "EMA crosses above SMA",
      "all": [
        {
          "type": "comparison",
          "operator": "CrossAbove",
          "left": { "type": "MovingAverage", "period": 20, "method": "Exponential", "price": "Close" },
          "right": { "type": "MovingAverage", "period": 50, "method": "Simple", "price": "Close" }
        },
        { "type": "dayOfWeek", "days": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] },
        { "type": "time", "start": "09:00", "end": "17:00" },
        { "type": "position", "required": "FlatOrShort" }
      ],
      "action": { "type": "Buy" }
    },
    {
      "name": "EMA crosses below SMA",
      "all": [
        {
          "type": "comparison",
          "operator": "CrossBelow",
          "left": { "type": "MovingAverage", "period": 20, "method": "Exponential", "price": "Close" },
          "right": { "type": "MovingAverage", "period": 50, "method": "Simple", "price": "Close" }
        },
        { "type": "position", "required": "LongOnly" }
      ],
      "action": { "type": "Close" }
    }
  ]
}

このサンプルは株式パネルテンプレートを反映しています:通常セッション中の20/50 EMA-SMA強気クロスでロングポジションを開き、逆クロスでポジションをクローズします。

using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// FatPanel Visual Builder strategy. Uses SMA crossover for signal generation.
/// </summary>
public class FatPanelVisualBuilderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal? _prevFast;
	private decimal? _prevSlow;

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

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public FatPanelVisualBuilderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 15)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

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

		_prevFast = null;
		_prevSlow = null;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		if (_prevFast == null || _prevSlow == null)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fastVal > slowVal;

		_prevFast = fastVal;
		_prevSlow = slowVal;

		if (!prevAbove && currAbove && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (prevAbove && !currAbove && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}