在 GitHub 上查看

Fibo Pivot MultiVal 策略

概述

Fibo Pivot MultiVal 策略 是 MetaTrader 4 专家顾问 _Fibo_Pivot_multiVal.mq4 的 StockSharp 移植版本。该策略利用上一交易日的枢轴点以及斐波那契比例,在枢轴附近的每个价格带内自动布设限价单。交易时间、出场目标和停止规则均参考原版 EA,以便老用户能够快速上手。

核心逻辑

  1. 每日基准水平:根据上一交易日的最高价、最低价和收盘价计算经典枢轴点(P、R1-R3、S1-S3),并使用斐波那契比例把相邻的支撑/阻力区间进一步细分,外层还会生成 R3/S3 的延伸目标。
  2. 盘中分析:按照设定的 K 线周期(默认 15 分钟)监控收盘价。当价格处于某个枢轴区间(例如 R2 与 R3 之间)时,对应的限价订单会被激活。
  3. 限价订单:在斐波那契子区间挂出多/空限价单。位于 R1-R2 与 S1-S2 的中间区间,可通过 MidZoneOrderMode 参数限制仅做多、仅做空或双向交易。
  4. 目标动态调整UseReversalTargets 为真时,止盈位设置在当前区间的另一侧,偏向震荡反弹;为假时,策略根据前一日振幅与 LimitPointOutLimitPointIn 阈值的比较来决定是使用突破目标(朝向 R3/S3 延伸)还是回归目标(朝向枢轴)。
  5. 风险控制:每日及单品种的收益/交易次数达到上限后会暂停开仓,并取消所有挂单,待次日 StartTime 前重置后方可继续。
  6. 交易时段管理StartTime 开始交易;FinishTime 后不再挂新单;CloseAllTime 触发清仓并撤销全部挂单。

参数

名称 默认值 说明
CandleType 15 分钟 K 线 计算与判断所使用的时间周期。
OrderVolume 0.1 每个限价单的下单数量。
StartTime 00:01 开始交易并重置统计的时间。
FinishTime 08:00 停止挂新单但保留持仓的时间。
CloseAllTime 12:00 清空持仓并撤单的时间。
UseReversalTargets true 为真时在区间内部止盈,为假时根据日振幅选择突破或回归目标。
LimitPointIn 150 日内振幅超过该阈值(以点为单位)时启用回归枢轴的目标。
LimitPointOut 50 日内振幅低于该阈值时倾向使用突破目标。
LevelPf1 33 枢轴与 R1/S1 之间的斐波那契比例。
LevelF1F2 50 R1-R2 与 S1-S2 的内部比例。
LevelF2F3 33 R2-R3 与 S2-S3 的内部比例。
LevelF3Out 40 R3/S3 延伸目标的比例。
MidZoneOrderMode "bs" 中间区间允许的方向("b"=仅多,"s"=仅空,"bs"=双向)。
DailyProfitTarget 50 每日收益上限(点)。
DailyTradeTarget 35 每日允许的完成交易次数。
SymbolProfitTarget 150 单品种收益上限(点)。
SymbolTradeTarget 15 单品种完成交易次数上限。

挂单与平仓

  • 每个活跃区间都会维护自己的入场、止盈与可选止损订单。入场成交后会根据当前设置重新生成出场订单。
  • 平仓会更新每日及单品种的统计数据;一旦达到任意限制,策略将暂停开仓并等待下一次重置。
  • 到达时间边界时自动撤销挂单;触发 CloseAllTime 时会通过市价平掉剩余仓位。

使用建议

  • 策略依赖品种的最小报价增量。请确保 SecurityPriceStep 设置正确,以便点值换算与原版一致。
  • 针对不同波动率的市场,可调整 LimitPointInLimitPointOut,以改变突破与回归模式的触发条件。
  • 若希望在中间区间只做一个方向,可将 MidZoneOrderMode 设置为 "b""s"
  • 代码中的参数均已开放优化,可在 StockSharp Designer 中执行批量回测以寻找合适的斐波那契比例与风控阈值。

与原版 EA 的差异

  • StockSharp 版本一次只处理一个交易品种,如需多品种交易可运行多个实例。
  • 下单数量使用交易所实际数量单位,不再使用 MetaTrader 的“手”概念,请根据券商要求设置 OrderVolume
  • 订单提交通过 StockSharp 高级 API (BuyLimitSellLimit 等) 完成,具体成交行为可能与 MetaTrader 略有不同,实盘部署前请充分测试。
using System;

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

namespace StockSharp.Samples.Strategies;

public class FiboPivotMultiValStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevMid;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public FiboPivotMultiValStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 48).SetDisplay("Channel Period", "Fibo channel lookback", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA filter", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 150).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = default;
		_prevMid = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0;
		_prevMid = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ema, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest, decimal ema)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		var mid = (highest + lowest) / 2;
		if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevClose = close;
			_prevMid = mid;
			return;
		}

		if (_prevClose <= _prevMid && close > mid && close > ema && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevClose >= _prevMid && close < mid && close < ema && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevClose = close;
		_prevMid = mid;
	}
}