股票布林带RSI策略
Timeframe
5m
Direction
Long Only
Stoploss
-3.5%
Trailing Stop
Yes
ROI
0m: 8.0%, 30m: 5.0%, 60m: 3.0%, 120m: 1.5%
Interface Version
3
Startup Candles
N/A
Indicators
6
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
"""
股票布林带RSI策略 - 适用于高波动性股票
基于对sh600629的分析开发
策略特点:
1. 多时间框架趋势确认
2. 布林带+RSI超卖超买判断
3. 支持做T(高抛低吸)
4. 放量确认机制
适用场景:
- 日内波动幅度>2%的股票
- 有明确趋势的股票
- 成交量活跃的股票
"""
from freqtrade.strategy import IStrategy, informative
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
class StockBollingerRSIStrategy(IStrategy):
"""
股票布林带RSI策略
"""
# 策略基础设置
INTERFACE_VERSION = 3
# 最小ROI设置 - 根据股票波动特性调整
minimal_roi = {
"0": 0.08, # 8%立即获利
"30": 0.05, # 30分钟后5%
"60": 0.03, # 1小时后3%
"120": 0.015 # 2小时后1.5%
}
# 止损设置 - 基于ATR平均值0.479设置为-3.5%
stoploss = -0.035
# 追踪止损
trailing_stop = True
trailing_stop_positive = 0.02
trailing_stop_positive_offset = 0.03
trailing_only_offset_is_reached = True
# 时间框架
timeframe = '5m'
# 启动模式
process_only_new_candles = True
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
# 持仓数量
max_entry_position_adjustment = 2 # 允许加仓2次(做T用)
# 策略参数
buy_rsi_threshold = 35 # RSI低于此值为超卖
sell_rsi_threshold = 65 # RSI高于此值为超买
bb_period = 20 # 布林带周期
bb_std = 2 # 布林带标准差倍数
volume_factor = 1.3 # 放量倍数
ma_short = 5 # 短期均线
ma_medium = 10 # 中期均线
ma_long = 20 # 长期均线
# 信息性时间框架(多时间框架分析)
@informative('15m')
def populate_indicators_15m(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""15分钟时间框架指标"""
# RSI
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
# 布林带
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2)
dataframe['bb_lower'] = bollinger['lower']
dataframe['bb_middle'] = bollinger['mid']
dataframe['bb_upper'] = bollinger['upper']
# 均线
dataframe['ma20'] = ta.SMA(dataframe, timeperiod=20)
return dataframe
@informative('1h')
def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""60分钟时间框架指标 - 判断大趋势"""
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
# 均线多头排列判断
dataframe['ma5'] = ta.SMA(dataframe, timeperiod=5)
dataframe['ma10'] = ta.SMA(dataframe, timeperiod=10)
dataframe['ma20'] = ta.SMA(dataframe, timeperiod=20)
# RSI
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
return dataframe
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
5分钟主时间框架指标
"""
# RSI
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
# 布林带
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=self.bb_period, stds=self.bb_std)
dataframe['bb_lower'] = bollinger['lower']
dataframe['bb_middle'] = bollinger['mid']
dataframe['bb_upper'] = bollinger['upper']
dataframe['bb_width'] = (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_middle'] * 100
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
# 移动平均线
dataframe['ma5'] = ta.SMA(dataframe, timeperiod=self.ma_short)
dataframe['ma10'] = ta.SMA(dataframe, timeperiod=self.ma_medium)
dataframe['ma20'] = ta.SMA(dataframe, timeperiod=self.ma_long)
# EMA
dataframe['ema12'] = ta.EMA(dataframe, timeperiod=12)
dataframe['ema26'] = ta.EMA(dataframe, timeperiod=26)
# ATR - 波动率
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
# 成交量指标
dataframe['volume_ma20'] = ta.SMA(dataframe['volume'], timeperiod=20)
dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_ma20']
# 价格变化
dataframe['price_change'] = dataframe['close'].pct_change() * 100
# 日内波动幅度
dataframe['intraday_range'] = (dataframe['high'] - dataframe['low']) / dataframe['low'] * 100
# MACD金叉死叉
dataframe['macd_cross_up'] = qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal'])
dataframe['macd_cross_down'] = qtpylib.crossed_below(dataframe['macd'], dataframe['macdsignal'])
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
入场信号 - 综合多时间框架
"""
conditions = []
# === 主入场信号(建仓) ===
# 条件1: 60分钟趋势向上
trend_up_1h = (
(dataframe['macd_1h'] > dataframe['macdsignal_1h']) & # MACD多头
(dataframe['ma5_1h'] > dataframe['ma20_1h']) # 均线多头
)
# 条件2: 15分钟确认回调
pullback_15m = (
(dataframe['rsi_15m'] < 40) | # RSI回调
(dataframe['close'] < dataframe['bb_lower_15m']) # 触及布林下轨
)
# 条件3: 5分钟精确入场
entry_signal_5m = (
(dataframe['rsi'] < self.buy_rsi_threshold) & # RSI超卖
(
(dataframe['close'] <= dataframe['bb_lower']) | # 触及布林下轨
(dataframe['close'] < dataframe['ma5'] * 0.98) # 远离MA5
) &
(dataframe['volume_ratio'] > self.volume_factor) # 放量
)
# 综合入场条件
main_entry = (
trend_up_1h &
pullback_15m &
entry_signal_5m
)
# === 强势入场信号(评分>=5) ===
strong_entry = (
(dataframe['macd_cross_up']) & # MACD金叉
(dataframe['ma5'] > dataframe['ma10']) & # 均线多头
(dataframe['ma10'] > dataframe['ma20']) &
(dataframe['volume_ratio'] > 1.5) & # 强放量
(dataframe['price_change'] > 0.5) # 上涨
)
# === 超卖反弹信号 ===
oversold_bounce = (
(dataframe['rsi'].shift(1) < 30) & # 前一根RSI超卖
(dataframe['rsi'] > 30) & # 当前反弹
(dataframe['close'].shift(1) < dataframe['bb_lower'].shift(1)) & # 前一根触及下轨
(dataframe['close'] > dataframe['bb_lower']) # 当前反弹
)
# === 突破MA20信号 ===
ma20_breakout = (
(dataframe['close'].shift(1) < dataframe['ma20'].shift(1)) &
(dataframe['close'] > dataframe['ma20']) &
(dataframe['volume_ratio'] > 1.3)
)
# 组合所有入场条件
conditions.append(main_entry | strong_entry | oversold_bounce | ma20_breakout)
if conditions:
dataframe.loc[
conditions[0],
'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
出场信号 - 高抛做T
"""
conditions = []
# === 高抛信号(做T用) ===
# 条件1: RSI超买
rsi_overbought = (dataframe['rsi'] > self.sell_rsi_threshold)
# 条件2: 触及布林上轨
bb_upper_touch = (
(dataframe['close'] >= dataframe['bb_upper']) |
(dataframe['close'] > dataframe['ma5'] * 1.02) # 远离MA5上方
)
# 条件3: MACD死叉
macd_cross_down = (dataframe['macd_cross_down'])
# === 止盈信号 ===
# 基于15分钟RSI超买
profit_take_15m = (
(dataframe['rsi_15m'] > 70) &
(dataframe['close'] > dataframe['bb_upper_15m'])
)
# === 趋势转弱信号 ===
trend_weak = (
(dataframe['ma5'] < dataframe['ma10']) & # 短期均线死叉
(dataframe['rsi'] < 50) & # RSI回落
(dataframe['volume_ratio'] > 1.5) # 放量下跌
)
# 组合出场条件
high_sell = (
(rsi_overbought & bb_upper_touch) | # 高抛
macd_cross_down | # 死叉
profit_take_15m | # 止盈
trend_weak # 趋势转弱
)
conditions.append(high_sell)
if conditions:
dataframe.loc[
conditions[0],
'exit_long'] = 1
return dataframe
def adjust_trade_position(self, trade, current_time, current_rate,
current_profit, min_stake, max_stake, **kwargs):
"""
仓位调整 - 做T加仓逻辑
"""
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
if len(dataframe) == 0:
return None
last_candle = dataframe.iloc[-1]
# 只在盈利时做T
if current_profit > 0.01:
# 低吸加仓条件
if (last_candle['rsi'] < 35 and
last_candle['close'] < last_candle['bb_lower'] and
trade.nr_of_successful_entries < 3):
# 加仓30%
return min_stake * 0.3
return None
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time, entry_tag, **kwargs) -> bool:
"""
入场确认 - 最后一道防线
"""
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(dataframe) < 1:
return False
last_candle = dataframe.iloc[-1]
# 检查日内波动幅度是否足够(>2%做T成功率高)
if last_candle['intraday_range'] < 1.5:
return False
# 检查60分钟趋势
if last_candle['rsi_1h'] > 80: # 60分钟严重超买,不入场
return False
return True