볼링거 밴드 브레이크아웃 전략 (Bollinger Bands Breakout Strategy) 은 암호화폐 거래에 최적화된 단기 트렌드 다음 전략이다. 잘 알려진 볼링거 밴드 지표를 핵심 신호 생성자로 활용하여 장기 및 단위 포지션을 모두 취할 수 있다. 포괄적인 리스크 관리 메커니즘으로 트렌딩 시장에 적합한 강력한 자동화 거래 시스템이다.
이 전략은 볼링거 밴드 매개 변수, 다양한 필터, 수익/손실 중지 설정 및 최대 내일 손실 문턱을 포함한 높은 수준의 구성성을 갖추고 있습니다. 이러한 적응력은 다양한 시장 체제에서 신뢰할 수있는 성능을 달성 할 수 있도록합니다.
이 전략은 중간 대역, 상위 대역 및 하위 대역을 계산하는 볼링거 밴드 지표 중심에 있습니다. 가격 평균과 변동성 제한에 대한 대리점으로 작용합니다. 상위 또는 하위 대역을 넘는 가격의 교차는 가격이 상위 대역 이상으로 넘어갈 때 긴, 하위 대역 아래에있을 때 짧은 입시 신호를 생성합니다.
또한 거짓 신호를 피하기 위해 여러 필터를 구현합니다.
트렌드 필터: 이동평균보다 길고 이동평균보다 짧습니다.
변동성 필터: 변동성이 증가할 때만 거래
방향 필터: 길게만, 짧게만 또는 양쪽 방향으로 설정할 수 있습니다.
변화율 필터: 이전 클로즈로부터 충분한 가격 움직임이 필요합니다.
날짜 필터: 역 테스트 시간 프레임 사양
출출은 이윤을 취하고 손실을 중단하고 손실을 제한하기 위해 후속 중단 메커니즘을 통해 처리됩니다. 최대 내일 손실 문턱은 매일 인출 보호의 또 다른 층을 제공합니다.
이 전략의 주요 장점은 다음과 같습니다.
기본 신호로 신뢰할 수 있는 볼링거 밴드 지표
사용자 정의 필터는 원치 않는 거래를 방지합니다.
종합적인 스톱 로스/프로프트 취득 설계
최대 내일 손실 보호
수익 잠재력 있는 트렌딩 시장에서 번성합니다.
이점에도 불구하고 몇 가지 위험이 남아 있습니다.
볼링거 밴드를 둘러싼 윙사브는 손실로 이어질 수 있습니다.
너무 딱딱한 필터는 범위에 묶인 시장에서 거래를 감소시킵니다.
격차는 선제적으로 포지션을 중단 할 수 있습니다
극단적 인 이동 은 완전히 피할 수 없다
완화에는 필터 조정, 수동 개입 및 조정된 정지 기능이 포함됩니다.
이 전략에 대한 가능한 최적화:
최적의 매개 변수 조합을 검색
적응적 최적화를 위한 기계 학습을 도입
더 나은 스톱 로스 방법을 연구하십시오. 예를 들어 변동성 스톱
감정적 인 행동을 안내 할 수 있습니다.
통계적 중재를 위해 상관된 도구를 사용
볼링거 밴드 브레이크아웃 전략 (Bollinger Bands Breakout Strategy) 은 단기 트렌드 트레이딩을 위한 시간 검증된 시스템이다. 볼링거 밴드 신호와 신중한 필터의 장점을 결합함으로써 잘못된 신호를 피하면서 트렌드에 대한 양질의 엔트리를 생성한다. 포괄적인 리스크 관리 메커니즘은 또한 유출을 효과적으로 포함한다. 지속적인 개선으로, 이 전략은 엄청난 자동화 거래 시스템으로 발전할 가능성이 있다.
/*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)