ボリンジャー・バンド・ブレークアウト戦略

作者: リン・ハーンチャオチャン開催日:2023年11月13日11時50分
タグ:

img

概要

この戦略は,ボリンジャーバンドの動的上下帯を利用し,価格が上部帯を超えるとロングで,価格が下部帯を下回ると閉じる.固定レベルを持つ伝統的なブレイクアウト戦略とは異なり,ボリンジャーバンドの帯は,歴史的な変動に基づいて動的に変化し,過剰購入および過剰販売状態を識別するのがよりよい.

戦略の論理

この戦略は,ブレークアウトを特定するために主にボリンジャーバンド指標に依存しています.ボリンジャーバンドは3つのラインで構成されています.

  1. 中間線: n 期間の移動平均線
  2. 上部帯: 中間線 + k * n 期標準偏差
  3. 下帯: 中間線 - k * n期標準偏差

価格が上位帯を超えると,市場は買い過ぎとみなされ,ロングポジションが開始される.価格が下位帯を下回ると,市場は売り過ぎであり,ポジションは閉鎖されるべきである.

この戦略は,ボリンジャー・バンドのパラメータをカスタマイズすることができます.移動平均期 n と標準偏差倍数 k. 標準値は移動平均期 20 と標準偏差倍数 2 です.

ストラテジーは,取引日ごとに閉じる価格が上位帯を下回るかどうかをチェックする.そうなった場合,翌日の開業時にロング信号が起動する.ロングになった場合,ストラテジーは,価格がリアルタイムで下位帯を下回るかどうかを監視し,そうなった場合,ポジションを閉じる.

この戦略には,移動平均線よりも価格が高くなる場合にのみ購入信号を生成する移動平均フィルターも組み込まれています.移動平均線は,エントリータイミングをよりよく制御するために,現在のまたはより高いタイムフレームに設定できます.

ストップ・ロスは2つ用意されています. 固定パーセントストップ・ロスは下帯に追いかける. 後者は利益の実行により多くの余地を与えます.

戦略 の 利点

  • 過剰購入/過剰販売レベルを判断するためにボリンジャー帯を使用する
  • 移動平均フィルターは,トレンドに反する取引を避ける.
  • Bollinger Bands パラメータは異なる期間に適しています.
  • ストップ・ロスの2つの方法の選択
  • バックテストはパラメータの最適化とサンプル外検証を可能にします

戦略 の リスク

  • ボリンジャー・バンドは,過買い/過売りを完全に判断できない.
  • 移動平均フィルターは,より速いブレイクアウトを見逃す可能性があります
  • 固定ストップ・ロスは保守的すぎるし,トレーリング・ストップは攻撃的すぎる
  • パラメーターは,異なる製品と時間枠に最適化する必要があります
  • 損失の大きさを制限できず,マネーマネジメントを考える必要がある

オプティマイゼーションの方向性

  • 異なる移動平均パラメータの組み合わせをテストする
  • 異なるボリンジャー帯のパラメータを試してください.
  • 固定パーセントストップ損失と後続低帯を比較する
  • トレード損失制限にマネーマネジメントモジュールを追加
  • Bollinger Bands 信号の確認のために他の指標を組み込む

結論

この戦略は,ボリンジャーバンドの動的帯を使用して過買い/過売り条件を特定し,移動平均フィルターを参照し,資本を保護するためにストップを使用する.伝統的な固定レベルブレイクアウトと比較して,市場変動により順応する.さらなるパラメータ最適化とリスク制御により,戦略はより高い安定性とリターンを達成することができる.全体的に,ボリンジャーバンドの動的性質を利用することにより,戦略はブレイクアウト戦略の強みを把握し,ライブ取引と長期的最適化に価値がある.


/*backtest
start: 2022-11-06 00:00:00
end: 2023-11-12 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version=5

// Revision:        1
// Author:          @millerrh
// Strategy:  
//      Entry: Buy when price breaks out of upper Bollinger Band
//      Exit: Trail a stop with the lower Bollinger Band 
// Conditions/Variables:
//    1. Can add a filter to only take setups that are above a user-defined moving average on current timeframe and/or longer timeframe (helps avoid trading counter trend) 
//    2. Manually configure which dates to back test
//    3. User-Configurable Bollinger Band Settings
//    4. Optionally use a tighter initial stop level.  Once Bollinger Band catches up, trail with lower Bollinger Band to give more breathing room.

// strategy('Donchian Breakout', overlay=true, initial_capital=100000, currency='USD', default_qty_type=strategy.percent_of_equity, calc_on_every_tick = true,
//   default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.1)

strategy('Bollinger Breakout', overlay=true, initial_capital=100000, currency='USD', default_qty_type=strategy.percent_of_equity,
  default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.0, calc_on_order_fills=true)

// === BACKTEST RANGE ===
Start = input(defval = timestamp("01 Jan 2019 06:00 +0000"), title = "Backtest Start Date", group = "backtest window")
Finish = input(defval = timestamp("01 Jan 2100 00:00 +0000"), title = "Backtest End Date", group = "backtest window")

// == INPUTS ==
// Bollinger Band Inputs
bbLength = input.int(20, minval=1, group = "Bollinger Band Settings", title="Bollinger Band Length",
  tooltip = "Bollinger Band moving average length.")
bbMultTop = input.float(2.0, minval=0.001, maxval=50, title="Standard Deviation (Top)")
bbMultBot = input.float(2.0, minval=0.001, maxval=50, title="Standard Deviation (Bottom)")

useTightStop = input.bool(title='Use Fixed Percentage for Initial Stop?', defval=false, group = "order entry",
  tooltip = "'Keep your losers small and let winners run' is the saying.  This will allow you to use a tight initial stop
  until the lower Bollinger Band catches up.")
percStop = input.int(title="Stop", defval=8, group = "order entry", inline = "perc")
trigInput = input.string(title='Execute Trades On...', defval='Wick', options=['Wick', 'Close'], group = "order entry",
  tooltip = "Useful for comparing standing stop orders at the Bollinger Band boundary (executing on the wick) vs. waiting for candle closes prior to taking action")

// Moving Average Filtering Inputs
useMaFilter = input.bool(title='Use Moving Average for Filtering (Current Timeframe)?', defval=false, group = "moving average filtering",
  tooltip = "Signals will be ignored when price is under this moving average.  The intent is to keep you out of bear periods and only buying when 
             price is showing strength.")
maType = input.string(defval='SMA', options=['EMA', 'SMA'], title='MA Type For Filtering', group = "moving average filtering")
maLength = input.int(defval=50, title="Moving Average:    Length", minval=1, group = "moving average filtering", inline = "1ma")
ma1Color = input.color(color.new(color.green, 50), title = " Color", group = "moving average filtering", inline = "1ma")
useMaFilter2 = input.bool(title='Use Moving Average for Filtering (High Timeframe)?', defval=false, group = "moving average filtering")
tfSet = input.timeframe(defval="D", title="Timeframe of Moving Average", group = "moving average filtering",
  tooltip = "Allows you to set a different time frame for a moving average filter.  Trades will be ignored when price is under this moving average.
  The idea is to keep your eye on the larger moves in the market and stay on the right side of the longer term trends and help you be pickier about 
  the stocks you trade.")
ma2Type = input.string(defval='SMA', options=['EMA', 'SMA'], title='MA Type For Filtering', group = "moving average filtering")
ma2Length = input.int(defval=50, title="Moving Average:    Length", minval=1, group = "moving average filtering", inline = "2ma")
ma2Color = input.color(color.new(color.white, 50), title = " Color", group = "moving average filtering", inline = "2ma")


// === THE BOLLINGER BAND ===
// Logic
bbBasis = ta.sma(close, bbLength)
bbUpper = bbBasis + bbMultTop * ta.stdev(close, bbLength)
bbLower = bbBasis - bbMultBot * ta.stdev(close, bbLength)

// Plotting
plot(bbBasis, "Basis", color=color.new(color.white, 50))
p1 = plot(bbUpper, color=color.new(color.blue, 50), linewidth=1, title='Upper Bollinger Band')
p2 = plot(bbLower, color=color.new(color.blue, 50), linewidth=1, title='Lower Bollinger Band')
fill(p1, p2, title = "Background", color=color.rgb(33, 150, 243, 95))

// == FILTERING LOGIC ==
// Declare function to be able to swap out EMA/SMA
ma(maType, src, length) =>
    maType == 'EMA' ? ta.ema(src, length) : ta.sma(src, length)  //Ternary Operator (if maType equals EMA, then do ema calc, else do sma calc)
maFilter = ma(maType, close, maLength)
maFilter2 = request.security(syminfo.tickerid, tfSet, ma(ma2Type, close, ma2Length))

// Plotting
plot(useMaFilter ? maFilter : na, title='Trend Filter MA - CTF', color=ma1Color, linewidth=2, style=plot.style_line)
plot(useMaFilter2 ? maFilter2 : na, title='Trend Filter MA - HTF', color=ma2Color, linewidth=2, style=plot.style_line)


// == ENTRY AND EXIT CRITERIA ==
// Trigger stop based on candle close or High/Low (i.e. Wick)
trigResistance = trigInput == 'Close' ? close : trigInput == 'Wick' ? high : na
trigSupport = trigInput == 'Close' ? close : trigInput == 'Wick' ? low : na
buySignal = trigResistance >= bbUpper 

buyConditions = (useMaFilter ? bbUpper > maFilter : true) and
  (useMaFilter2 ? bbUpper > maFilter2 : true) 
  
// == STOP AND PRICE LEVELS ==
// Configure initial stop level
inPosition = strategy.position_size > 0
stopLevel = strategy.position_avg_price - (strategy.position_avg_price * percStop/100)
posStop = stopLevel > bbLower ? stopLevel : bbLower


// Check if using stop vs. not
stop = useTightStop ? posStop : bbLower
plot(inPosition ? stop : na, style=plot.style_linebr, color=color.new(color.red, 40), linewidth = 1, title = "Stop Levels", trackprice=false)

sellSignal = trigSupport <= stop

// == STRATEGY ENTRIES & EXITS ==
// This string of code enters and exits at the candle close
if trigInput == 'Close'
    strategy.entry('Long', strategy.long, when=buyConditions and buySignal)
    strategy.close('Long', when=sellSignal)

// This string of code enters and exits at the wick (i.e. with pre-set stops)
if trigInput == 'Wick'
    strategy.entry('Long', strategy.long, stop=bbUpper, when=buyConditions)
    strategy.exit('Exit Long', from_entry='Long', stop=stop)
strategy.cancel('Long',when= not(buyConditions)) // Resets stop level once buyConditions aren't true anymore



もっと