GitHub で見る

Boring EA2アラート

概要

Boring EA2 Alertは、MetaTrader 4エキスパートアドバイザーboring-ea2の通知ロジックを再現します。戦略は確定ローソク足を監視し、3本の単純移動平均(SMA 3、SMA 20、SMA 150)を計算し、移動平均間でクロスが発生すると情報ログを出します。実装は意図的に注文配置を避けています。目的は、トレーダーが裁量執行や他の自動戦略と組み合わせられるタイムリーなアラートを提供することです。

戦略ロジック

移動平均の追跡

  • 短期バイアス: 3期間SMAは直近の価格変化に反応します。
  • 中期トレンド: 20期間SMAは短期スイングの範囲で価格を平滑化します。
  • 長期トレンド: 150期間SMAは支配的な背景トレンドを表します。

クロス検出

  • SMA3 vs SMA20: SMA3がSMA20を上回ると"crossed up"、下回ると"crossed down"を報告します。内部フラグにより各遷移は一度だけ報告されます。
  • SMA3 vs SMA150: 長期平均に対して同じロジックを反映し、支配的トレンドに対するモメンタム急増または反転を検出します。
  • SMA20 vs SMA150: 中長期の確認層を追加し、上位時間軸構造の変化が独自のアラートを発生させます。
  • 初期化ガード: 最初の確定ローソク足は初期状態を設定するだけです。真の関係変化が観測されると、2本目の確定ローソク足からアラートが始まります。

通知形式

  • アラートは元EAのメッセージを反映します: Alert!!! - SYMBOL - TF - description
  • 時間軸コードは設定されたローソク足タイプから導かれます。利用可能な場合はMetaTrader風の標準ラベル(M1、M5、H1など)を使い、その他の時間軸はコンパクト表記(例: M45またはD2)へフォールバックします。
  • メッセージはAddInfoLogで書き込まれ、ログビューアー、スクリプト、GUIダッシュボードへルーティングできます。

パラメーター

  • Short SMA Length: 高速移動平均の期間数(既定3)。
  • Medium SMA Length: 中間移動平均の期間数(既定20)。
  • Long SMA Length: 低速移動平均の期間数(既定150)。
  • Candle Type: 移動平均の計算に使う時間軸。既定は1分足で、EAのティックベース確認に近い高い反応性を持ちます。

追加メモ

  • 戦略は注文を送信、変更、キャンセルしません。純粋に情報提供用です。
  • Bindは確定値を供給するため、各クロスは確定ローソク足で評価されます。これにより、元EAがティック数で緩和していたノイズの多いバー内反転を避けます。
  • ログベース通知は、ホストアプリケーション内で戦略ログイベントを購読することでカスタムハンドラーと統合できます。
  • 現時点ではPython翻訳は提供されていません。APIパッケージにはC#版のみ含まれます。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Boring EA2 strategy: Triple SMA crossover.
/// Buys when fast crosses above medium while medium above slow.
/// Sells when fast crosses below medium while medium below slow.
/// </summary>
public class BoringEa2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;

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

	public BoringEa2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

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

		var fast = new SimpleMovingAverage { Length = 10 };
		var med = new SimpleMovingAverage { Length = 20 };
		var slow = new SimpleMovingAverage { Length = 40 };

		decimal? prevFast = null;
		decimal? prevMed = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, med, slow, (candle, fastVal, medVal, slowVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (prevFast.HasValue && prevMed.HasValue)
				{
					var fastCrossUp = prevFast.Value <= prevMed.Value && fastVal > medVal;
					var fastCrossDown = prevFast.Value >= prevMed.Value && fastVal < medVal;

					if (fastCrossUp && medVal > slowVal && Position <= 0)
						BuyMarket();
					else if (fastCrossDown && medVal < slowVal && Position >= 0)
						SellMarket();
				}

				prevFast = fastVal;
				prevMed = medVal;
			})
			.Start();

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