多因子RSI反转策略


创建日期: 2023-12-06 11:57:29 最后修改: 2023-12-06 11:57:29
复制: 0 点击次数: 461
avatar of ChaoZhang ChaoZhang
1
关注
1300
关注者

多因子RSI反转策略

概述

本策略运用RSI指标识别超买超卖现象,结合多种辅助因子如MACD、Stochastic指标等入场。该策略旨在捕捉短期反转机会,属于反转策略。

策略原理

本策略主要利用RSI指标判断市场是否处于超买或超卖状态。当RSI指标超过设定的超买线时,表明市场可能处于超买状态,这时策略选择做空;当RSI指标低于设定的超卖线时,表明市场有可能处于超卖状态,策略此时选择做多。这样通过捕捉行情由一个极端状态切换到另一个极端状态的反转过程中产生的短期交易机会获利。

另外,策略还引入了MACD、Stochastic等多个辅助因子。这些辅助因子的作用是过滤掉一些可能出现的假阳性交易信号。只有当RSI指标发出信号、并且辅助因子也支持该信号时,策略才会采取真正的交易行动。这种多因子配合的方式可以提高策略信号的可靠性,从而也提高了策略的稳定性。

优势分析

这套策略最大的优势在于捕捉效率高,实现了多因子验证提高了信号质量。具体来说主要体现在以下几个方面:

  1. RSI指标本身对市场 regimes 的识别能力较强,可以有效识别超买超卖现象。
  2. 借助多种辅助工具进行多因子验证,提高了信号质量,过滤了大量假阳性。
  3. 策略对参数不敏感,容易优化。

风险及解决方法

该策略也面临一定的风险,主要集中在两个方面:

  1. 反转失败风险。反转信号本身依赖的就是统计套利机会,不排除个别反转失败的概率。可以通过降低仓位,或设置止损来控制风险。
  2. 多头行情下的亏损风险。策略整体上仍以逆市操作为主,在多头行情下难免会出现一定亏损。这需要我们对大趋势判断要准确,必要时通过人工干预来跳过不利的行情环境。

优化方向

本策略后续需要从以下几个方面进行优化:

  1. 测试不同品种,寻找最佳参数组合。策略对参数并不敏感,但仍建议针对不同品种寻找最优参数。
  2. 增加自适应退出机制。可以测试加入动态止损、时间退出等方式,使策略更适应市场的变化。
  3. 引入机器学习算法。可以尝试让模型学习判断反转成功的概率,从而提高策略胜率。

总结

本策略整体而言是一个短线反转策略。利用RSI指标判断超买超卖的能力,同时借助多种辅助工具进行多因子验证,从而提高了信号的质量。该策略捕捉效率高,稳定性也较好。值得进一步测试和优化,最终实现盈利。

策略源码
/*backtest
start: 2022-12-05 00:00:00
end: 2023-03-24 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//@version=4

strategy(shorttitle='Ain1',title='All in One Strategy', overlay=true, initial_capital = 1000, process_orders_on_close=true, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type=strategy.commission.percent, commission_value=0.18, calc_on_every_tick=true)

kcolor = #0094FF
dcolor = #FF6A00



// -----------------  Strategy Inputs -------------------------------------------------------------
//Backtest dates with auto finish date of today
start = input(defval = timestamp("01 April 2021 00:00 -0500"), title = "Start Time", type = input.time)
finish = input(defval = timestamp("31 December 2021 00:00 -0600"), title = "Start Time", type = input.time)
window()  => true


// Strategy Selection - Long, Short, or Both
strat = input(title="Strategy", defval="Long/Short", options=["Long Only", "Long/Short", "Short Only"])
strat_val = strat == "Long Only" ? 1 : strat == "Long/Short" ? 0 : -1

// Risk Management Inputs
sl= input(10.0, "Stop Loss %", minval = 0, maxval = 100, step = 0.01)
stoploss = sl/100
tp = input(20.0, "Target Profit %", minval = 0, maxval = 100, step = 0.01)
TargetProfit = tp/100

// RSI and Stochastic Inputs
length = input(14, "RSI Length", minval=1)
ob_min = input(52, "Overbought Lookback Minimum Value", minval=0, maxval=200)
ob_lb = input(25, "Overbought Lookback Bars", minval=0, maxval=100)
os_min = input(50, "Oversold Lookback Minimum Value", minval=0, maxval=200)
os_lb = input(35, "Oversold Lookback Bars", minval=0, maxval=100)
source = input(title="Source", type=input.source, defval=close)
RSI = rsi(source, length)

// Define f_print function to show key recommendations for RSI
// f_print(_text) =>
//     // Create label on the first bar.
//     var _label = label(na),
//     label.delete(_label), 
//     _label := label.new(
//      time + (time-time[1]), 
//      ohlc4,
//      _text,
//      xloc.bar_time,
//      yloc.price,
//      color(na),
//      label.style_none,
//      color.gray,
//      size.large,
//      text.align_left
//      )
    

    
// Display highest and lowest RSI values

AvgHigh(src,cnt,val) =>
    total = 0.0
    count = 0
    for i = 0 to cnt
        if src[i] > val
            count := count + 1
            total := total + src[i]
    round(total / count)
    
RSI_high = AvgHigh(RSI, ob_lb, ob_min)

AvgLow(src,cnt,val) =>
    total = 0.0
    count = 0
    for i = 5 to cnt by 5
        if src[i] < val
            count := count + 1
            total := total + src[i]
    round(total / count)

RSI_low = AvgLow(RSI, os_lb, os_min)


// f_print("Recommended RSI Settings:" + "\nOverbought = " + tostring(RSI_high) + "\nOversold = " + tostring(RSI_low))


overbought= input(62, "Overbought")
oversold= input(35, "Oversold")


// Price Movement Inputs
look_back = input(9,"Look Back Bars")
high_source = input(high,"High Source")
low_source= input(low,"Low Source")
HTF = input("","Curernt or Higher time frame only", type=input.resolution)

// EMA and SMA Background Inputs
smoothK     = input(3, "K", minval=1)
smoothD     = input(3, "D", minval=1)
k_mode      = input("SMA", "K Mode", options=["SMA", "EMA", "WMA"])

// MACD Inputs
fastLength = input(5, minval=1, title="EMA Fast Length")
slowLength = input(10, minval=1, title="EMA Slow Length")

// Selections to show or hide the overlays
showZones = input(true, title="Show Bullish/Bearish Zones")
showStoch = input(true, title="Show Stochastic Overlays")
showRSIBS = input(true, title="Show RSI Buy Sell Zones")
showMACD = input(true, title="Show MACD")
color_bars=input(true, "Color Bars")
useXRSI = input(false, "Use RSI crossing back, select only one")
useMACD = input(false, "Use MACD Only, select only one")
useCRSI = input(false, "Use Tweaked Connors RSI, select only one")


// ------------------ Background Colors based on EMA Indicators -----------------------------------
// Uses standard lengths of 9 and 21, if you want control delete the constant definition and uncomment the inputs
haClose(gap) => (open[gap] + high[gap] + low[gap] + close[gap]) / 4
rsi_ema = rsi(haClose(0), length)
v2 = ema(rsi_ema, length)                                                
v3 = 2 * v2 - ema(v2, length)  
emaA = ema(rsi_ema, fastLength)                                     
emaFast = 2 * emaA - ema(emaA, fastLength)
emaB = ema(rsi_ema, slowLength)                                     
emaSlow = 2 * emaB - ema(emaB, slowLength)  

// bullish signal rule: 
bullishRule =emaFast > emaSlow
// bearish signal rule: 
bearishRule =emaFast < emaSlow

// current trading State
ruleState = 0
ruleState := bullishRule ? 1 : bearishRule ? -1 : nz(ruleState[1])
bgcolor(showZones ? ( ruleState==1 ? color.blue : ruleState==-1 ? color.red : color.gray ) : na , title=" Bullish/Bearish Zones", transp=95)


// ------------------  Stochastic Indicator Overlay -----------------------------------------------

// Calculation
// Use highest highs and lowest lows
h_high = highest(high_source ,look_back)
l_low = lowest(low_source ,look_back)

stoch = stoch(RSI, RSI, RSI, length)
k =
 k_mode=="EMA" ? ema(stoch, smoothK) :
 k_mode=="WMA" ? wma(stoch, smoothK) :
 sma(stoch, smoothK)
d = sma(k, smoothD)
k_c = change(k)
d_c = change(d)
kd = k - d

// Plot
signalColor = k>oversold and d<overbought and k>d and k_c>0 and d_c>0 ? kcolor : 
 k<overbought and d>oversold and k<d and k_c<0 and d_c<0 ? dcolor : na
kp = plot(showStoch ? k : na, "K", transp=80, color=kcolor)
dp = plot(showStoch ? d : na, "D", transp=80, color=dcolor)
fill(kp, dp, color = signalColor, title="K-D", transp=88)
signalUp = showStoch ? not na(signalColor) and kd>0 : na
signalDown = showStoch ? not na(signalColor) and kd<0 : na
plot(signalUp ? kd : na, "Signal Up", color=kcolor, transp=90, style=plot.style_columns)
plot(signalDown ? (kd+100) : na , "Signal Down", color=dcolor, transp=90, style=plot.style_columns, histbase=100)


// -------------- Add Price Movement to Strategy for better visualization -------------------------
// Calculations
h1 = vwma(high, length)
l1 = vwma(low, length)
hp = h_high[1]
lp = l_low[1]

// Plot
var plot_color=#353535
var sig = 0
if (h1 >hp)
    sig:=1
    plot_color:=color.lime
else if (l1 <lp)
    sig:=-1
    plot_color:=color.maroon
plot(1,title = "Price Movement Bars", style=plot.style_columns,color=plot_color)
plot(sig,title="Signal 1 or -1",display=display.none)



// --------------------------------------- RSI Plot ----------------------------------------------
// Plot Oversold and Overbought Lines
over = hline(oversold, title="Oversold", color=color.green)
under = hline(overbought, title="Overbought", color=color.red)
fill(over, under, color=#9915FF, transp=90, title="Band Background")


// Show RSI and EMA crosses with arrows and RSI Color (tweaked Connors RSI)
// Improves strategy setting ease by showing where EMA 5 crosses EMA 10 from above to confirm overbought conditions or trend reversals
// This shows where you should enter shorts or exit longs

// Tweaked Connors RSI Calculation
connor_ob = 80
connor_os = 20
ma3 = sma(close,3)
ma20 = sma(close, 20)
ma50 = sma(close, 50)
erection = ((((close[1]-close[2])/close[2]) + ((close[0]-close[1])/close[1]))/2)*100

// Buy Sell Zones using tweaked Connors RSI (RSI values of 80 and 20 for Crypto as well as ma3, ma20, and ma50 are the tweaks)
RSI_SELL = ma20 > ma50 and open > ma3 and RSI >= connor_ob and erection <=4 and window()
RSI_BUY = ma20 < ma50 and ma3 > close and RSI <= connor_os and window()

// Color Definition
col = useCRSI ? (close > ma20 and close < ma3 and RSI <= connor_os ? color.lime : close < ma20 and close > ma3 and RSI <= connor_ob ? color.red : color.yellow ) : color.yellow

// Plot colored RSI Line
plot(RSI, title="RSI", linewidth=3, color=col)


// Shape Plots
plotshape(showRSIBS ? RSI_BUY: na, title = "RSI Buy", style = shape.arrowup, text = "RSI Buy", location = location.bottom, color=color.green, textcolor=color.green, size=size.small)
plotshape(showRSIBS ? RSI_SELL: na, title = "RSI Sell", style = shape.arrowup, text = "RSI Sell", location = location.bottom, color=color.red, textcolor=color.red, size=size.small)


// MACD as another complement to RSI strategy
[macdLine, signalLine, _] = macd(close, fastLength, slowLength, length)

bartrendcolor = macdLine > signalLine and k > 50 and RSI > 50 ? color.teal : macdLine < signalLine and k < 50 and RSI < 50 ? color.maroon : macdLine < signalLine ? color.yellow : color.gray
barcolor(color = color_bars ? bartrendcolor : na)


MACDBuy = crossover(macdLine, signalLine) and macdLine<0 and RSI<RSI_low and window()
MACDSell = crossunder(macdLine, signalLine) and macdLine>0 and RSI>RSI_high and window()

plotshape(showMACD ? MACDBuy: na, title = "MACD Buy", style = shape.arrowup, text = "MACD Buy", color=color.green, textcolor=color.green, size=size.small)
plotshape(showMACD ? MACDSell: na, title = "MACD Sell", style = shape.arrowdown, text = "MACD Sell", color=color.red, textcolor=color.red, size=size.small)
bgcolor(showMACD ? (MACDBuy ? color.teal : MACDSell ? color.maroon : na) : na, title ="MACD Signals", transp=50)


// -------------------------------- Entry and Exit Logic ------------------------------------


// Entry Logic
XRSI_OB = crossunder(RSI, overbought) and window()
RSI_OB = crossover(RSI, overbought) and window()
XRSI_OS = crossover(RSI, oversold) and window()
RSI_OS = crossunder(RSI, oversold) and window()


// Strategy Entry and Exit with built in Risk Management
GoLong = strat_val > -1 ? (useXRSI ? XRSI_OS : useMACD ? MACDBuy : useCRSI ? RSI_BUY : RSI_OS) : false

GoShort = strat_val < 1 ? (useXRSI ? XRSI_OB : useMACD ? MACDSell : useCRSI ? RSI_SELL : RSI_OB) : false

convert_percent_to_points(percent) =>
    strategy.position_size != 0 ? round(percent * strategy.position_avg_price / syminfo.mintick) : float(na)
    
setup_percent(percent) =>
    convert_percent_to_points(percent)


if (GoLong)
    strategy.entry("LONG", strategy.long)
    strategy.exit(id="Exit Long", from_entry = "LONG", loss=setup_percent(stoploss), profit=setup_percent(TargetProfit))

CloseLong = strategy.position_size > 0 ? (useXRSI ? XRSI_OB : useMACD ? MACDSell : useCRSI ? RSI_SELL : RSI_OB) : false

if(CloseLong)
    strategy.close("LONG")



if (GoShort) 
    strategy.entry("SHORT", strategy.short)
    strategy.exit(id="Exit Short", from_entry = "SHORT", loss=setup_percent(stoploss), profit=setup_percent(TargetProfit))
        
CloseShort = strat_val < 1 and strategy.position_size < 0 ? (useXRSI ? XRSI_OS : useMACD ? MACDBuy : useCRSI ? RSI_BUY : RSI_OS) : false

if(CloseShort)
    strategy.close("SHORT")