GitHub で見る

Fibonacci Time Zones戦略

概要

この戦略は、MetaTrader のエキスパートアドバイザー "Fibonacci Time Zones" を StockSharp に移植したものです。上位時間枠の MACD フィルター、Bollinger バンドによるエグジット、充実した資金管理モジュールを組み合わせることで、元のスクリプトの裁量的な特徴を保っています。すべての取引管理ルーチンは高レベル API を使って書き直されています。戦略は 2 つのローソク足ストリーム (取引用時間枠と MACD 確認用の低速時間枠) を購読し、Bind/BindEx コールバックでインジケーターを直接バインドします。

中核ロジック

  1. Momentumフィルター - 月足 (設定可能) の MACD ヒストグラムを計算します。シグナルラインを上抜ける強気クロスはロングエントリーを予約し、弱気クロスはショートエントリーを予約します。同じクロスで注文が繰り返されるのを避けるため、実際のポジションは次の取引用ローソク足で開かれます。
  2. エントリー実行 - 各シグナルは、ユーザー定義数の成行注文を送信します。新しいポジションを開く前に、既存の反対方向エクスポージャーを解消します。
  3. エグジットルール - 複数の防御層が適用されます。
    • Bollingerバンド・エグジット: ロングは価格が上側バンドに触れたとき、ショートは下側バンドに到達したときに決済されます。
    • クラシックなストップ/目標: 固定の stop-loss、take-profit、trailing-stop 距離は pips から価格単位に変換され、StartProtection に渡されます。
    • Break-even: 価格が設定可能な pips 数だけ進むと、ストップは break-even にオフセットを加えた位置へ引き上げられます。価格がその水準まで戻るとポジションを決済します。
    • 金額ベースのtrailing: 未決済 PnL と実現 PnL を監視します。含み益がしきい値に達すると、戦略はその利益を trail し始め、設定可能な drawdown 後にすべてを決済します。
    • Equity目標: 任意の絶対額または割合の利益目標に達すると、すべての取引を直ちに決済します。

パラメーター

パラメーター 説明
UseTakeProfitMoney, TakeProfitMoney 合計利益 (実現 + 未実現) が指定した口座通貨額に達したとき、すべてのポジションを決済します。
UseTakeProfitPercent, TakeProfitPercent 前のオプションと似ていますが、開始時 equity に対する割合で測定します。
EnableTrailingProfit, TrailingTakeProfitMoney, TrailingStopLossMoney 最初のしきい値に達した時点で金額ベースの trailing を有効にし、蓄積した利益を保護します。
UseStop, StopLossPips, TakeProfitPips, TrailingStopPips pips で表されるクラシックなストップ、目標、trailing 距離。
UseMoveToBreakEven, WhenToMoveToBreakEven, PipsToMoveStopLoss break-even 動作を制御します。
NumberOfTrades 各シグナルで送信される成行注文数 (エントリーを積み増せた元の EA を模倣)。
CandleType, MacdCandleType 管理用ローソク足と MACD フィルターの時間枠。

元のEAとの違い

  • チャートボタン処理とグラフィカルな Fibonacci オブジェクトは再現していません。StockSharp 版は純粋に体系的な実行に集中しています。
  • 元のエキスパートは手動ボタンクリックで取引していました。この移植版は MACD クロスで自動的にエントリーし、決定論的でバックテスト可能な戦略にしています。
  • MetaTrader 固有の口座関数は、StockSharp の同等機能 (Portfolio 値と PnL) に置き換えられました。

使用のヒント

  1. 戦略を開始する前に、適切なローソク足タイプを選択してください。デフォルトは、月足 MACD フィルターを持つ 15 分足の取引チャートに対応します。
  2. 商品の tick サイズに合わせて pip ベースの距離を調整します。戦略は内部で Security.PriceStep を使い、pips を価格に変換します。
  3. 裁量的に介入する場合は、自動利益目標を無効にし、Bollinger エグジットのみを使用します。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class FibonacciTimeZonesStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public FibonacciTimeZonesStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}