在 GitHub 上查看

FiboChannel Line 策略

概述

FiboChannel Line 策略 是 MetaTrader “FIBOCHANNEL” 智能交易系统的移植版本。 原版依赖手工绘制的斐波那契通道方向、较高周期上的动量摆动,以及线性加权移动平均与 MACD 的组合。 在 StockSharp 中我们使用高层 API 复现其核心思想,并加入内置的风险控制。

主要特点:

  • 通过快、慢两个线性加权移动平均线 (LWMA) 判断趋势方向。
  • 使用动量指标在中性水平 100 周围的偏离来确认动能爆发。
  • 通过 MACD 线与信号线的相对位置过滤交易。
  • 用线性回归斜率取代对图表对象的读取,以判断通道方向。
  • 借助 StartProtection 实现百分比止盈、止损和权益回撤保护。

默认时间框架为 30 分钟 K 线,兼顾反应速度与指标稳定性。策略可应用于任何支持蜡烛聚合的数据源。

交易逻辑

  1. 趋势过滤:当快 LWMA 高于慢 LWMA 时只考虑多头信号;低于时只考虑空头信号。
  2. 动量要求:保存最近三个动量值,只要其中任意一个与 100 的差值超过阈值即视为动能有效, 这一机制对应 MQL 版本中的多周期动量检测。
  3. MACD 过滤:多头信号要求 MACD 线高于信号线,空头信号要求 MACD 线低于信号线。
  4. 通道方向:线性回归斜率需要高于(或低于)Slope Threshold,以模拟原策略中通道锚点的比较。
  5. 入场与反手:满足所有条件且当前持仓方向不同,取消挂单后以 Volume + |Position| 的手数市价成交, 从而完成顺滑的反手。
  6. 离场:若通道方向或 MACD 过滤条件不再成立,则取消挂单并平掉现有仓位; 同时百分比止盈、止损和最大回撤限制由 StartProtection 自动执行。

参数说明

参数 含义 默认值
Candle Type 指标计算使用的蜡烛类型。 30 分钟
Fast LWMA 快速线性加权移动平均长度。 6
Slow LWMA 慢速线性加权移动平均长度。 85
Momentum Period 动量指标周期。 14
Momentum Threshold 动量偏离 100 的最小阈值。 0.3
Channel Length 计算线性回归斜率所用的柱数。 50
Slope Threshold 斜率确认趋势方向的最小绝对值。 0.0
MACD Fast MACD 内部的快 EMA 周期。 12
MACD Slow MACD 内部的慢 EMA 周期。 26
MACD Signal MACD 信号线周期。 9
Take Profit % 百分比止盈距离。 2
Stop Loss % 百分比止损距离。 1
Equity Risk % 允许的最大权益回撤百分比。 3

所有数值参数都提供了与原 MQL 输入类似的优化范围设置。

风险控制

  • StartProtection 会基于百分比自动设置止损与止盈;
  • 一旦权益回撤超过设定的百分比,将自动平仓并停止交易;
  • 通过这些内置保护替代了原版中复杂的资金管理与移动止损逻辑,使行为更加透明。

与 MetaTrader 版本的差异

  • 由于 StockSharp 策略无法读取手绘通道,改用线性回归斜率进行方向确认。
  • 原策略的大量资金管理、保本和拖动止损代码被统一替换为 StartProtection
  • 指标组合保持一致,但通过高阶绑定获取数据,无需手动访问指标历史值。
  • 移除了邮件、提醒等通知逻辑,由平台日志统一输出。

使用建议

  1. 将策略连接到指定的证券和投资组合,通过 Volume 属性设置下单手数,并根据品种调整参数。
  2. 确保所选时间框架拥有足够的历史数据,以便动量缓冲区和斜率指标形成有效值。
  3. 建议先在模拟或小仓位环境中运行,根据标的波动性微调动量阈值和风险参数。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class FiboChannelLineStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public FiboChannelLineStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}