在 GitHub 上查看

HarVesteR 策略

概述

HarVesteR 策略源自同名的 MetaTrader 智能交易系统,属于典型的趋势跟随模型。策略通过 MACD 动能确认与两条简单移动平均线配合,判定市场方向并管理持仓。可选的 ADX 滤波器能够在趋势充足时才允许开仓,从而减少震荡期的交易。

默认参数完全复刻原版 EA:MACD(12, 24, 9)、50 周期管理 SMA、100 周期趋势 SMA,以及当价格走出两倍初始风险距离时的分批止盈机制。

交易逻辑

  1. 趋势偏向——100 周期 SMA 充当方向门槛。收盘价跌破该均线时激活多头准备,收盘价上破时激活空头准备。每次交易完成后标志会被清零,只有当价格重新穿越趋势均线时才会再次允许同方向入场。
  2. MACD 确认——只有当 MACD 主线位于零轴预期一侧,且在最近 MACD 确认柱数 内至少有一次位于另一侧时才算有效信号,对应了原始 MQL 代码中向前回溯查找异号的循环。
  3. 入场条件——多头要求收盘价加上设定的点差偏移量同时高于两条 SMA,MACD 为正值,并且(若启用)ADX 大于 50;空头逻辑为镜像条件。
  4. 初始止损——止损价格取最近 止损柱数 根已完成 K 线的最低价(多头)或最高价(空头),与 MQL 中 iLowest/iHighest 以偏移 1 根的调用保持一致。
  5. 仓位管理——当价格向盈利方向运行,达到初始风险距离的 风险倍数 时平掉一半仓位,并将止损移动到入场价。剩余仓位在 50 周期 SMA 与经偏移调整后的收盘价发生反向交叉时全部离场。
  6. 保护性离场——任意一根 K 线触及当前止损价位都会立即平仓,防止回撤扩大。

参数

参数 说明 默认值
Fast EMA MACD 中使用的快速 EMA 周期。 12
Slow EMA MACD 中使用的慢速 EMA 周期。 24
Signal EMA MACD 信号线平滑周期。 9
MACD Confirmation Bars MACD 需在该数量的 K 线内出现一次反向值。 6
Trend SMA 管理用 SMA 的周期,控制拖尾退出。 50
Filter SMA 趋势过滤 SMA 的周期。 100
Offset (points) 与均线比较时加/减的点数偏移。 10
Stop Bars 用于计算初始止损的历史 K 线数量。 6
Risk Multiplier 触发分批止盈时的风险倍数。 2.0
Use ADX 是否启用 ADX>50 的趋势强度过滤。 关闭
ADX Period 启用过滤时 ADX 的计算周期。 14
Candle Type 指标所用的 K 线类型(默认 1 小时)。 1 小时

实现细节

  • 点值换算优先采用 Security.StepSecurity.PriceStep,若行情未提供最小跳动则退化为 0.0001,与原始外汇策略的“Point” 概念保持一致。
  • 分批止盈通过市价单直接平掉当前仓位的一半,完全复刻原 EA 中的手数调整方式。
  • 调用了 StartProtection(),确保策略在真正进场前已经启动内置的保护机制。
  • 当 ADX 过滤关闭时,算法会用常数 60 代替 ADX 数值,因此整体行为与历史脚本完全一致。

使用建议

  1. 启动策略前请设置 Volume(下单手数),该值同时影响初始建仓与分批平仓的数量。
  2. 根据研究周期调整 Candle Type。原版针对小时线进行设计,如需尝试更短周期可结合参数优化。
  3. MACD Confirmation BarsOffset (points)Risk Multiplier 对胜率和交易频率影响最大,建议优先纳入回测调优范围。
using System;



using StockSharp.Algo.Indicators;

using StockSharp.Algo.Strategies;

using StockSharp.BusinessEntities;

using StockSharp.Messages;



namespace StockSharp.Samples.Strategies;



public class HarVesteRMacdTrendStrategy : Strategy

{

	private readonly StrategyParam<int> _fastPeriod;

	private readonly StrategyParam<int> _slowPeriod;

	private readonly StrategyParam<DataType> _candleType;



	private decimal _prevFast; private decimal _prevSlow; private bool _hasPrev;

	private int _cooldown;



	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }

	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

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



	public HarVesteRMacdTrendStrategy()

	{

		_fastPeriod = Param(nameof(FastPeriod), 12).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 26).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");

	}



	/// <inheritdoc />

	protected override void OnReseted()

	{

		base.OnReseted();

		_prevFast = default;

		_prevSlow = default;

		_hasPrev = default;

		_cooldown = default;

	}



	/// <inheritdoc />

	protected override void OnStarted2(DateTime time)

	{

		base.OnStarted2(time);

		_hasPrev = false;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };

		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription.Bind(fast, slow, ProcessCandle).Start();

	}



	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)

	{

		if (candle.State != CandleStates.Finished) return;

		if (!IsFormedAndOnlineAndAllowTrading()) return;

		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldown > 0)

		{

			_cooldown--;

			_prevFast = fast; _prevSlow = slow;

			return;

		}



		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)

		{

			var volume = Volume + Math.Abs(Position);

			BuyMarket(volume);

			_cooldown = 2;

		}

		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)

		{

			var volume = Volume + Math.Abs(Position);

			SellMarket(volume);

			_cooldown = 2;

		}

		_prevFast = fast; _prevSlow = slow;

	}

}