GitHub で見る

システムチャンピオンシップ戦略

概要

  • MetaTrader 4 エキスパート アドバイザー「A システム: チャンピオンシップ戦略最終編集」(ファイル ACB6.MQ4) の移植。
  • 設定可能なプライマリタイムフレームで強気または弱気のブレイクアウトを検出し、ライブビッド/アスク価格で勢いを確認します。
  • セカンダリ タイムフレームを使用してトレーリング ストップの距離を設定し、2 つのローソク足ストリームを通じて元の EA のマルチスレッド ロジックを再現します。
  • ソース ロボットにハードコードされたグローバル株式ストップ、取引一時停止、および適応型リスク サイジング ブロックを実装します。

データサブスクリプション

  • 2 つのローソク足シリーズ (PrimaryTimeFrameSecondaryTimeFrame) をサブスクライブして、ターゲットとトレーリング ストップに使用される価格帯を再構築します。
  • エントリー、ストップロスチェック、テイクプロフィット、リトレースメントエグジットをトリガーする最良の買値/売値を読み取るために、レベル 1 の相場を購読します。

エントリー条件

  1. 主ローソク足が終了するのを待ち、その範囲に TakeFactor を掛けた値を計算します。
  2. 次の場合はロングになります。
    • ローソク足は中点を超えて終了します。
    • 現在の売値はローソク足の高値を突破します。
    • 入札とローソク足の安値の間の距離が MinStopDistance を超えています。
  3. 下値ブレイクアウトのミラー条件が当てはまる場合はショートします。
  4. 計算されたテイクプロフィット距離が最小ストップ間隔より小さい場合は、エントリをスキップします。

出口管理

  • 初期保護レベル: ストップは前のローソク足の安値/高値に固定されており、テイクプロフィットはエントリー価格にレンジを乗じた値にTakeFactorを乗算した値をプラス/マイナスしたものに等しくなります。
  • リトレースメント出口 (FallLimit/FallFactor):
    • アクティブな位置の最大の有利な偏位を追跡します。
    • 現在の動きが FallLimit * maxMove を下回り、かつすでに動きが FallFactor * target を超えている場合は、成行での取引を終了します。
  • トレーリングストップ (TrailFactor):
    • トレーリング距離は、2 番目のタイムフレーム範囲に TrailFactor を掛けたものと等しくなります。
    • ストップは取引方向にのみ移動し、テイクプロフィットや最小ストップ間隔を横切ることはありません。
  • ハード ストップ: 維持されたストップまたはテイク レベルに価格が接触すると、成行注文を使用して即座に清算されます。

リスク管理

  • 動的位置サイジング: RiskPerTrade と、Security.StepSize および Security.StepPrice から派生した pip 値を組み合わせます。結果のボリュームは交換制約に合わせて四捨五入され、BaseVolume を下回ることはありません。
  • 統計調整: 元の EA からの比率 LossesExpected/TradesExpected は、実現損失率と比較することで取引ごとのリスクを調整します。
  • 株式ストップ (SystemStop): 株式のピークを追跡し、現在の値が SystemStop * peak を下回った場合、新しい取引を無効にします。情報ログは、アクティベーションとリカバリの停止をマークします。
  • 取引一時停止 (TradePause): MT4 実装と同様に、成行注文ごとにクールダウン ウィンドウを強制します。

パラメーター

名前 デフォルト 説明
PrimaryTimeFrame 1日 ブレイクアウト検出に使用されるより長い時間枠。
SecondaryTimeFrame 4時間 トレーリングストップ範囲を提供するタイムフレーム。
TakeFactor 0.8 テイクプロフィットを作成するときに主要なローソク足の範囲に適用される乗数。
TrailFactor 10 トレーリングストップを更新するときにセカンダリローソク足の範囲に適用される乗数。
FallLimit 0.5 リトレースメントエグジットを可能にする最大利益の比率。
FallFactor 0.4 リトレースメント出口が許可される前に到達する必要がある完全なターゲットの最小シェア。
RiskPerTrade 0.02 調整前の各取引に割り当てられた資本の割合。
BaseVolume 1 リスクサイジングによってより小さなボリュームが得られる場合に使用されるフォールバック注文サイズ。
MinStopDistance 0 価格単位で表される交換停止距離の制約。
TradePause 5分 注文が実行された後の待機期間。
SystemStop 0.8 ポートフォリオのエクイティストップのドローダウン係数 (例: 0.8 = 20% の許容ドローダウン)。
LossesExpected 20 リスク調整のための損失トレードの予想数。
TradesExpected 85 リスク調整のために予想される合計取引数。

実装メモ

  • StockSharp 戦略はネット ポジションで動作するため、MQL バージョンのロック/ヘッジ スレッドは省略されます。リスク制御とトレーリング ロジックは、同等の資本保護メカニズムを提供します。
  • ストップ レベルとテイク レベルは、バックテスト エンジンとの連携を維持するために個別のネイティブ注文を使用するのではなく、戦略内で追跡されます。
  • 接続されたセキュリティが StepSizeStepPriceMinVolume、および VolumeStep を公開していることを確認します。それ以外の場合、サイズ設定は BaseVolume に戻ります。
  • この戦略は、リアルタイム相場を有効にして実行する必要があります。それ以外の場合は、キャンドル駆動の更新のみが実行され、停止ロジックはキャンドルのレイテンシーに反応します。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Multi-timeframe breakout strategy converted from the original MetaTrader "A System" expert advisor.
/// Enters on momentum breakouts when close is above/below the midpoint of the previous candle range.
/// Uses ATR-based trailing stop for position management.
/// </summary>
public class ASystemChampionshipStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _takeFactor;
	private readonly StrategyParam<decimal> _trailFactor;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _prevClose;
	private bool _hasPrev;
	private decimal _entryPrice;
	private decimal _stopPrice;

	public ASystemChampionshipStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for breakout detection.", "General");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetDisplay("ATR Period", "Period for ATR used in trailing stop.", "Indicators");

		_takeFactor = Param(nameof(TakeFactor), 2.5m)
			.SetDisplay("Take Factor", "ATR multiplier for take profit.", "Risk");

		_trailFactor = Param(nameof(TrailFactor), 1.5m)
			.SetDisplay("Trail Factor", "ATR multiplier for trailing stop.", "Risk");
	}

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

	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	public decimal TakeFactor
	{
		get => _takeFactor.Value;
		set => _takeFactor.Value = value;
	}

	public decimal TrailFactor
	{
		get => _trailFactor.Value;
		set => _trailFactor.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevHigh = 0;
		_prevLow = 0;
		_prevClose = 0;
		_hasPrev = false;
		_entryPrice = 0;
		_stopPrice = 0;
	}

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

		_prevHigh = 0;
		_prevLow = 0;
		_prevClose = 0;
		_hasPrev = false;
		_entryPrice = 0;
		_stopPrice = 0;

		var atr = new AverageTrueRange { Length = AtrPeriod };

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

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

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

		if (atrValue <= 0)
		{
			SaveCandle(candle);
			return;
		}

		// Manage open position
		if (Position > 0)
		{
			// Trail stop up
			var newStop = candle.ClosePrice - atrValue * TrailFactor;
			if (newStop > _stopPrice)
				_stopPrice = newStop;

			if (candle.LowPrice <= _stopPrice)
			{
				SellMarket();
				ResetPosition();
			}
			else if (_entryPrice > 0 && candle.HighPrice >= _entryPrice + atrValue * TakeFactor)
			{
				SellMarket();
				ResetPosition();
			}
		}
		else if (Position < 0)
		{
			// Trail stop down
			var newStop = candle.ClosePrice + atrValue * TrailFactor;
			if (_stopPrice == 0 || newStop < _stopPrice)
				_stopPrice = newStop;

			if (candle.HighPrice >= _stopPrice)
			{
				BuyMarket();
				ResetPosition();
			}
			else if (_entryPrice > 0 && candle.LowPrice <= _entryPrice - atrValue * TakeFactor)
			{
				BuyMarket();
				ResetPosition();
			}
		}

		// Entry logic
		if (_hasPrev && Position == 0)
		{
			var mid = (_prevHigh + _prevLow) / 2m;

			if (_prevClose > mid && candle.ClosePrice > _prevHigh)
			{
				// Bullish breakout
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = candle.ClosePrice - atrValue * TrailFactor;
			}
			else if (_prevClose < mid && candle.ClosePrice < _prevLow)
			{
				// Bearish breakout
				SellMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = candle.ClosePrice + atrValue * TrailFactor;
			}
		}

		SaveCandle(candle);
	}

	private void SaveCandle(ICandleMessage candle)
	{
		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_prevClose = candle.ClosePrice;
		_hasPrev = true;
	}

	private void ResetPosition()
	{
		_entryPrice = 0;
		_stopPrice = 0;
	}
}