Bollinger Bands Breakout Strategy adalah strategi tren jangka pendek yang dioptimalkan untuk perdagangan kripto. Ini memanfaatkan indikator Bollinger Bands yang mapan sebagai generator sinyal inti dan mampu mengambil posisi panjang dan pendek. Dengan mekanisme manajemen risiko yang komprehensif, ini adalah sistem perdagangan otomatis yang kuat yang cocok untuk pasar tren.
Strategi ini memiliki tingkat konfigurasi yang tinggi, termasuk parameter Bollinger Bands, berbagai filter, pengaturan take profit/stop loss dan ambang kerugian intraday maksimum.
Strategi ini berpusat di sekitar indikator Bollinger Bands, yang menghitung band tengah, band atas dan band bawah yang berfungsi sebagai proxy untuk rata-rata harga dan batas volatilitas.
Selain itu, beberapa filter diimplementasikan untuk menghindari sinyal palsu:
Trend Filter: panjang di atas rata-rata bergerak, pendek di bawah rata-rata bergerak
Volatility Filter: hanya diperdagangkan ketika volatilitas meningkat
Filter arah: dapat dikonfigurasi untuk arah panjang saja, pendek saja atau kedua arah
Rate of Change Filter: pergerakan harga yang cukup dari penutupan sebelumnya diperlukan
Filter tanggal: untuk spesifikasi kerangka waktu backtesting
Keluar ditangani melalui mengambil keuntungan, stop loss dan trailing stop mekanisme untuk mengunci keuntungan dan membatasi kerugian.
Keuntungan utama dari strategi ini meliputi:
Indikator Bollinger Bands yang dapat diandalkan sebagai sinyal inti
Filter yang dapat disesuaikan mencegah perdagangan yang tidak diinginkan
Desain stop loss/take profit yang komprehensif
Perlindungan kerugian intraday maksimum terhadap penarikan ekstrim
Berkembangbiak di pasar tren dengan potensi keuntungan
Meskipun ada manfaatnya, tetap ada beberapa risiko:
Whipsaws di sekitar Bollinger Bands dapat menyebabkan kerugian
Filter yang terlalu kaku mengurangi perdagangan di pasar yang terikat rentang
Celah dapat menghentikan posisi secara preventif
Pergeseran ekstrem tidak bisa dihindari sepenuhnya
Pengurangan termasuk penyesuaian filter, intervensi manual dan penghentian tweaked.
Optimasi yang mungkin untuk strategi ini:
Mencari kombinasi parameter yang optimal
Memperkenalkan pembelajaran mesin untuk optimasi adaptif
Penelitian metode stop loss yang lebih baik misalnya volatility stops
Masukkan sentimen untuk memandu tindakan discretionary
Menggunakan instrumen korelasi untuk arbitrase statistik
Bollinger Bands Breakout Strategy adalah sistem yang telah diuji waktu untuk perdagangan tren jangka pendek. Dengan menggabungkan keunggulan sinyal Bollinger Bands dan filter yang bijaksana, ia menghasilkan entri berkualitas untuk tren sambil menghindari sinyal palsu. Mekanisme manajemen risiko yang komprehensif juga mengandung penarikan secara efektif. Dengan perbaikan terus-menerus, strategi ini memiliki potensi untuk menjadi sistem perdagangan otomatis yang tangguh.
/*backtest start: 2022-11-22 00:00:00 end: 2023-11-04 05:20:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ //@version=5 strategy("Bollinger Bands - Breakout Strategy",overlay=true ) // Define the length of the Bollinger Bands bbLengthInput = input.int (15,title="Length", group="Bollinger Bands", inline="BB") bbDevInput = input.float (2.0,title="StdDev", group="Bollinger Bands", inline="BB") // Define the settings for the Trend Filter trendFilterInput = input.bool(false, title="Above/Below", group = "Trend Filter", inline="Trend") trendFilterPeriodInput = input(223,title="", group = "Trend Filter", inline="Trend") trendFilterType = input.string (title="", defval="EMA",options=["EMA","SMA","RMA", "WMA"], group = "Trend Filter", inline="Trend") volatilityFilterInput = input.bool(true,title="StdDev", group = "Volatility Filter", inline="Vol") volatilityFilterStDevLength = input(15,title="",group = "Volatility Filter", inline="Vol") volatilityStDevMaLength = input(15,title=">MA",group = "Volatility Filter", inline="Vol") // ROC Filter // f_security function by LucF for PineCoders available here: https://www.tradingview.com/script/cyPWY96u-How-to-avoid-repainting-when-using-security-PineCoders-FAQ/ f_security(_sym, _res, _src, _rep) => request.security(_sym, _res, _src[not _rep and barstate.isrealtime ? 1 : 0])[_rep or barstate.isrealtime ? 0 : 1] high_daily = f_security(syminfo.tickerid, "D", high, false) roc_enable = input.bool(false, "", group="ROC Filter from CloseD", inline="roc") roc_threshold = input.float(1, "Treshold", step=0.5, group="ROC Filter from CloseD", inline="roc") closed = f_security(syminfo.tickerid,"1D",close, false) roc_filter= roc_enable ? (close-closed)/closed*100 > roc_threshold : true // Trade Direction Filter // tradeDirectionInput = input.string("Auto",options=["Auto", "Long&Short","Long Only", "Short Only"], title="Trade", group="Direction Filter", tooltip="Auto: if a PERP is detected (in the symbol description), trade long and short\n Otherwise as per user-input") // tradeDirection = switch tradeDirectionInput // "Auto" => str.contains(str.lower(syminfo.description), "perp") or str.contains(str.lower(syminfo.description), ".p") ? strategy.direction.all : strategy.direction.long // "Long&Short" => strategy.direction.all // "Long Only" => strategy.direction.long // "Short Only" => strategy.direction.short // => strategy.direction.all // strategy.risk.allow_entry_in(tradeDirection) // Calculate and plot the Bollinger Bands [bbMiddle, bbUpper, bbLower] = ta.bb (close, bbLengthInput, bbDevInput) plot(bbMiddle, "Basis", color=color.orange) bbUpperPlot = plot(bbUpper, "Upper", color=color.blue) bbLowerrPlot = plot(bbLower, "Lower", color=color.blue) fill(bbUpperPlot, bbLowerrPlot, title = "Background", color=color.new(color.blue, 95)) // Calculate and view Trend Filter float tradeConditionMa = switch trendFilterType "EMA" => ta.ema(close, trendFilterPeriodInput) "SMA" => ta.sma(close, trendFilterPeriodInput) "RMA" => ta.rma(close, trendFilterPeriodInput) "WMA" => ta.wma(close, trendFilterPeriodInput) // Default used when the three first cases do not match. => ta.wma(close, trendFilterPeriodInput) trendConditionLong = trendFilterInput ? close > tradeConditionMa : true trendConditionShort = trendFilterInput ? close < tradeConditionMa : true plot(trendFilterInput ? tradeConditionMa : na, color=color.yellow) // Calculate and view Volatility Filter stdDevClose = ta.stdev(close,volatilityFilterStDevLength) volatilityCondition = volatilityFilterInput ? stdDevClose > ta.sma(stdDevClose,volatilityStDevMaLength) : true bbLowerCrossUnder = ta.crossunder(close, bbLower) bbUpperCrossOver = ta.crossover(close, bbUpper) bgcolor(volatilityCondition ? na : color.new(color.red, 95)) // Date Filter start = input(timestamp("2017-01-01"), "Start", group="Date Filter") finish = input(timestamp("2050-01-01"), "End", group="Date Filter") date_filter = true // Entry and Exit Conditions entryLongCondition = bbUpperCrossOver and trendConditionLong and volatilityCondition and date_filter and roc_filter entryShortCondition = bbLowerCrossUnder and trendConditionShort and volatilityCondition and date_filter and roc_filter exitLongCondition = bbLowerCrossUnder exitShortCondition = bbUpperCrossOver // Orders if entryLongCondition strategy.entry("EL", strategy.long) if entryShortCondition strategy.entry("ES", strategy.short) if exitLongCondition strategy.close("EL") if exitShortCondition strategy.close("ES") // Long SL/TP/TS xl_ts_percent = input.float(2,step=0.5, title= "TS", group="Exit Long", inline="LTS", tooltip="Trailing Treshold %") xl_to_percent = input.float(0.5, step=0.5, title= "TO", group="Exit Long", inline="LTS", tooltip="Trailing Offset %") xl_ts_tick = xl_ts_percent * close/syminfo.mintick/100 xl_to_tick = xl_to_percent * close/syminfo.mintick/100 xl_sl_percent = input.float (2, step=0.5, title="SL",group="Exit Long", inline="LSLTP") xl_tp_percent = input.float(9, step=0.5, title="TP",group="Exit Long", inline="LSLTP") xl_sl_price = strategy.position_avg_price * (1-xl_sl_percent/100) xl_tp_price = strategy.position_avg_price * (1+xl_tp_percent/100) strategy.exit("XL+SL/TP", "EL", stop=xl_sl_price, limit=xl_tp_price, trail_points=xl_ts_tick, trail_offset=xl_to_tick,comment_loss= "XL-SL", comment_profit = "XL-TP",comment_trailing = "XL-TS") // Short SL/TP/TS xs_ts_percent = input.float(2,step=0.5, title= "TS",group="Exit Short", inline ="STS", tooltip="Trailing Treshold %") xs_to_percent = input.float(0.5, step=0.5, title= "TO",group="Exit Short", inline ="STS", tooltip="Trailing Offset %") xs_ts_tick = xs_ts_percent * close/syminfo.mintick/100 xs_to_tick = xs_to_percent * close/syminfo.mintick/100 xs_sl_percent = input.float (2, step=0.5, title="SL",group="Exit Short", inline="ESSLTP", tooltip="Stop Loss %") xs_tp_percent = input.float(9, step=0.5, title="TP",group="Exit Short", inline="ESSLTP", tooltip="Take Profit %") xs_sl_price = strategy.position_avg_price * (1+xs_sl_percent/100) xs_tp_price = strategy.position_avg_price * (1-xs_tp_percent/100) strategy.exit("XS+SL/TP", "ES", stop=xs_sl_price, limit=xs_tp_price, trail_points=xs_ts_tick, trail_offset=xs_to_tick,comment_loss= "XS-SL", comment_profit = "XS-TP",comment_trailing = "XS-TS") max_intraday_loss = input.int(10, title="Max Intraday Loss (Percent)", group="Risk Management") //strategy.risk.max_intraday_loss(max_intraday_loss, strategy.percent_of_equity) // Monthly Returns table, modified from QuantNomad. Please put calc_on_every_tick = true to plot it. monthly_table(int results_prec, bool results_dark) => new_month = month(time) != month(time[1]) new_year = year(time) != year(time[1]) eq = strategy.equity bar_pnl = eq / eq[1] - 1 cur_month_pnl = 0.0 cur_year_pnl = 0.0 // Current Monthly P&L cur_month_pnl := new_month ? 0.0 : (1 + cur_month_pnl[1]) * (1 + bar_pnl) - 1 // Current Yearly P&L cur_year_pnl := new_year ? 0.0 : (1 + cur_year_pnl[1]) * (1 + bar_pnl) - 1 // Arrays to store Yearly and Monthly P&Ls var month_pnl = array.new_float(0) var month_time = array.new_int(0) var year_pnl = array.new_float(0) var year_time = array.new_int(0) last_computed = false if (not na(cur_month_pnl[1]) and (new_month or barstate.islast)) if (last_computed[1]) array.pop(month_pnl) array.pop(month_time) array.push(month_pnl , cur_month_pnl[1]) array.push(month_time, time[1]) if (not na(cur_year_pnl[1]) and (new_year or barstate.islast)) if (last_computed[1]) array.pop(year_pnl) array.pop(year_time) array.push(year_pnl , cur_year_pnl[1]) array.push(year_time, time[1]) last_computed := barstate.islast ? true : nz(last_computed[1]) // Monthly P&L Table var monthly_table = table(na) cell_hr_bg_color = results_dark ? #0F0F0F : #F5F5F5 cell_hr_text_color = results_dark ? #D3D3D3 : #555555 cell_border_color = results_dark ? #000000 : #FFFFFF // ell_hr_bg_color = results_dark ? #0F0F0F : #F5F5F5 // cell_hr_text_color = results_dark ? #D3D3D3 : #555555 // cell_border_color = results_dark ? #000000 : #FFFFFF if (barstate.islast) monthly_table := table.new(position.bottom_right, columns = 14, rows = array.size(year_pnl) + 1, bgcolor=cell_hr_bg_color,border_width=1,border_color=cell_border_color) table.cell(monthly_table, 0, 0, syminfo.tickerid + " " + timeframe.period, text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 1, 0, "Jan", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 2, 0, "Feb", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 3, 0, "Mar", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 4, 0, "Apr", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 5, 0, "May", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 6, 0, "Jun", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 7, 0, "Jul", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 8, 0, "Aug", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 9, 0, "Sep", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 10, 0, "Oct", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 11, 0, "Nov", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 12, 0, "Dec", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) table.cell(monthly_table, 13, 0, "Year", text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) for yi = 0 to array.size(year_pnl) - 1 table.cell(monthly_table, 0, yi + 1, str.tostring(year(array.get(year_time, yi))), text_color=cell_hr_text_color, bgcolor=cell_hr_bg_color) y_color = array.get(year_pnl, yi) > 0 ? color.lime : array.get(year_pnl, yi) < 0 ? color.red : color.gray table.cell(monthly_table, 13, yi + 1, str.tostring(math.round(array.get(year_pnl, yi) * 100, results_prec)), bgcolor = y_color) for mi = 0 to array.size(month_time) - 1 m_row = year(array.get(month_time, mi)) - year(array.get(year_time, 0)) + 1 m_col = month(array.get(month_time, mi)) m_color = array.get(month_pnl, mi) > 0 ? color.lime : array.get(month_pnl, mi) < 0 ? color.red : color.gray table.cell(monthly_table, m_col, m_row, str.tostring(math.round(array.get(month_pnl, mi) * 100, results_prec)), bgcolor = m_color) results_prec = input(2, title = "Precision", group="Results Table") results_dark = input.bool(defval=true, title="Dark Mode", group="Results Table") monthly_table(results_prec, results_dark)