BOLL(布林線指標)策略
簡介
BOLL(布林線)指標是技術分析的常用工具之一,由美國股市分析家約翰•布林根據統計學中的標准差原理設計出來的一種非常簡單實用的技術分析指標。一般而言,價格的運動總是圍繞某一價值中樞(如均線、成本線等)在一定的范圍內變動,布林線指標正是在上述條件的基礎上,引進了“價格通道”的概念,通過計算一段時間價格的“標准差”,再由均線加/減某一倍數的標准差,求出價格的“信賴區間”。該指標在圖形上畫出三條線,其中上下兩條線可以分別看成是價格的壓力線和支撐線,而在兩條線之間還有一條價格平均線。一般來說,股價會運行在壓力線和支撐線所形成的通道中。
計算方法
(1) 中軌線 = SMA(N)
(2) 上軌線 = 中軌線 + 寬度倍數 * SD(N)
(3) 下軌線 = 中軌線 – 寬度倍數 * SD(N)
其中,SMA(N)代表N個時間周期的簡單移動平均;寬度倍數表示布林線上下軌偏離均線標准差的倍數,一般為2倍;SD(N)表示N個時間周期內的價格標准差。
由於采用了標准差的概念,使得布林通道的寬度會根據近期價格的波動而做出動態調整。波動小,布林通道會變窄;波動大,布林通道會變寬。
使用方法
布林線的使用方法有很多,可以單獨使用,也可以和其他指標結合在一起使用。 本文中我們采用布林線一種最簡單的使用方法。當價格自下而上突破上軌,即突破上方壓力線時,我們認為多方力量正在走強,一波上漲行情已經形成,買入信號產生;當價格自上而下跌破下軌,即跌破支撐線時,我們認為空方力量正在走強,一波下跌趨勢已經形成,賣出信號產生。
如上圖所示,在比特幣價格一路上漲,當價格向上突破了上軌,產生了買入信號。捕捉到了之后價格的大幅攀升,收益不斷增加。之后開始回落,當價格突破下軌的時候,產生賣出信號。提前離場,避免了后面更大的損失。
布林線還有另一種用法,當價格突破上軌時,是超買信號,應當賣出而不是買入;當價格跌破下軌時,是超賣信號,應當買入而不是賣出。這種做法與上一種完全反向。它的思路是認為價格會在上下軌內波動,高了就會跌下去,低了就會漲起來,用上下軌來判斷高與低。實盤操作見下圖:
在這種震盪的行情中,基本做到的高拋低吸。
一種是追漲殺跌,一種是高拋低吸,這兩種觀點雖然操作相反,但都有道理,只是適用於不同的行情。趨勢行情中,適合追漲殺跌;而震盪行情中,適合高拋低吸。
優點
布林線本身既包括了趨勢指標,也包括了震盪指標,能幫助我們快速的認清市場的走勢,是非常常用的技術指標。一般情況下,使用布林線操作的勝率要高於KDJ和RSI等指標。因為這些指標通常會在價格盤整的時候失去作用,產生很多錯誤信號。而布林線可以很好的幫我們尋找盤整階段,以及在盤整結束時及時入場。
回測
- 參數設置:
| 時間段 | 2014-06-01至2016-10-01 |
|---|---|
| 回測頻率(context.frequency) | 1d |
| 標准差倍數 | 2 |
| 回看時間窗口 | 14(天) |
通過回看14天來構造布林通道的上下軌。當價格突破上軌時,買入;當價格突破下軌時,賣出。對於標准差倍數的設置,如果比較大,就會比較難觸發到信號;如果比較小,又會信號頻發。所以這個倍數設置也比較講究。我們這里是以天為單位回測,而且用布林軌道作為突破的指標,追蹤大的趨勢。所以倍數不應設置太小,否則會產生很多干擾信號。
- 回測結果:
策略比較成功,在基准處於慢熊的過程中大部分時間處於空倉,而在基准反彈時也能成功加倉。當然,有些過於快速的暴跌還是很難避開,這也是做這種中長期趨勢的通病。
總結
布林線指標利用統計學原理,通過對過去一段時間價格的波動,來構造一個當前價格的信賴區間。通過對布林線上下軌以及布林通道的寬度的研究,可以幫助我們更好的認清市場。
代碼
# !/usr/bin/env python # -*- coding: utf-8 -*- # 策略代碼總共分為三大部分,1)PARAMS變量 2)initialize函數 3)handle_data函數 # 請根據指示閱讀。或者直接點擊運行回測按鈕,進行測試,查看策略效果。 # 策略名稱:BOLL指標策略 # 策略詳細介紹:https://wequant.io/study/strategy.boll.html # 關鍵詞:價格通道、價格突破。 # 方法: # 1)利用均值和標准差構建價格區間 # 2)以價格超越軌道作為突破信號,向上突破買入,向下突破賣出 import numpy as np import talib # 閱讀1,首次閱讀可跳過: # PARAMS用於設定程序參數,回測的起始時間、結束時間、滑點誤差、初始資金和持倉。 # 可以仿照格式修改,基本都能運行。如果想了解詳情請參考新手學堂的API文檔。 PARAMS = { "start_time": "2017-02-01 00:00:00", "end_time": "2017-08-01 00:00:00", "slippage": 0.003, "account_initial": {"huobi_cny_cash": 100000, "huobi_cny_btc": 0}, } # 閱讀2,遇到不明白的變量可以跳過,需要的時候回來查閱: # initialize函數是兩大核心函數之一(另一個是handle_data),用於初始化策略變量。 # 策略變量包含:必填變量,以及非必填(用戶自己方便使用)的變量 def initialize(context): # 設置回測頻率, 可選:"1m", "5m", "15m", "30m", "60m", "4h", "1d", "1w" context.frequency = "30m" # 設置回測基准, 比特幣:"huobi_cny_btc", 萊特幣:"huobi_cny_ltc", 以太坊:"huobi_cny_eth" context.benchmark = "huobi_cny_btc" # 設置回測標的, 比特幣:"huobi_cny_btc", 萊特幣:"huobi_cny_ltc", 以太坊:"huobi_cny_eth" context.security = "huobi_cny_btc" # 設置計算布林線的參數 # 布林線的長度(回看時間窗口為20個bar) context.user_data.period_window = 20 # 布林線的寬度(2倍標准差) context.user_data.standard_deviation_range = 2 context.user_data.bbands_opt_width_m = 60 # 閱讀3,策略核心邏輯: # handle_data函數定義了策略的執行邏輯,按照frequency生成的bar依次讀取並執行策略邏輯,直至程序結束。 # handle_data和bar的詳細說明,請參考新手學堂的解釋文檔。 def handle_data(context): # 獲取歷史數據 hist = context.data.get_price(context.security, count=context.user_data.period_window + context.user_data.bbands_opt_width_m + 1, frequency=context.frequency) if len(hist.index) < (context.user_data.period_window + context.user_data.bbands_opt_width_m + 1): context.log.warn("bar的數量不足, 等待下一根bar...") return # 獲取收盤價 prices = np.array(hist["close"]) # 初始化做多/做空信號 long_signal_triggered = False short_signal_triggered = False # 使用talib計算布林線的上中下三條線 upper, middle, lower = talib.BBANDS(prices, timeperiod=context.user_data.period_window, nbdevup=context.user_data.standard_deviation_range, nbdevdn=context.user_data.standard_deviation_range, matype=talib.MA_Type.SMA) # 獲取最新價格 current_price = context.data.get_current_price(context.security) # 生成交易信號 if current_price > upper[-1]: # 穿越上軌,買入信號 long_signal_triggered = True if current_price < lower[-1]: # 穿越下軌,賣出信號 short_signal_triggered = True context.log.info("當前 價格為:%s, 上軌為:%s, 下軌為: %s" % (current_price, upper[-1], lower[-1])) # 根據信號買入/賣出 if short_signal_triggered: context.log.info("價格穿越下軌,產生賣出信號") if context.account.huobi_cny_btc >= HUOBI_CNY_BTC_MIN_ORDER_QUANTITY: # 賣出信號,且不是空倉,則市價單全倉清空 context.log.info("正在賣出 %s" % context.security) context.log.info("賣出數量為 %s" % context.account.huobi_cny_btc) context.order.sell(context.security, quantity=str(context.account.huobi_cny_btc)) else: context.log.info("倉位不足,無法賣出") elif long_signal_triggered: context.log.info("價格穿越上軌,產生買入信號") if context.account.huobi_cny_cash >= HUOBI_CNY_BTC_MIN_ORDER_CASH_AMOUNT: # 買入信號,且持有現金,則市價單全倉買入 context.log.info("正在買入 %s" % context.security) context.log.info("下單金額為 %s 元" % context.account.huobi_cny_cash) context.order.buy(context.security, cash_amount=str(context.account.huobi_cny_cash)) else: context.log.info("現金不足,無法下單") else: context.log.info("無交易信號,進入下一根bar")
回測
30m

60m

4h

1d

