在 GitHub 上查看
Alexav D1 Profit GBPUSD 策略
概述
Alexav D1 Profit GBPUSD 是从 MetaTrader 4 智能交易系统 Alexav_d1_profit_gbpusd.mq4 转换而来的日线突破策略。该策略基于 GBP/USD 的日线 K 线运行,每个交易日(周二至周五)只评估一次已经完成的蜡烛。RSI 和 MACD 提供动量过滤,ATR 用于生成波动率自适应的止损和分级止盈目标。
交易逻辑
- 指标准备
- 使用相同周期的两条 EMA 分别应用于最高价和最低价,构建多头和空头参考线。
- 10 周期 RSI 衡量动量,当 RSI 触及极值时会暂时禁止新的同向交易。
- MACD(5/24/14)比较最近两次柱状图数值,用于识别加速度变化。
- ATR(28)提供波动率单位,用来计算止损和止盈距离。
- 交易时段过滤
- 仅在周二至周五的日线蜡烛收盘后评估一次。周一及周末被忽略。
- 做多条件
- 前一根日线收盘价必须高于向前两根计算的高价 EMA。
- 前一根日线 RSI 必须高于上方阈值(默认 60),但低于上限(默认 80)。
- MACD 需满足:两根之前低于 0,或最近一次柱状图相对上一数值的增长幅度超过阈值。
- 当前一根日线开盘价重新跌破高价 EMA 时,允许再次触发新的多头批次。
- 做空条件
- 逻辑与做多相反,使用低价 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class alexav_d1_profit_gbp_usd_breakout_strategy(Strategy):
"""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."""
def __init__(self):
super(alexav_d1_profit_gbp_usd_breakout_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA period", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_ema = 0.0
self._prev_rsi = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
def OnReseted(self):
super(alexav_d1_profit_gbp_usd_breakout_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._prev_rsi = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(alexav_d1_profit_gbp_usd_breakout_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, rsi, self._process_candle).Start()
def _process_candle(self, candle, ema_value, rsi_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema_value)
rsi_val = float(rsi_value)
if not self._has_prev:
self._prev_close = close
self._prev_ema = ema_val
self._prev_rsi = rsi_val
self._has_prev = True
return
# Breakout above EMA with rising RSI
if self._prev_close <= self._prev_ema and close > ema_val and rsi_val > self._prev_rsi and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Breakout below EMA with falling RSI
elif self._prev_close >= self._prev_ema and close < ema_val and rsi_val < self._prev_rsi and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_ema = ema_val
self._prev_rsi = rsi_val
def CreateClone(self):
return alexav_d1_profit_gbp_usd_breakout_strategy()