本文主要阐述了一个基于Stochastic RSI指标的动量震荡交易策略。该策略采用了较短周期(如30分钟)的技术指标,根据Stochastic RSI是否进入超买超卖区域来进行交易决策。相比其他动量策略,该策略同时结合了RSI和Stochastic两个指标的优点,可以更加准确地捕捉市场的短期震荡。
该策略的核心指标是Stochastic RSI。Stochastic RSI指标的计算公式是:
Stochastic RSI = (RSI - 低点RSI) / (高点RSI - 低点RSI) * 100
其中RSI采用 lengthRSI参数(默认12)计算,Stochastic RSI采用 lengthStoch参数(默认12)计算。
当Stochastic RSI高于紫色填充区域时为超买区,这时做空;当Stochastic RSI低于紫色填充区域时为超卖区域,这时做多。
此外,策略还设置了均线过滤条件。只有当快速EMA高于慢速EMA时,才能开仓做多;只有当快速EMA低于慢速EMA时,才能开仓做空。这样可以避免逆势交易。
相比单一的RSI策略,该策略结合了Stochastic指标,可以更加清晰地识别超买超卖区域,从而提高信号的可靠性。
相比单一的Stochastic策略,该策略采用了RSI作为Stochastic的输入数据源,可以过滤掉部分噪音,使得信号更加可靠。
设置了均线过滤条件,可以有效避免逆势建仓,从而减少不必要的损失。
设置了持仓时间延迟,可以避免被假突破停止出。
该策略主要采用了短周期指标,所以只适合短线操作,长线效果可能不佳。
Stochastic RSI指标本身会产生一定的延迟,可能会错过短期价格剧烈变动后的信号。
在震荡行情中,Stochastic RSI指标会产生多次超买超卖区域穿越,可能会过度交易从而增加交易成本。
可以测试不同参数组合,进一步优化Stochastic RSI的长度、K值和D值。
可以测试不同的RSI长度参数,寻找更合适的RSI周期长度。
可以尝试结合其他指标进行组合,进一步提高信号的准确性。比如MACD, Bollinger Bands等。
可以测试不同的持仓时间延迟参数,找到更合适的出场时间。
本文详细介绍了基于Stochastic RSI指标的动量策略的构建原理、优势、风险和优化思路。相比单一指标策略,该策略同时利用了RSI和Stochastic的优势,可以更加清晰和可靠地识别市场的短期超买超卖现象,从而进行反转交易。通过参数优化和指标组合,有望进一步提升策略效果。
/*backtest
start: 2023-11-25 00:00:00
end: 2023-12-25 00:00:00
period: 1h
basePeriod: 15m
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/
// © Drun30 (Federico Magnani)
//@version=4
//STRATEGIA PRINCIPALE
capitaleIniziale=10000
var sizeordineInit= 50 // → % di capitale investita per ogni trade
var deltaSize = 25 // → delta% di capitale investito se trade precedente è stato in perdita
var sizeLimite = 100 //il trade non userà mai questa percentuale di capitale investito
var sizeordine = sizeordineInit
//Parametri ottimali 30 min
usiShort=false
usiLong=true
ipercomprato=85.29
ipervenduto=30.6
//
strategy("Momentum Strategy (V7.B.4)", initial_capital=capitaleIniziale, currency="USD", default_qty_type=strategy.percent_of_equity, commission_type=strategy.commission.percent, commission_value=0.1, slippage = 5, default_qty_value=sizeordineInit, overlay=false, pyramiding=0)
backtest = input(title="------------------------Backtest Period------------------------", defval = false)
start = timestamp(input(2020, "start year"), input(1, "start month"), input(1, "start day"), 00, 00)
end = timestamp(input(0, "end year"), input(0, "end month"), input(0, "end day"), 00, 00)
siamoindata=time > start?true:false
if end > 0
siamoindata:=time > start and time <= end?true:false
basicParameters = input(title="------------------------Basic Parameters------------------------", defval = false)
smoothK = input(3, minval=1)
smoothD = input(6, minval=1)
lengthRSI = input(12, minval=1)
src = input(close, title="RSI Source")
rsi1 = rsi(src, lengthRSI)
lengthStoch = input(12, minval=1)
k = ema(stoch(rsi1, rsi1, rsi1, lengthStoch), smoothK)
d = ema(k, smoothD)
altezzaipercomprato= input(ipercomprato, title="Overbought Height", minval=1, type=input.float)
altezzaipervenduto= input(ipervenduto, title="Oversold Height", minval=1,type=input.float)
BarsDelay = input(6,title="Bars delay",minval=0)
GambleSizing = input(true, title = "Gamble Sizing?",type=input.bool)
gambleAdd = input(deltaSize,title="Gamble Add (%)",minval=0,type=input.integer)
gambleLimit = input(sizeLimite,title="Gamble MAX (%)",minval=0,type=input.integer)
if GambleSizing and strategy.closedtrades[0]>strategy.closedtrades[1]
if strategy.losstrades[0]>strategy.losstrades[1] and sizeordine<gambleLimit
sizeordine:=sizeordine+gambleAdd
if strategy.wintrades[0]>strategy.wintrades[1]
sizeordine:=sizeordineInit
periodomediamobile_fast = input(1, title="Fast EMA length",minval=1)
periodomediamobile_slow = input(60, title="Slow EMA length",minval=1)
plot(k, color=color.blue)
plot(d, color=color.orange)
h0 = hline(altezzaipercomprato)
h1 = hline(altezzaipervenduto)
fill(h0, h1, color=color.purple, transp=80)
// n=input(Vicinanzadalcentro,title="Vicinanza dal centro",minval=0)
//sarebbe il livello di D in cui si acquista o si vende, maggiore è la vicinanza maggiore sarà la frequenza dei trades, SE 0 è DISABILITATO
// siamoinipervenduto= d<=altezzaipervenduto and d<=d[n] and d>d[1]?true:false //and d<d[3] and d>d[1]
// siamoinipercomprato= d>=altezzaipercomprato and d>=d[n] and d<d[1]?true:false //and d>d[3] and d<d[1]
goldencross = crossover(k,d)
deathcross = crossunder(k,d)
// METTI VARIABILE IN CUI AVVIENE CROSSOVER O CROSSUNDER
valoreoro = valuewhen(goldencross,d,0)
valoremorte = valuewhen(deathcross,d,0)
siamoinipervenduto = goldencross and valoreoro<=altezzaipervenduto?true:false//d<=altezzaipervenduto?true:false
siamoinipercomprato = deathcross and valoremorte>=altezzaipercomprato?true:false//d>=altezzaipercomprato?true:false
long_separator = input(title="------------------------LONG------------------------", defval = usiLong)
sl_long_inp = input(10, title="Stop Loss LONG %", type=input.float)
tp_long_inp = input(8, title="Take Profit LONG %",type=input.float)
stop_level_long = strategy.position_avg_price * (1 - (sl_long_inp/100)) //strategy.position_avg_price corrisponde al prezzo con cui si è aperta la posizione
take_level_long = strategy.position_avg_price * (1 + (tp_long_inp/100))
//BINANCE
JSON_long = 'OPEN LONG: PUT THE JSON HERE FOR THE API CALL'
JSON_chiusura = 'CLOSE POSITION: PUT THE JSON HERE FOR THE API CALL'
webhookLong = JSON_long
webhookClose= JSON_chiusura
trendFilterL = input(title="TREND FILTER LONG?", defval = true)
EMAfast=ema(close,periodomediamobile_fast)
EMAslow=ema(close,periodomediamobile_slow)
siamoinuptrend_ema=EMAfast>EMAslow?true:false //close>=EMAfast and EMAfast>EMAslow
siamoinuptrend = siamoinuptrend_ema
// CondizioneAperturaLong = siamoinipervenduto and siamoindata // and siamoinuptrend
CondizioneAperturaLong = siamoinipervenduto and siamoindata and long_separator
if trendFilterL
CondizioneAperturaLong := siamoinipervenduto and siamoindata and long_separator and siamoinuptrend
CondizioneChiusuraLong = siamoinipercomprato and siamoindata
possiamoAprireLong=0
if trendFilterL and siamoinuptrend
possiamoAprireLong:=5
plot(possiamoAprireLong,color=color.green)
sonPassateLeBarreG = barssince(CondizioneAperturaLong) == BarsDelay?true:false
sonPassateLeBarreD = barssince(CondizioneChiusuraLong) == BarsDelay?true:false
haiUnLongAncoraAperto = false
haiUnLongAncoraAperto := strategy.position_size>0?true:false
// Se l'ultimo valore della serie "CondizioneAperturaLong" è TRUE, allora hai un long ancora aperto
// Se l'ultimo valore della serie "CondizioneAperturaLong" è FALSE, allora:
// Se l'ultimo valore della serie "CondizioneChiusuraLong" è TRUE, allora NON hai un long ancora aperto
// Se l'ultimo valore della serie "CondizioneChiusuraLong" è FALSE, allora restituisce l'ultimo valore della serie "haiUnLongAncoraAperto"
haiUnLongAncoraAperto_float = if(haiUnLongAncoraAperto==true)
10
else
0
plot(haiUnLongAncoraAperto_float,color=color.red) //FInché la linea rossa si trova a livello "1" allora c'è un ordine long in corso
quantita = (sizeordine/100*(capitaleIniziale+strategy.netprofit))/valuewhen(haiUnLongAncoraAperto==false and CondizioneAperturaLong,close,0)
plot(sizeordine,color=color.purple, linewidth=3)
if strategy.position_size<=0 and CondizioneAperturaLong //and sonPassateLeBarreG and haiUnLongAncoraAperto==false strategy.opentrades==0
strategy.entry("Vamonos",strategy.long, alert_message=webhookLong, comment="OPEN LONG", qty=quantita)
if strategy.position_size>0 //and sonPassateLeBarreD // and CondizioneChiusuraLong
if siamoinuptrend == true and sonPassateLeBarreD
strategy.close("Vamonos", alert_message=webhookClose, comment="CLOSE LONG")
else if siamoinuptrend == false and CondizioneChiusuraLong
strategy.close("Vamonos", alert_message=webhookClose, comment="CLOSE LONG")
if strategy.position_size>0 and siamoindata
strategy.exit("Vamonos", stop=stop_level_long, limit=take_level_long, comment="CLOSE LONG (LIMIT/STOP)")
short_separator = input(title="------------------------SHORT------------------------", defval = usiShort)
sl_short_inp = input(20, title="Stop Loss SHORT %")
tp_short_inp = input(35, title="Take Profit SHORT %")
stop_level_short = strategy.position_avg_price * (1 + (sl_short_inp/100))
take_level_short= strategy.position_avg_price * (1 - (tp_short_inp/100))
// BINANCE
JSON_short = 'OPEN SHORT: PUT THE JSON HERE FOR THE API CALL'
webhookShort = JSON_short
trendFilterS = input(title="TREND FILTER SHORT?", defval = true)
siamoindowntrend_ema=EMAfast<EMAslow?true:false //close<=EMAfast and EMAfast<EMAslow
siamoindowntrend=siamoindowntrend_ema
CondizioneAperturaShort = short_separator and siamoinipercomprato and siamoindata
if trendFilterS
CondizioneAperturaShort:=short_separator and siamoinipercomprato and siamoindata and siamoindowntrend
CondizioneChiusuraShort = siamoinipervenduto and siamoindata
sonPassateLeBarreGs = barssince(CondizioneAperturaShort) == BarsDelay?true:false
sonPassateLeBarreDs = barssince(CondizioneChiusuraShort) == BarsDelay?true:false
haiUnoShortAncoraAperto = false
haiUnoShortAncoraAperto := strategy.position_size<0?true:false
haiUnoShortAncoraAperto_float = if(haiUnoShortAncoraAperto==true)
15
else
0
plot(haiUnoShortAncoraAperto_float,color=color.purple) //FInché la linea viola si trova a livello "2" allora c'è un ordine short in corso
if CondizioneAperturaShort and strategy.position_size>=0 //and haiUnoShortAncoraAperto==false
strategy.entry("Andale",strategy.short,alert_message=webhookShort, comment="OPEN SHORT")
if strategy.position_size<0 //and sonPassateLeBarreD // and CondizioneChiusuraLong
if siamoindowntrend == true and sonPassateLeBarreDs
strategy.close("Andale",alert_message=webhookClose, comment="CLOSE SHORT")
else if siamoindowntrend == false and CondizioneChiusuraShort
strategy.close("Andale",alert_message=webhookClose, comment="CLOSE SHORT")
if strategy.position_size<0 and siamoindata
strategy.exit("Andale", stop=stop_level_short, limit=take_level_short, comment="CLOSE SHORT (LIMIT/STOP)")