在 GitHub 上查看
Profit Hunter HSI with Fibonacci 策略
概述
该策略是 MetaTrader 4 智能交易系统 Profit_Hunter_HSI_with_fibonacci.mq4 的 C# 版本。原始脚本通过日线图上的
Fibonacci 回撤区间配合日内指数移动平均线 (EMA) 滤波来识别交易机会。StockSharp 版本沿用这一思路:订阅一组
日内蜡烛和一组日线蜡烛,基于最新日线高低点实时重建 Fibonacci 网格,在价格进入关键区间时产生信号,并使用
自适应止损与分段式移动止损管理仓位。
行情数据流程
- 日内蜡烛:
TimeFrame 参数控制主工作周期(默认 1 分钟)。每根收盘蜡烛都会更新 EMA 趋势、读取 NumBars
根之前的支撑/阻力,并触发交易规则。
- 日线蜡烛:额外的订阅记录高周期数据。两个可配置的索引用于选择构建 Fibonacci 网格所需的摆动高点与低点。
每当新的日线蜡烛出现,所有回撤与扩展(161.8%、261.8%、423.6%)都会重新计算。
信号生成
原始 EA 会记住最近一次出现的高点和低点,并判断哪个先出现 (highFirst)。移植版通过比较用户指定的日线索引实现
同样的效果:
- 若高点索引小于低点索引(更靠近当前),认为市场先冲高后回落,Fibonacci 从低点向上测量。
- 否则视为先探底后反弹,Fibonacci 从高点向下投射。
每根完成的日内蜡烛都会执行以下逻辑,完全对应 MT4 版本:
- 趋势过滤:长度为
MaPeriod 的 EMA 用于判定短期方向。收盘价高于 EMA 视为 “Naik”(上涨),低于则视为
“Turun”(下跌)。若价格正好贴近 EMA,则不会开仓。
- Fibonacci 信号:根据
highFirst 的结果,比较价格与 23.6%、76.4%、91%、14.6% 水平的位置关系,生成
Reverse-Buy、Reverse-Sell、Trading-Area 或 Continuation 四种字符串信号。前两种和 Trading-Area 会参与
入场决策,Continuation 仅用于提示趋势延续。
- 入场规则: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 创建了大量图形对象,出于平台差异这些装饰性元素在移植过程中被省略。
- 移植版不再调用
iLow、iHigh 等历史函数,而是维护两个内存列表来存放已完成的蜡烛,并直接根据索引读取值。
- 止损逻辑通过
ManagePosition 在策略内部实现,无需依赖 OrderModify,从而保持与经纪商无关的行为。
- 当订单注册失败时,会清除挂起的入场状态,避免参数调整后留下残留标记,与许多已有的 StockSharp 策略保持一致。
与 MT4 版本的差异
- MT4 可直接读取 Tick 级别的
Ask 与 Bid,而 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();
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, Highest, Lowest
class profit_hunter_hsi_with_fibonacci_strategy(Strategy):
def __init__(self):
super(profit_hunter_hsi_with_fibonacci_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "Period for trend filter EMA", "Indicators")
self._lookback_period = self.Param("LookbackPeriod", 50) \
.SetDisplay("Lookback Period", "Bars to look back for range high/low", "Fibonacci")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe for analysis", "General")
self._bar_count = 0
@property
def EmaPeriod(self):
return self._ema_period.Value
@property
def LookbackPeriod(self):
return self._lookback_period.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(profit_hunter_hsi_with_fibonacci_strategy, self).OnStarted2(time)
self._bar_count = 0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaPeriod
self._highest = Highest()
self._highest.Length = self.LookbackPeriod
self._lowest = Lowest()
self._lowest.Length = self.LookbackPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._highest, self._lowest, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_value, highest_value, lowest_value):
if candle.State != CandleStates.Finished:
return
self._bar_count += 1
if self._bar_count < self.LookbackPeriod:
return
ema_val = float(ema_value)
high_val = float(highest_value)
low_val = float(lowest_value)
rng = high_val - low_val
if rng <= 0:
return
fib382 = high_val - rng * 0.382
fib618 = high_val - rng * 0.618
close = float(candle.ClosePrice)
# Manage position
if self.Position > 0:
if close >= high_val or close < fib618:
self.SellMarket()
elif self.Position < 0:
if close <= low_val or close > fib382:
self.BuyMarket()
# Entry logic
if self.Position == 0:
if close > ema_val and close <= fib382 and close > fib618:
self.BuyMarket()
elif close < ema_val and close >= fib618 and close < fib382:
self.SellMarket()
def OnReseted(self):
super(profit_hunter_hsi_with_fibonacci_strategy, self).OnReseted()
self._bar_count = 0
def CreateClone(self):
return profit_hunter_hsi_with_fibonacci_strategy()