在 GitHub 上查看

Profit Hunter HSI with Fibonacci 策略

概述

该策略是 MetaTrader 4 智能交易系统 Profit_Hunter_HSI_with_fibonacci.mq4 的 C# 版本。原始脚本通过日线图上的 Fibonacci 回撤区间配合日内指数移动平均线 (EMA) 滤波来识别交易机会。StockSharp 版本沿用这一思路:订阅一组 日内蜡烛和一组日线蜡烛,基于最新日线高低点实时重建 Fibonacci 网格,在价格进入关键区间时产生信号,并使用 自适应止损与分段式移动止损管理仓位。

行情数据流程

  1. 日内蜡烛TimeFrame 参数控制主工作周期(默认 1 分钟)。每根收盘蜡烛都会更新 EMA 趋势、读取 NumBars 根之前的支撑/阻力,并触发交易规则。
  2. 日线蜡烛:额外的订阅记录高周期数据。两个可配置的索引用于选择构建 Fibonacci 网格所需的摆动高点与低点。 每当新的日线蜡烛出现,所有回撤与扩展(161.8%、261.8%、423.6%)都会重新计算。

信号生成

原始 EA 会记住最近一次出现的高点和低点,并判断哪个先出现 (highFirst)。移植版通过比较用户指定的日线索引实现 同样的效果:

  • 若高点索引小于低点索引(更靠近当前),认为市场先冲高后回落,Fibonacci 从低点向上测量。
  • 否则视为先探底后反弹,Fibonacci 从高点向下投射。

每根完成的日内蜡烛都会执行以下逻辑,完全对应 MT4 版本:

  1. 趋势过滤:长度为 MaPeriod 的 EMA 用于判定短期方向。收盘价高于 EMA 视为 “Naik”(上涨),低于则视为 “Turun”(下跌)。若价格正好贴近 EMA,则不会开仓。
  2. Fibonacci 信号:根据 highFirst 的结果,比较价格与 23.6%、76.4%、91%、14.6% 水平的位置关系,生成 Reverse-BuyReverse-SellTrading-AreaContinuation 四种字符串信号。前两种和 Trading-Area 会参与 入场决策,Continuation 仅用于提示趋势延续。
  3. 入场规则:EA 中的六个分支被逐条还原:
    • 上涨趋势 + 交易区间 + 向上突破参考阻力 → 做多,止损设为对应支撑。
    • 上涨趋势 + 反转卖出 + highFirst == false + 价格仍低于阻力 → 做空,止损设为 14.6% 水平。
    • 上涨趋势 + 反转买入 + highFirst == false + 价格低于阻力 → 做多,止损设为 91% 水平。
    • 下跌趋势 + 交易区间 + 跌破支撑 → 做空,止损设为阻力位。
    • 下跌趋势 + 反转卖出 + highFirst == true + 价格低于阻力 → 做空,止损设为 91% 水平。
    • 下跌趋势 + 反转买入 + highFirst == true + 价格低于阻力 → 做多,止损设为 14.6% 水平。 策略始终保持单一仓位,不会叠加订单。

仓位管理

  • 支撑/阻力离场:与原版一致,若持有多单且价格回落至支撑则平仓;持有空单且价格回升到阻力则平仓,无论盈亏。
  • 初始保护止损:入场时计算出的止损价格会存储下来,并在每根蜡烛中作为离场条件检查,而不是像 MT4 那样修改 经纪商订单。
  • 分段移动止损:EA 在盈利 60 点后开始移动止损,此后每增加 20 点就把止损向盈利方向推进 5 点(一直到 260 点)。移植版依据品种 PriceStep 将点值转换为价格差,完全复刻这一阶梯。做空时止损会向下移动,锁定同样 的利润空间。

参数

名称 说明 默认值 备注
NumBars 读取哪一根历史蜡烛的高低点作为临时支撑/阻力。 3 对应 EA 的 numBars,必须大于 0。
MaPeriod EMA 趋势过滤的周期。 5 对应 maPeriod
TimeFrame 日内蜡烛周期。 1 分钟 对应 timeFrame,接受任意 TimeSpan
DaysBackForHigh 提供摆动高点的日线索引。 1 对应 daysBackForHigh
DaysBackForLow 提供摆动低点的日线索引。 1 对应 daysBackForLow
Volume 市价单交易量。 1 以手/股为单位,要求为正。

实现说明

  • 原 EA 创建了大量图形对象,出于平台差异这些装饰性元素在移植过程中被省略。
  • 移植版不再调用 iLowiHigh 等历史函数,而是维护两个内存列表来存放已完成的蜡烛,并直接根据索引读取值。
  • 止损逻辑通过 ManagePosition 在策略内部实现,无需依赖 OrderModify,从而保持与经纪商无关的行为。
  • 当订单注册失败时,会清除挂起的入场状态,避免参数调整后留下残留标记,与许多已有的 StockSharp 策略保持一致。

与 MT4 版本的差异

  • MT4 可直接读取 Tick 级别的 AskBid,而 StockSharp 默认基于蜡烛收盘价,因此移植版使用收盘价作为双向报价 的近似值即可复刻逻辑。
  • “哪个极值先出现”的判定在 MT4 中依赖全局数组 High[]Low[]。移植版通过比较用户选择的日线索引实现同等效果, 对默认参数和其它组合都能保持原有意图。
  • 经纪商侧的止损/止盈订单被虚拟化为策略内部条件判断,以便在不同连接器之间保持一致,同时确保和原策略相同的 离场触发条件。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Profit Hunter strategy with Fibonacci retracement levels.
/// Uses EMA trend filter and enters on pullbacks to Fibonacci levels.
/// </summary>
public class ProfitHunterHsiWithFibonacciStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _lookbackPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private int _barCount;

	public ProfitHunterHsiWithFibonacciStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "Period for trend filter EMA.", "Indicators");

		_lookbackPeriod = Param(nameof(LookbackPeriod), 50)
			.SetDisplay("Lookback Period", "Bars to look back for range high/low.", "Fibonacci");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
	}

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

	public int LookbackPeriod
	{
		get => _lookbackPeriod.Value;
		set => _lookbackPeriod.Value = value;
	}

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

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_barCount = 0;
	}

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

		_barCount = 0;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var highest = new Highest { Length = LookbackPeriod };
		var lowest = new Lowest { Length = LookbackPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, highest, lowest, ProcessCandle)
			.Start();

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

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

		_barCount++;

		if (_barCount < LookbackPeriod)
			return;

		var range = highestValue - lowestValue;
		if (range <= 0)
			return;

		// Fibonacci levels
		var fib382 = highestValue - range * 0.382m;
		var fib618 = highestValue - range * 0.618m;
		var close = candle.ClosePrice;

		// Manage position
		if (Position > 0)
		{
			// Exit long at 0 fib (range high) or if price drops below 61.8%
			if (close >= highestValue || close < fib618)
			{
				SellMarket();
			}
		}
		else if (Position < 0)
		{
			// Exit short at 100% fib (range low) or if price rises above 38.2%
			if (close <= lowestValue || close > fib382)
			{
				BuyMarket();
			}
		}

		// Entry logic
		if (Position == 0)
		{
			if (close > emaValue && close <= fib382 && close > fib618)
			{
				// Uptrend + pullback to Fib zone -> buy
				BuyMarket();
			}
			else if (close < emaValue && close >= fib618 && close < fib382)
			{
				// Downtrend + pullback to Fib zone -> sell
				SellMarket();
			}
		}
	}
}