在 GitHub 上查看

斐波那契回撤入场策略

概述

Fibonacci Potential Entries Retracement Strategy 重现了 MetaTrader 专家顾问 EA_PUB_FibonacciPotentialEntries。策略订阅 Level 1 行情,在获取到正的买价和卖价后,于人工输入的斐波那契回撤价位附近布置两笔挂单。当行情触及公共的目标价位时,每笔仓位都会减仓 50%,并把止损移动到盈亏平衡点。

原版逻辑映射

  • 挂单布局
    • 第一笔:在 50% 回撤价 (P50Level) 放置限价单。牛市模式下止损设在 61% 价位下方 3 个点差,熊市模式则位于上方 3 个点差。
    • 第二笔:在 61% 回撤价 (P61Level) 放置限价单,止损距离 61% 与 100% 价位的中点 3 个点差。
  • 方向选择:原始的 bType 输入映射为 MarketBias 参数(Bull 表示做多挂单,Bear 表示做空挂单)。
  • 风险拆分:第一笔仓位始终使用账户权益的 0.7% 风险;第二笔仓位使用 RiskPercent - 0.7 的剩余部分,沿用 EA 的风险比例。
  • 手数计算:通过 Portfolio.CurrentValue(若不可用则回退到 CurrentBalanceBeginValue)结合品种的最小跳动、跳动成本和乘数来换算风险对应的手数。
  • 部分止盈:当行情超过 TargetLevel 时,对每笔已成交的仓位发送市价单平掉一半仓位,并将止损移动到记录的开仓价,与 EA 中的 OrderClose + OrderModify 逻辑保持一致。

参数

名称 说明
P50Level 50% 斐波那契回撤对应的价格。
P61Level 61.8% 斐波那契回撤对应的价格。
P100Level 100% 斐波那契回撤对应的价格(用于计算第二笔止损)。
TargetLevel 两笔交易共用的止盈价位。
RiskPercent 两笔仓位合计使用的风险百分比(必须 ≥ 0.7)。
MarketBias 选择多头(Bull)或空头(Bear)模式。

执行流程

  1. 通过 SubscribeLevel1() 订阅 Level 1 行情,等待有效的买卖价。
  2. 计算点差、止损价与手数。策略每次运行仅提交一次挂单(与原 EA 相同)。
  3. 挂单成交后记录平均开仓价,按腿放置对应的止损单,并跟踪剩余仓位。
  4. 当行情超过 TargetLevel 时,对每条腿分别发送一次减仓市价单,随后将止损移动到开仓价。
  5. 当仓位归零或策略停止时撤销未完成的止损单。

注意事项

  • 每当仓位变化时都会重新生成止损单。如果券商拒绝止损,请检查适配器权限并根据交易所规则调整设置。
  • 策略不再单独挂出止盈单,而是像原版 EA 一样实时监控价格并管理出场。
  • 参数调整后需要重启策略才能重新布置挂单,这一点与 MetaTrader 的操作方式一致。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Fibonacci Retracement Entries strategy: EMA trend with Fibonacci retracement levels.
/// Buys on retracement to 61.8% in uptrend, sells on retracement to 38.2% in downtrend.
/// </summary>
public class FibonacciPotentialEntriesRetracementStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _lookback;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _high;
	private decimal _low;
	private int _barCount;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int Lookback { get => _lookback.Value; set => _lookback.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public FibonacciPotentialEntriesRetracementStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");
		_lookback = Param(nameof(Lookback), 50)
			.SetGreaterThanZero()
			.SetDisplay("Lookback", "Lookback for high/low", "General");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_high = 0;
		_low = decimal.MaxValue;
		_barCount = 0;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_high = 0;
		_low = decimal.MaxValue;
		_barCount = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (candle.HighPrice > _high) _high = candle.HighPrice;
		if (candle.LowPrice < _low) _low = candle.LowPrice;
		_barCount++;

		if (_barCount < 20) return;

		var range = _high - _low;
		if (range <= 0) return;

		var close = candle.ClosePrice;
		var fib618 = _high - range * 0.618m;
		var fib382 = _high - range * 0.382m;
		if (close > emaValue && close <= fib618 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			BuyMarket();
			_candlesSinceTrade = 0;
		}
		else if (close < emaValue && close >= fib382 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			SellMarket();
			_candlesSinceTrade = 0;
		}
	}
}