在 GitHub 上查看

RPM5 BullsBearsEyes 策略

概述

RPM5 BullsBearsEyes 策略 是 MetaTrader 4 专家顾问 Rpm5_mt4v1 的 C# 版本。原始 EA 通过 Bulls Power 与 Bears Power 构建自定义的 BullsBearsEyes 指标,并根据该指标的多空倾向持有单一仓位。本移植版使用 StockSharp 高阶 API 重现相同行为,同时保留原有的风险参数、移动止损逻辑和信号阈值。

指标重建

  • 计算配置周期上的 Bulls PowerBears Power 指标。
  • 将二者的和送入与 MT4 指标相同的四级 IIR 平滑器,平滑系数 Gamma 决定振荡器的响应速度。
  • 平滑输出被压缩在 01 之间。数值高于阈值表示多头占优,低于阈值则代表空头占优。当任一方完全耗尽时会出现精确的 0 或 1,与原指标的极端情况一致。

交易规则

  1. 订阅所选时间框(默认 5 分钟),仅处理已经收盘的 K 线。
  2. 空仓时评估 BullsBearsEyes 数值:
    • 做多:当前值严格大于 Threshold(默认 0.5)。
    • 做空:当前值严格小于 Threshold
    • 策略始终只持有一个仓位,在风险控制平仓之前会忽略反向信号。
  3. 持仓后仅由止损、止盈或跟踪止损触发退出,不会主动换向。

风险控制

  • 止损 / 止盈:完全复刻原始 25 / 150 点设置。每次建仓时都会利用标的的 PriceStep 将点值换算为价格距离。
  • ATR 跟踪止损:每根完成的 K 线都会计算周期为 AtrPeriod(默认 5)的平均真实波幅。跟踪距离 = 1 点 + AtrMultiplier × ATR。当收盘价超过该距离时,保护性止损会上移或下移以保持同样的间隔,对应 MT4 中反复调用 OrderModify 的逻辑。
  • 在处理新信号之前优先检查保护性价位,确保退出始终先于新的入场决定。

参数

名称 默认值 说明
Bulls/Bears Period 13 Bulls Power 与 Bears Power 的均线周期。
Gamma 0.5 BullsBearsEyes 振荡器使用的四级 IIR 平滑系数。
Threshold 0.5 多头(> 阈值)与空头(< 阈值)区域的分界线。
ATR Period 5 ATR 跟踪止损的计算周期。
ATR Multiplier 1.5 ATR 的乘数,用于得到跟踪距离。
Stop Loss (pips) 25 止损距离(以点数表示,建仓时转换为价格)。
Take Profit (pips) 150 止盈距离(以点数表示,建仓时转换为价格)。
Trade Volume 1 每次建仓使用的市价单数量。
Candle Type 5 分钟 K 线 策略处理的时间框。

说明

  • MT4 中绘制的日内通道线仅用于视觉辅助,因此在该移植版本中省略。
  • 源码中的注释全部使用英文。
  • 测试脚本保持不变,如需验证请运行解决方案级检查。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// RPM5 Bulls Bears Eyes strategy using Bull/Bear power with EMA filter.
/// Buy when bull power is positive and bear power crosses above threshold.
/// Sell when bear power is negative and bull power crosses below threshold.
/// </summary>
public class Rpm5BullsBearsEyesStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _powerPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevBull;
	private decimal _prevBear;
	private bool _hasPrev;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int PowerPeriod { get => _powerPeriod.Value; set => _powerPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public Rpm5BullsBearsEyesStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 13)
			.SetDisplay("EMA Period", "EMA trend period", "Indicators");

		_powerPeriod = Param(nameof(PowerPeriod), 13)
			.SetDisplay("Power Period", "Bulls/Bears power period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevBull = 0m;
		_prevBear = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var bulls = new BullPower { Length = PowerPeriod };
		var bears = new BearPower { Length = PowerPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, bulls, bears, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal bullValue, decimal bearValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevBull = bullValue;
			_prevBear = bearValue;
			_hasPrev = true;
			return;
		}

		// Long: price above EMA, bull power positive, bear power crossing from negative to less negative
		var longSignal = candle.ClosePrice > emaValue && bullValue > 0 && _prevBear < 0 && bearValue > _prevBear;
		// Short: price below EMA, bear power negative, bull power crossing from positive to less positive
		var shortSignal = candle.ClosePrice < emaValue && bearValue < 0 && _prevBull > 0 && bullValue < _prevBull;

		if (Position <= 0 && longSignal)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (Position >= 0 && shortSignal)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevBull = bullValue;
		_prevBear = bearValue;
	}
}