GitHub で見る

Ten Pips EURUSD 戦略

概要

Ten Pips EURUSD 戦略は、オリジナルの MetaTrader Expert Advisor のロジックを再現するブレイクアウトシステムです。最近完了したローソク足を監視し、そのレンジの上下にストップ注文を配置します。注文は pips 単位でサイズ決定され、現在の銘柄のティックサイズに調整され、オプションでトレーリングストップで管理されます。この実装は StockSharp の高レベルローソク足サブスクリプションと注文板更新を組み合わせて、ブローカー中立を保ちながら MQL バージョンに近い動作を維持します。

戦略ロジック

  1. 選択したローソク足タイプをサブスクライブし、新しいバーがアクティブになるまで待ちます。
  2. そのバーが終了したとき、前のローソク足の高値と安値を取得します。オリジナル EA はそれらを 1 バーに制限しているため、この時点で保留注文がキャンセルされます。
  3. 新しいバーの最初のティックで以下を確認します:
    • 現在の始値が前のローソク足のレンジ内にある(ギャップフィルタリング)。
    • 現在の価格が両方の極値から少なくとも 3 pip 単位離れている(ブローカーのストップレベルのプロキシ)。
  4. ベスト bid/ask を使って現在のスプレッドを計算します。Level 1 データがない場合、戦略は pip サイズにフォールバックします。
  5. 2 つの保留ストップ注文を配置します:
    • Buy Stop: 前の高値 + 2 × スプレッド で発動し、エントリー価格の StopLossPips 下にストップロス、トレーリングが無効の場合は 前の高値 + 2 × スプレッド + TakeProfitPips にテイクプロフィット。
    • Sell Stop: 前の安値 − スプレッド で発動し、対称的な決済レベル。
  6. ローソク足が完了するか、両注文が約定/キャンセルされるとすぐに、次のバーのプロセスが繰り返されます。

ポジション管理

  • ポジションがオープンの間、戦略は各注文板更新でベスト bid/ask を監視します。
  • トレーリングが無効の場合、価格が固定ストップまたはテイクプロフィットレベルに触れるとポジションが決済されます。
  • トレーリングが有効の場合:
    • ロングトレードでは、価格が TrailingStopPips 進んだ後にトレーリングストップが発動します。ストップは bid − TrailingStopPips に設定され、価格が少なくとも TrailingStepPips 改善するたびに移動します。
    • ショートトレードでは、ask 価格を使ってロング側のロジックを反映します。
  • 手動決済はすべての保護レベルをリセットし、EA のストラドル動作を再現するために、ローソク足が終了するまで未決済の反対側のストップ注文を維持します。

パラメーター

名前 デフォルト 説明
Volume 0.01 ロット単位の注文ボリューム(非 FX シンボルの場合は契約単位)。
StopLossPips 50 エントリーと保護ストップ間の距離(pips 単位)。
TakeProfitPips 150 pips 単位のテイクプロフィット距離。トレーリングが無効の場合のみ使用。
UseTrailing false トレーリングストップロジックを有効にします。
TrailingStopPips 50 トレーリングストップの初期距離(pips 単位)。
TrailingStepPips 25 アクティブなトレーリングストップを移動するために必要な最小利益(pips 単位)。
CandleType 15 分足 ブレイクアウトレベルの検出に使用するローソク足シリーズ。

注意事項と推奨

  • pip サイズは Security.PriceStep から自動的に算出され、MQL の桁数調整をエミュレートするため、戦略は 3 桁・5 桁の FX シンボルに対応します。
  • すべての距離は注文を配置する前に価格単位に再計算されるため、ティックサイズが定義されている限り、非 FX 資産とも互換性があります。
  • 最小ストップレベルのフォールバック(3 pip 単位)は、ブローカーがストップレベルを報告しない場合の元の EA の動作を模倣します。
  • 保留注文は各ローソク足の終わりに期限切れになるため、入力ローソク足ストリームにギャップなく、希望の時間軸で戦略を実行する必要があります。
  • リスク管理が重要です。実際の資金で取引する前に、現実的なスプレッドと手数料モデルでテストすることを検討してください。
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>
/// Breakout strategy: enters long when price breaks above previous candle high,
/// enters short when price breaks below previous candle low.
/// Uses ATR-based stop loss and take profit.
/// </summary>
public class TenPipsEurusdStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stopLossMult;
	private readonly StrategyParam<decimal> _takeProfitMult;
	private readonly StrategyParam<decimal> _trailingMult;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takePrice;
	private bool _hasPrev;

	public decimal StopLossMult { get => _stopLossMult.Value; set => _stopLossMult.Value = value; }
	public decimal TakeProfitMult { get => _takeProfitMult.Value; set => _takeProfitMult.Value = value; }
	public decimal TrailingMult { get => _trailingMult.Value; set => _trailingMult.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TenPipsEurusdStrategy()
	{
		_stopLossMult = Param(nameof(StopLossMult), 1.0m)
			.SetGreaterThanZero()
			.SetDisplay("SL Mult", "Stop loss ATR multiplier", "Risk");

		_takeProfitMult = Param(nameof(TakeProfitMult), 2.0m)
			.SetGreaterThanZero()
			.SetDisplay("TP Mult", "Take profit ATR multiplier", "Risk");

		_trailingMult = Param(nameof(TrailingMult), 0.8m)
			.SetGreaterThanZero()
			.SetDisplay("Trail Mult", "Trailing stop ATR multiplier", "Risk");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR calculation length", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh = 0;
		_prevLow = 0;
		_entryPrice = 0;
		_stopPrice = null;
		_takePrice = null;
		_hasPrev = false;
	}

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

		var atr = new AverageTrueRange { Length = AtrPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevHigh = candle.HighPrice;
			_prevLow = candle.LowPrice;
			_hasPrev = true;
			return;
		}

		var close = candle.ClosePrice;

		// Manage existing position
		if (Position > 0)
		{
			// Trail
			var trail = close - TrailingMult * atr;
			if (_stopPrice == null || trail > _stopPrice)
				_stopPrice = trail;

			if (close <= _stopPrice || (_takePrice != null && close >= _takePrice))
			{
				SellMarket(Math.Abs(Position));
				_stopPrice = null;
				_takePrice = null;
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			var trail = close + TrailingMult * atr;
			if (_stopPrice == null || trail < _stopPrice)
				_stopPrice = trail;

			if (close >= _stopPrice || (_takePrice != null && close <= _takePrice))
			{
				BuyMarket(Math.Abs(Position));
				_stopPrice = null;
				_takePrice = null;
				_entryPrice = 0;
			}
		}

		// Entry on breakout
		if (_hasPrev && Position == 0)
		{
			if (close > _prevHigh + atr * 0.5m)
			{
				BuyMarket();
				_entryPrice = close;
				_stopPrice = close - StopLossMult * atr;
				_takePrice = close + TakeProfitMult * atr;
			}
			else if (close < _prevLow - atr * 0.5m)
			{
				SellMarket();
				_entryPrice = close;
				_stopPrice = close + StopLossMult * atr;
				_takePrice = close - TakeProfitMult * atr;
			}
		}

		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_hasPrev = true;
	}
}