在 GitHub 上查看

Fibonacci Potential Entries 策略

概述

该策略完整复刻原始的 EA_PUB_FibonacciPotentialEntries 智能交易系统。它会在 50% 和 61% 的斐波那契回撤位上挂出两笔限价单,并使用 StockSharp 的高级 API 管理整个生命周期。

交易逻辑

  1. 初始下单

    • 当买卖报价可用时,策略计算当前点差并同时提交两笔限价单:
      • 订单 #1:挂在 50% 水平位,止损设置在 61% 水平位之外(做空时相反)。
      • 订单 #2:挂在 61% 水平位,止损设置在 61% 与 100% 水平位的中点附近。
    • 仓位规模按照资金管理计算:第一单承担 0.7% 的账户风险,第二单承担剩余的 RiskPercent 百分比。
  2. 目标管理

    • 当价格触及 TargetPrice 时,策略通过市价单平掉每个持仓的一半头寸。
    • 部分离场后,剩余仓位被移动到保本价位。如果行情回撤到入场价,剩余部分会被自动平仓。
  3. 方向选择

    • IsBullish = true 时执行多头模板,挂买入限价单。
    • IsBullish = false 时执行空头模板,对应的止损与目标判断全部镜像。

参数

名称 说明
PriceOn50Level 第一笔限价单的价格。
PriceOn61Level 第二笔限价单的价格。
PriceOn100Level 用于计算第二笔交易止损的参考价格。
TargetPrice 两笔交易共用的盈利目标。
RiskPercent 两笔交易整体承担的账户风险百分比。
IsBullish 选择多头或空头场景。

转换要点

  • 只使用高级接口(SubscribeLevel1BuyLimitSellLimitBuyMarketSellMarket),完全符合仓库的转换规范。
  • 部分平仓与保本保护通过市价单实现,无需任何底层订单修改,忠实再现 MQL 机器人的处理方式。
  • 按合约的交易步长对下单数量进行规范化,确保与 StockSharp 的风控和撮合保持一致。
namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// Fibonacci Potential Entries strategy: Two-candle reversal with RSI filter.
/// Uses price swing highs/lows as fibonacci reference points.
/// </summary>
public class FibonacciPotentialEntriesStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _highestHigh;
	private decimal _lowestLow;
	private decimal _prevClose;
	private int _barCount;
	private int _candlesSinceTrade;
	private bool _hasPrevClose;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public FibonacciPotentialEntriesStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_highestHigh = 0;
		_lowestLow = decimal.MaxValue;
		_prevClose = 0;
		_barCount = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrevClose = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_highestHigh = 0;
		_lowestLow = decimal.MaxValue;
		_prevClose = 0;
		_barCount = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrevClose = false;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (candle.HighPrice > _highestHigh) _highestHigh = candle.HighPrice;
		if (candle.LowPrice < _lowestLow) _lowestLow = candle.LowPrice;
		_barCount++;

		if (_barCount < 20) return;

		var range = _highestHigh - _lowestLow;
		if (range <= 0) return;

		var fib382 = _highestHigh - range * 0.382m;
		var fib618 = _highestHigh - range * 0.618m;
		var close = candle.ClosePrice;
		var crossedIntoBuyZone = _hasPrevClose && _prevClose > fib618 && close <= fib618;
		var crossedIntoSellZone = _hasPrevClose && _prevClose < fib382 && close >= fib382;

		if (crossedIntoBuyZone && rsiValue < 40 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			BuyMarket();
			_candlesSinceTrade = 0;
		}
		else if (crossedIntoSellZone && rsiValue > 60 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			SellMarket();
			_candlesSinceTrade = 0;
		}

		_prevClose = close;
		_hasPrevClose = true;
	}
}