在 GitHub 上查看

HPCS Inter4 策略 (3518)

概述

该策略将 MetaTrader 专家顾问“_HPCS_IntFourth_MT4_EA_V01_WE”移植到 StockSharp 高级 API。原版脚本在启动后立即建立多头仓位,按照 MetaTrader 点(pip)设置止损和止盈,并在持仓时间达到预设阈值后强制平仓。C# 版本通过保护管理器与 1 秒计时器的组合,完整复刻了上述流程。

交易逻辑

  1. 初始化阶段

    • 启动时根据交易品种的 PriceStep 与小数位数计算 MetaTrader 点值(对于 5 位或 3 位报价的品种会乘以 10)。
    • 调用 StartProtection 设置止损和止盈距离,其中止损距离包含原 EA 中使用 OrderModify 添加的额外缓冲。
    • 交易量固定,由 OrderVolume 参数提供。
  2. 入场

    • 策略启动后立即发送一张市价买单,不再追加新的建仓指令。
    • 当收到首笔成交时记录成交时间,用于后续的持仓计时。
  3. 出场

    • 计时器每秒检查一次当前仓位状态。
    • 当持仓时间达到 CloseDelaySeconds 指定的阈值且仍持有多头时,发送市价卖单强制平仓。
    • 止损与止盈由保护管理器维护,并使用市价方式执行,与原脚本中调用 OrderClose 的行为一致。

整体逻辑仅参与做多,与 MetaTrader 原策略保持一致。

参数

名称 说明 默认值 可优化
OrderVolume 发送初始市价买单时使用的固定交易量。 1
StopLossPips 初始止损的 MetaTrader 点距离。 10
ExtraStopPips 建仓后额外再向下调整的 MetaTrader 点数。 10
TakeProfitPips 止盈目标的 MetaTrader 点距离。 10
CloseDelaySeconds 持仓达到该秒数后触发强制平仓,0 表示关闭该功能。 30

实现细节

  • 点值计算会在 3 位或 5 位报价品种上乘以 10,以匹配 MetaTrader 的 pip 定义。
  • StartProtectionUnitTypes.Price 为单位管理止损和止盈,并使用市价平仓方式复现原始脚本。
  • OnNewMyTrade 中记录首次买入成交时间,并在仓位完全平掉时重置状态,确保计时逻辑正确运行。
  • 采用 1 秒周期的计时器来模拟原脚本 OnTick 中的时间检查,即便市场暂时没有报价也能继续倒计时。
  • 源码中的注释全部为英文,遵循仓库的统一规范。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the "_HPCS_IntFourth_MT4_EA_V01_WE" MetaTrader expert advisor.
/// Uses SMA crossover to enter positions with time-based exit.
/// </summary>
public class HpcsInter4Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _isFirstValue = true;

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

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

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

	public HpcsInter4Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for calculation", "General");

		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_isFirstValue = true;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_isFirstValue = true;

		var fastSma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowSma = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastSma, slowSma, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_isFirstValue)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_isFirstValue = false;
			return;
		}

		// Fast crosses above slow - buy
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			BuyMarket();
		}
		// Fast crosses below slow - sell
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			SellMarket();
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}