在 GitHub 上查看

Alexav D1 Profit GBPUSD 策略

概述

Alexav D1 Profit GBPUSD 是从 MetaTrader 4 智能交易系统 Alexav_d1_profit_gbpusd.mq4 转换而来的日线突破策略。该策略基于 GBP/USD 的日线 K 线运行,每个交易日(周二至周五)只评估一次已经完成的蜡烛。RSI 和 MACD 提供动量过滤,ATR 用于生成波动率自适应的止损和分级止盈目标。

交易逻辑

  1. 指标准备
    • 使用相同周期的两条 EMA 分别应用于最高价和最低价,构建多头和空头参考线。
    • 10 周期 RSI 衡量动量,当 RSI 触及极值时会暂时禁止新的同向交易。
    • MACD(5/24/14)比较最近两次柱状图数值,用于识别加速度变化。
    • ATR(28)提供波动率单位,用来计算止损和止盈距离。
  2. 交易时段过滤
    • 仅在周二至周五的日线蜡烛收盘后评估一次。周一及周末被忽略。
  3. 做多条件
    • 前一根日线收盘价必须高于向前两根计算的高价 EMA。
    • 前一根日线 RSI 必须高于上方阈值(默认 60),但低于上限(默认 80)。
    • MACD 需满足:两根之前低于 0,或最近一次柱状图相对上一数值的增长幅度超过阈值。
    • 当前一根日线开盘价重新跌破高价 EMA 时,允许再次触发新的多头批次。
  4. 做空条件
    • 逻辑与做多相反,使用低价 EMA、RSI 下限阈值(39 / 25)以及相同的 MACD 加速度过滤。

持仓管理

当信号成立时,策略会按当前 Volume 开出四笔市价单:

  • 止损:所有订单共用一个保护性止损,距离为 ATR * AtrStopMultiplier(默认 1.6)。
  • 止盈:第 i 笔订单的目标为 AtrTargetMultiplier * (1 + i / 2) 倍 ATR,对应原始 EA 的 1.0、1.5、2.0、2.5 ATR 目标。
  • 冲突处理:如存在反向持仓,会先平仓后再开新批次;开启多头批次会清除所有未完成的空头批次(反之亦然)。

策略基于已完成的蜡烛监控仓位:若日内最低价触及止损,则以市价平掉对应多单;若最高价达到目标,同样平仓。空头仓位的处理对称,使用最高价检查止损、最低价检查止盈。

参数

参数 说明 默认值
CandleType 主数据序列,默认使用日线。 1 天
MaPeriod 作用于高低价 EMA 的周期。 6
RsiPeriod RSI 动量过滤周期。 10
AtrPeriod ATR 周期,用于止损与止盈。 28
AtrStopMultiplier 止损 ATR 倍数。 1.6
AtrTargetMultiplier 止盈 ATR 基准倍数。 1.0
RsiUpperLevel 做多动量确认阈值。 60
RsiUpperLimit 多头禁入的 RSI 上限。 80
RsiLowerLevel 做空动量确认阈值。 39
RsiLowerLimit 空头禁入的 RSI 下限。 25
FastMaPeriod MACD 快速 EMA 周期。 5
SlowMaPeriod MACD 慢速 EMA 周期。 24
SignalMaPeriod MACD 信号线 EMA 周期。 14
MacdDiffBuy 做多所需的 MACD 加速度阈值。 0.5
MacdDiffSell 做空所需的 MACD 加速度阈值。 0.15

在启动策略前,请将 Volume 设置为每笔订单期望的手数。

说明

  • 转换版本保留了原始 EA 每日只评估一次的特点。
  • 回测时请使用 GBP/USD 的日线历史数据以获得最接近原版的表现。
  • 止损与止盈依赖于日线最高/最低价模拟,日内的瞬时波动不会被捕捉。
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>
/// Alexav D1 Profit breakout strategy.
/// Buys when price breaks above EMA and RSI is rising.
/// Sells when price breaks below EMA and RSI is falling.
/// </summary>
public class AlexavD1ProfitGbpUsdBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevEma;
	private decimal _prevRsi;
	private bool _hasPrev;

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

	public AlexavD1ProfitGbpUsdBreakoutStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "EMA period", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI 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();
		_prevClose = 0m;
		_prevEma = 0m;
		_prevRsi = 0m;
		_hasPrev = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevClose = close;
			_prevEma = emaValue;
			_prevRsi = rsiValue;
			_hasPrev = true;
			return;
		}

		// Breakout above EMA with rising RSI
		if (_prevClose <= _prevEma && close > emaValue && rsiValue > _prevRsi && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Breakout below EMA with falling RSI
		else if (_prevClose >= _prevEma && close < emaValue && rsiValue < _prevRsi && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevClose = close;
		_prevEma = emaValue;
		_prevRsi = rsiValue;
	}
}