在 GitHub 上查看
Tops Bottoms Trend RSI 策略
概述
本策略是 MetaTrader 专家顾问 “Tops bottoms trend and rsi ea” 在 StockSharp 平台上的移植版本。算法订阅所选时间框架的蜡烛,始终只处理已经收盘的柱线,利用可配置的历史窗口捕捉新的顶部或底部,并结合相对强弱指数 (RSI) 过滤信号。当条件满足时,策略仅开一笔市价单,并根据预设的点差距离立即计算止损和止盈。
交易逻辑
- 数据来源:订阅参数指定的蜡烛类型,只在蜡烛状态为
Finished 时进行计算,避免使用尚未完成的价格信息。
- 底部识别(做多):当前蜡烛的收盘价需比
BuyTrendCandles 根之前的最高价低至少 BuyTrendPips 个点,同时期间所有低点都必须高于当前收盘价,且趋势质量过滤器 (BuyTrendQuality) 要求最近的高点不能远离参考高点。当上述形态出现且上一根蜡烛的 RSI 低于 BuyRsiThreshold 时,策略以 BuyVolume 的手数开多。
- 顶部识别(做空):当前蜡烛的收盘价需比
SellTrendCandles 根之前的最低价高至少 SellTrendPips 个点。期间所有高点必须低于当前收盘价,而趋势质量过滤器 (SellTrendQuality) 保证最近低点紧贴参考低点。若同时上一根蜡烛的 RSI 高于 SellRsiThreshold,策略以 SellVolume 的手数开空。
- 风险控制:入场后记录成交价并计算基于点差的保护水平。止损使用
BuyStopLossPips 或 SellStopLossPips,止盈优先根据止损距离乘以 BuyTakeProfitPercentOfStop / SellTakeProfitPercentOfStop 的百分比得到;若多头百分比为零,则改用固定的 BuyTakeProfitPips。之后的蜡烛只要触及对应的止损或止盈,仓位即通过市价单平仓。
- 仓位管理:任意时刻仅允许存在一笔仓位,持仓或有挂单时所有新信号都会忽略。RSI 过滤始终使用上一根蜡烛的值(向后偏移一根),与原始 EA 保持一致。
参数
| 参数 |
说明 |
默认值 |
BuyVolume |
做多开仓手数。 |
0.01 |
BuyStopLossPips |
多头止损距离(点)。 |
20 |
BuyTakeProfitPips |
多头固定止盈(点),当百分比关闭时启用。 |
5 |
BuyTakeProfitPercentOfStop |
多头止盈占止损距离的百分比。 |
100 |
SellVolume |
做空开仓手数。 |
0.01 |
SellStopLossPips |
空头止损距离(点)。 |
20 |
SellTakeProfitPercentOfStop |
空头止盈占止损距离的百分比。 |
100 |
SellTrendCandles |
搜索顶部时回溯的蜡烛数量。 |
10 |
SellTrendPips |
做空所需相对最低价的最小上破幅度(点)。 |
20 |
SellTrendQuality |
空头趋势质量过滤(限制在 1–9 之间)。 |
5 |
BuyTrendCandles |
搜索底部时回溯的蜡烛数量。 |
10 |
BuyTrendPips |
做多所需相对最高价的最小下破幅度(点)。 |
20 |
BuyTrendQuality |
多头趋势质量过滤(限制在 1–9 之间)。 |
5 |
BuyRsiPeriod |
多头确认使用的 RSI 周期。 |
14 |
BuyRsiThreshold |
多头信号需要跌破的 RSI 超卖阈值。 |
40 |
SellRsiPeriod |
空头确认使用的 RSI 周期。 |
14 |
SellRsiThreshold |
空头信号需要突破的 RSI 超买阈值。 |
60 |
CandleType |
策略使用的蜡烛时间框架。 |
30 分钟 |
备注
- 点差距离通过交易品种的
PriceStep 转换为价格。对五位或带分数点的外汇报价会自动还原到传统的点值,与原专家顾问的规则一致。
- 由于 RSI 过滤基于上一根蜡烛,策略在启动后的前几根蜡烛会等待指标形成,随后始终维持一根的延迟。
- 每当仓位完全平仓时,对应的止损与止盈都会被清除,以确保下一次进场使用全新的风险设置。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Tops Bottoms Trend RSI strategy: RSI trend direction with EMA filter.
/// Buys when RSI above 50 and close above EMA, sells when RSI below 50 and close below EMA.
/// </summary>
public class TopsBottomsTrendRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _emaPeriod;
private decimal _prevRsi;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public TopsBottomsTrendRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_hasPrev = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevRsi <= 45 && rsiValue > 45 && candle.ClosePrice > emaValue && Position <= 0)
BuyMarket();
else if (_prevRsi >= 55 && rsiValue < 55 && candle.ClosePrice < emaValue && Position >= 0)
SellMarket();
}
_prevRsi = rsiValue;
_hasPrev = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class tops_bottoms_trend_rsi_strategy(Strategy):
def __init__(self):
super(tops_bottoms_trend_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._rsi_period = self.Param("RsiPeriod", 21)
self._ema_period = self.Param("EmaPeriod", 50)
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 RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
def OnReseted(self):
super(tops_bottoms_trend_rsi_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(tops_bottoms_trend_rsi_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, ema, self._process_candle).Start()
def _process_candle(self, candle, rsi_value, ema_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
ema_val = float(ema_value)
close = float(candle.ClosePrice)
if self._has_prev:
if self._prev_rsi <= 45 and rsi_val > 45 and close > ema_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_rsi >= 55 and rsi_val < 55 and close < ema_val and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rsi_val
self._has_prev = True
def CreateClone(self):
return tops_bottoms_trend_rsi_strategy()