GitHub で見る

Fly System Scalp戦略

概要

Fly System Scalp Strategyは、元のMQL4エキスパートアドバイザーFlySystemEAの中核動作を再現する高頻度ブレイクアウトシステムです。戦略は最良bid/askを常時監視し、市場価格の周囲に2つの対称的なstop注文を配置します。短期保ち合い後に現れる素早いマイクロトレンドを捉えつつ、スプレッド、手数料、取引セッション境界を厳密に管理することを目指します。

変換では次の主要メカニクスに焦点を当てています。

  • 市場から設定可能な距離にbuy stopとsell stop注文を自動配置。
  • スプレッド(手数料込み)が許容しきい値を超える、または取引が許可セッション外の場合、待機注文を自動キャンセル。
  • 新しい待機注文に直接付与される任意のテイクプロフィットと必須のストップロス管理。
  • ブローカー契約仕様(price step、step value、lot step、最小/最大数量)を使う手動固定数量と自動リスクベースポジションサイズのサポート。
  • ポジションが閉じるのを待ってから新しいstop注文ペアを準備する自己リセット型取引サイクル。

StockSharp実装は高レベルAPI(bind付きlevel-1購読)を活用し、プロジェクト規約に従います。戦略パラメーターはStrategyParamで公開され、コメントは英語、namespaceはfile-scoped宣言です。

取引ロジック

  1. Level 1フィード: 戦略は割り当てられた銘柄のlevel-1データを購読します。各更新で最新bid/askペアを記録します。
  2. 検証層: 取引アクション前にエンジンは確認します。
    • 戦略がオンラインで取引を許可されている。
    • 現在時刻が任意の取引時間窓内にある。
    • スプレッドと手数料の合計がMaxSpread pipsを超えない。
  3. 待機注文配置: 上記条件を満たし、ポジションがなく、新しいサイクル準備ができている場合、2つの注文を準備します。
    • Ask + PendingDistance * pipのBuy Stop。保護Stop Lossと任意Take Profit付き。
    • Bid - PendingDistance * pipのSell Stop。反対側の保護付き。 希望価格と実際価格の差がModifyThreshold pipsに達すると、注文は再登録されます。
  4. 注文管理: ポジションが開くと、反対側の待機注文は即座にキャンセルされます。スプレッド/時間違反で取引サイクルが中断されると、すべての待機注文を削除し、有効な条件を待ちます。
  5. ポジションサイズ: AutoLotSizeが有効な場合、数量はエクイティのRiskFactorパーセントを、設定ストップ距離での1契約あたり損失で割って導かれます。数量はブローカーのロットステップへ丸められ、最小/最大に制限されます。
  6. 保護: インフラが必要とする場合にStockSharpが緊急清算を監視できるよう、StartProtection()を呼び出します。

パラメーター

名前 説明 既定値
PendingDistance 市場価格と両stop注文の距離(pips)。 4
StopLossDistance 新規ポジションへ付与するストップロス距離(pips)。 0.4
TakeProfitDistance 有効時のテイクプロフィット距離(pips)。 10
UseTakeProfit テイクプロフィット配置を有効にします。 false
MaxSpread 最大許容スプレッド(pips)。0でフィルター無効。 1
CommissionInPips スプレッドフィルターに加える手数料(pips)。 0
AutoLotSize リスクベースのポジションサイズを有効にします。 false
RiskFactor 自動サイズ有効時にポジションサイズへ使うエクイティ割合。 10
ManualVolume 自動サイズ無効時の固定数量。 0.1
UseTimeFilter 取引セッションフィルターを有効にします。 false
TradeStartTime セッション開始時刻(含む)。 00:00:00
TradeStopTime セッション終了時刻(含まない)。 00:00:00
ModifyThreshold 待機注文を再登録する前に必要な価格差(pips)。 1

使用上の注意

  • 自動サイズはこれらの値に依存するため、対象銘柄がStepPriceStepStepPriceLotStepMinVolumeMaxVolumeを提供していることを確認してください。データがない場合、戦略は安全にManualVolumeへ戻ります。
  • pip値は銘柄の小数精度と価格ステップから推定され、元のMQL実装のロジック(3/5桁Forex気配の特別処理を含む)に一致します。
  • UseTimeFilterが有効でTradeStartTimeTradeStopTimeが同じ場合、セッションは常時開いていると見なされます。開始時刻が停止時刻より大きい場合、セッションは深夜を跨ぎます。
  • スプレッド検証は現在スプレッドにCommissionInPipsを加え、MQL版がスプレッドと手数料を単一フィルターへ組み合わせた動作を再現します。
  • 戦略はチャートオブジェクトを作成または管理しません。level-1データをチャートへバインドして外部で可視化できます。

元EAとの差異

  • MQL版の低レベルティックタイマーとGUI要素は意図的に省略されています。StockSharp版はlevel-1イベントと組み込みログに依存します。
  • 注文変更ロジックは簡略化されています。目標価格との差がModifyThreshold pipsを超えると、EAの複雑な分岐調整ではなく注文を再登録します。
  • 取引履歴からの自動手数料検出は静的なCommissionInPipsパラメーターへ置き換えられています。ただしリスクフィルターは取引前にこの値をスプレッドへ加えます。
  • StockSharp版はカスタムのストップ水準監視ループではなくStartProtection()を使用します。

バックテスト

stop注文の発動ロジックを再現するには、戦略にlevel-1気配データが必要です。履歴シミュレーションではbid/ask系列を提供するか、ティック履歴から合成level-1データを構築してください。待機stop注文はスプレッド変化へ反応する必要があるため、ローソク足のみのフィードでは不十分です。

using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Fly System Scalp strategy: EMA crossover + RSI confirmation.
/// Buys when close crosses above EMA and RSI confirms.
/// Sells when close crosses below EMA and RSI confirms.
/// </summary>
public class FlySystemScalpStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;

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

	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

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

		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		decimal? prevClose = null;
		decimal? prevEma = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, rsi, (candle, emaVal, rsiVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;

				if (prevClose.HasValue && prevEma.HasValue)
				{
					var crossUp = prevClose.Value <= prevEma.Value && close > emaVal;
					var crossDown = prevClose.Value >= prevEma.Value && close < emaVal;

					if (crossUp && rsiVal < 55m && Position <= 0)
						BuyMarket();
					else if (crossDown && rsiVal > 45m && Position >= 0)
						SellMarket();
				}

				prevClose = close;
				prevEma = emaVal;
			})
			.Start();

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