Động lực dao động vượt qua Bollinger Bands với chiến lược trung bình động

Tác giả:ChaoZhang, Ngày: 2023-12-19 11:34:46
Tags:

img

Tổng quan

Đây là một chiến lược giao dịch định lượng dựa trên các chỉ số Bollinger Bands và MACD. Nó kết hợp hai chỉ số kỹ thuật chính để xác định các cơ hội giao dịch, nhằm đạt tỷ lệ thắng cao hơn trong các thị trường xu hướng.

Chỉ số RSI có thể được cấu hình để hỗ trợ xác định mức mua quá mức và bán quá mức để tránh thêm tổn thất.

Chiến lược logic

Chiến lược chủ yếu bao gồm các Dải Bollinger và chỉ số MACD.

Bollinger Bands tính toán các dải trên và dưới dựa trên độ lệch chuẩn của giá. Bước phá lên của dải trên báo hiệu tình trạng mua quá mức, trong khi bước phá xuống của dải dưới báo hiệu tình trạng bán quá mức. Chiến lược này đi dài khi giá phá vỡ dải dưới và đóng vị trí khi nó phá vỡ dải trên.

Chỉ số MACD đánh giá động lực và hướng của giá. Chuỗi giữa trung bình di chuyển ngắn hạn trên trung bình di chuyển dài hạn là tín hiệu mua, trong khi chéo bên dưới là tín hiệu bán. MACD giúp lọc sự đột phá sai của Bollinger Bands trong chiến lược này.

Ngoài ra, chỉ số RSI có thể giúp xác định mức mua quá mức / bán quá mức. RSI thấp đại diện cho oversold và tăng tín hiệu mua, trong khi RSI cao đại diện cho mua quá mức và tăng tín hiệu bán.

Ưu điểm của Chiến lược

Chiến lược kết hợp các chỉ số Bollinger Bands, MACD và RSI có thể xác định hiệu quả xu hướng và biến động giá.

  1. Bollinger Bands nắm bắt xu hướng sau khi giá phá vỡ các dải
  2. MACD lọc các tín hiệu sai từ Bollinger Bands bằng cách đánh giá động lượng
  3. RSI tránh mua ở đỉnh bằng cách xác định mức mua quá mức / bán quá mức
  4. Tỷ lệ thắng cao hơn có thể đạt được thông qua tối ưu hóa tham số

Rủi ro của chiến lược

Ngoài ra còn có một số rủi ro cần lưu ý:

  1. Rủi ro dừng lỗ cao khi giá dao động mạnh
  2. Lợi nhuận giảm khi cài đặt tham số không đúng
  3. MACD có thể đánh giá sai khi xu hướng đảo ngược

Các biện pháp đối phó:

  1. Tỷ lệ dừng lỗ có thể được nới lỏng một cách thích hợp
  2. Kiểm tra hậu quả rộng rãi cần thiết để tìm các thông số tối ưu
  3. Có thể sử dụng nhiều chỉ số hơn để dự đoán sự đảo ngược xu hướng

Hướng dẫn tối ưu hóa

Các hướng chính để tối ưu hóa chiến lược bao gồm:

  1. Tối ưu hóa các thông số của Bollinger Bands cho nhiều chế độ thị trường hơn
  2. Tăng các chỉ số để cải thiện độ bền
  3. Sử dụng máy học để tự động tối ưu hóa các thông số
  4. Hiệu suất chiến lược thử nghiệm trên dữ liệu tần số cao
  5. Thêm mô-đun quản lý rủi ro vào giới hạn lỗ trên mỗi giao dịch

Kết luận

Nhìn chung, đây là một xu hướng điển hình sau chiến lược. Bằng cách kết hợp nhiều chỉ số kỹ thuật, nó cải thiện độ bền và có thể đạt được tỷ lệ thắng tốt khi tín hiệu chính xác. Tuy nhiên, rủi ro cần được theo dõi. Những cải tiến hơn nữa có thể được thực hiện thông qua tối ưu hóa và điều chỉnh liên tục.


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

//@version=4
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tedwardd

// This strategy is intended to help users of the 3commas.io platform backtest bot performance based on a Bollinger Strategy.
// It can also be used to signal a bot to open a deal by providing the Bot ID, email token and trading pair in the strategy settings screen.
// As currently written, this strategy uses a basic Bollinger Band strategy, recommening a deal start when the closing price crosses under the lower band.
// The thick red line plotted on the chart shows the average entry price of the current deal.

strategy("[v1.3laoowai]BNB_USDT_3m_3Commas_Bollinger_Strategy_by_tedwardd", overlay=true, default_qty_type=strategy.cash, default_qty_value=1000, initial_capital=900, currency="USD", commission_value=0.1)

// 3Commas Bot settinsg
bot_type                = input(title="Simple bot", defval="simple", options=["simple", "composite"])
bot_id                  = input(title="3Commas Bot ID", defval="")
email_token             = input(title="Bot Email Token", defval="")
base_order_size         = input(title="Base order size",minval=10, step=1, defval=10)
safety_order_size       = input(title="Safety order size", minval=15, step=1, defval=400)
volume_scale            = input(title="Safety Order Vol Scale (%)", minval=0.00, step=0.01, defval=1.83)
safety_step             = input(title="Safety Order Step Scale (%)", minval=0.00, step=0.1, defval=1.55)
safety_max              = input(title="Max Number of Safety Orders", minval=0, step=1, defval=2)
initial_deviation_input = input(title="Initial SO Deviation (%)", minval=0, step=0.01, defval=1.54) * 0.01
stoploss_input          = input(title="Long Stop Loss (%)", minval=0, step=1, defval=15) * 0.01
takeprofit_input        = input(title="Long Take Profit (%)", minval=0, step=1, defval=1.4) * 0.01

// USER INPUTS
sma_short_val           = input(title="Short MA Window", defval=21)
sma_long_val            = input(title="Long MA Window", defval=100)
ubOffset                = input(title="Upper Band Offset", defval=2.2, step=0.5)
lbOffset                = input(title="Lower Band Offset", defval=2.40, step=0.5)
cross                   = input(title="Entrry at Cross Over/Under Lower", defval="under", options=["over", "under"])

// Backtesting Date Ranges
startDate  = input(title="Start Date", defval=1, minval=1, maxval=31)
startMonth = input(title="Start Month", defval=1, minval=1, maxval=12)
startYear  = input(title="Start Year", defval=2016, minval=1800, maxval=2100)
endDate    = input(title="End Date", defval=31, minval=1, maxval=31)
endMonth   = input(title="End Month", defval=12, minval=1, maxval=12)
endYear    = input(title="End Year", defval=2022, minval=1800, maxval=2100)

// VARS
short_sma        = sma(close, sma_short_val)
long_sma         = sma(close, sma_long_val)
stdDev           = stdev(close, sma_short_val)
upperBand        = short_sma + (stdDev * ubOffset)
lowerBand        = short_sma - (stdDev * lbOffset)
stoploss_value   = strategy.position_avg_price * (1 - stoploss_input)
takeprofit_value = strategy.position_avg_price * (1 + takeprofit_input)
initial_dev_val  = strategy.position_avg_price * (1 - initial_deviation_input)
inDateRange      = true

initial_deviation = close < initial_dev_val

// Market Conditions
goodBuy    = cross=="over"?crossover(close, lowerBand):crossunder(close, lowerBand) // Buy when close crossing lower band
safety     = initial_deviation and (1-(close/strategy.position_avg_price))/.01 > strategy.opentrades-1 * safety_step and strategy.opentrades <= safety_max // SO when price deviates below SO threshold %
stoploss   = close <= stoploss_value // Stoploss condition - true if closing price for current bar drops below stoploss %
takeprofit = close >= takeprofit_value // Take profit condition - true if closing price for current bar is >= take profit percentage
goodSell = crossover(high, upperBand)

// goodSell is currently unused for any practical purpose. If you wish to try it, switch these two values. 
// Doing so will make sell suggestions at high crossover upper bollinger but it does not trigger the bot to sell as written but may affect backtest results

// Plot some lines
plot(short_sma, color=color.green)
plot(upperBand)
plot(lowerBand, color=color.yellow)
plot(strategy.position_avg_price, color=color.red, linewidth=3)


// Webhook message. Defaults to string. To signal 3c bot, fill in bot_id and email_token in user settings
var enter_msg = "Enter Position"
var exit_msg  = "Exit Position"
var close_all = "Exit Position"
if bot_id != "" and email_token != ""
    if bot_type == "composite"
        enter_msg := '{"message_type": "bot", "bot_id": ' + bot_id + ', "email_token": "' + email_token + '", "delay_seconds": 0, "pair": "' + syminfo.currency + "_" + syminfo.basecurrency + '"}'
    else
        enter_msg := '{"message_type": "bot", "bot_id": ' + bot_id + ',  "email_token": "' + email_token + '", "delay_seconds": 0}'
    if bot_type == "composite"
        exit_msg := '{"message_type": "bot", "bot_id": ' + bot_id + ', "email_token": "' + email_token + '", "delay_seconds": 0, "pair": "' + syminfo.currency + "_" + syminfo.basecurrency + '", "action": "close_at_market_price"}'
    else
        exit_msg := '{"message_type": "bot", "bot_id": ' + bot_id + ', "email_token": "' + email_token + '", "delay_seconds": 0, "action": "close_at_market_price"}'
    close_all := '{"message_type": "bot", "bot_id": ' + bot_id + ', "email_token": "' + email_token + '", "delay_seconds": 0, "action": "close_at_market_price_all"}'

actual_safety_size = float(safety_order_size) // Set safety order size to starting safety
if strategy.opentrades > 1 // If we have more than two open trades we need to start scaling the safety size by the volume_scale
    actual_safety_size := (strategy.position_size - base_order_size) * volume_scale // Remove base order from total position size and scale it for next safety order

// Momentum Strategy (BTC/USDT; 1h) - MACD (with source code) by Drun30

//@version=4
// Getting inputs
fast_length = input(title="Fast Length", type=input.integer, defval=23,group="MACD")
slow_length = input(title="Slow Length", type=input.integer, defval=16,group="MACD")
src = input(title="Source", type=input.source, defval=open,group="MACD")

signal_length = input(title="Signal Smoothing", type=input.integer, minval = 1, maxval = 50, defval = 9,group="MACD")
sma_source1 = input(title="Simple MA FAST (Oscillator)", defval="EMA", options=["HMA","DHMA","THMA","FHMA","WMA","DWMA","TWMA","FWMA","SMA","DSMA","TSMA","FSMA","EMA","DEMA","TEMA","FEMA","RMA","DRMA","TRMA","FRMA"],group="MACD")
sma_source2 = input(title="Simple MA SLOW (Oscillator)", defval="EMA", options=["HMA","DHMA","THMA","FHMA","WMA","DWMA","TWMA","FWMA","SMA","DSMA","TSMA","FSMA","EMA","DEMA","TEMA","FEMA","RMA","DRMA","TRMA","FRMA"],group="MACD")

sma_signal = input(title="Simple MA(Signal Line)",defval="EMA", options=["HMA","DHMA","THMA","FHMA","WMA","DWMA","TWMA","FWMA","SMA","DSMA","TSMA","FSMA","EMA","DEMA","TEMA","FEMA","RMA","DRMA","TRMA","FRMA"],group="MACD")
// Calculating
ma(source,length,type)=>
    type=="FEMA"?4*ema(source,length)-ema(ema(ema(ema(source,length),length),length),length):type=="FSMA"?4*sma(source,length)-sma(sma(sma(sma(source,length),length),length),length):type=="FWMA"?4*wma(source,length)-wma(wma(wma(wma(source,length),length),length),length):type=="FRMA"?4*rma(source,length)-rma(rma(rma(rma(source,length),length),length),length):type=="TEMA"?3*ema(source,length)-ema(ema(ema(source,length),length),length):type=="TSMA"?3*sma(source,length)-sma(sma(sma(source,length),length),length):type=="TWMA"?3*wma(source,length)-wma(wma(wma(source,length),length),length):type=="TRMA"?3*rma(source,length)-rma(rma(rma(source,length),length),length):type=="EMA"?ema(source,length):type=="SMA"?sma(source,length):type=="WMA"?wma(source,length):type=="RMA"?rma(source,length):type=="DEMA"?2*ema(source,length)-ema(ema(source,length),length):type=="DSMA"?2*sma(source,length)-sma(sma(source,length),length):type=="DWMA"?2*wma(source,length)-wma(wma(source,length),length):type=="DRMA"?2*rma(source,length)-rma(rma(source,length),length):type=="HMA"?hma(source,length):type=="DHMA"?2*hma(source,length)-hma(hma(source,length),length):type=="THMA"?3*hma(source,length)-hma(hma(hma(source,length),length),length):type=="FHMA"?4*hma(source,length)-hma(hma(hma(hma(source,length),length),length),length):ema(source,length)
fast_ma = ma(src,fast_length,sma_source1)  
slow_ma = ma(src,slow_length,sma_source2)
macd = fast_ma - slow_ma //Differenza tra la media mobile veloce e quella lenta 
signal = ma(macd,signal_length,sma_signal) //usa o la SMA oppure la EMA sulla differenza tra la media mobile veloce e lenta
hist = macd - signal //Differenza tra la differenza precedente e la media mobile della differenza

use_stress=input(true,title="Use stress on recent bars",group="Stress")
recent_stress=input(0.41,title="Stress on recent bars",group="Stress",step=0.01,minval=0.01,maxval=0.99)
level=input(6,title="Level of stress",group="Stress")
if use_stress 
    macd:=macd*(1/(1-recent_stress))
    if not na(macd[1])
        macd:=pow((macd*(recent_stress)),level)+(1-recent_stress*macd[1])

use_ma= input(true,title="Use moving average (MACD)?",group="Moving Average")
if use_ma
    macd:=ma(macd,input(36,title="Length",group="Moving Average"),input(title="Type MA",defval="THMA", options=["HMA","DHMA","THMA","FHMA","WMA","DWMA","TWMA","FWMA","SMA","DSMA","TSMA","FSMA","EMA","DEMA","TEMA","FEMA","RMA","DRMA","TRMA","FRMA"],group="Moving Average"))

use_linreg= input(true,title="Use linear regression (MACD)?",group="Linear Regression")
if use_linreg
    macd:=linreg(macd,input(10,title="Length",group="Linear Regression"),input(1,title="Offset",group="Linear Regression"))

//macd == linea blu (differenza tra media mobile veloce e media mobile lenta)
//signal == linea arancione (media mobile dell'macd)
//hist == istogramma (differenza tra macd e media mobile)

on_cross = input(false,title="Use cross macd and signal",group="Condition entry/exit")
on_minmax = input(true,title="Use min/max macd",group="Condition entry/exit")


aperturaLong = change(macd)>0//crossover(macd,signal)
aperturashort=not (change(macd)>0)//crossunder(macd,signal)

if on_cross
    on_minmax:=false
    aperturaLong := crossover(macd,signal)
    aperturashort := crossunder(macd,signal)
if on_minmax
    on_cross:=false
    aperturaLong := change(macd)>0//crossover(macd,signal)
    aperturashort:=change(macd)<0//crossunder(macd,signal)

rsiFilter = input(false,title="Use RSI filter?",group="RSI")
rsiTP = input(true,title="Use RSI Take Profit?",group="RSI")

len=input(22,title="RSI period",group="RSI")
srcr=input(close,title="RSI source",group="RSI")
rsi=rsi(srcr,len)
ovb=input(90,title="Overbought height",group="RSI") 
ovs=input(45,title="Oversold height",group="RSI")
okLong=rsi<ovb and change(macd)>0 and change(macd)[1]<=0
okShort=rsi>ovs and change(macd)<0 and change(macd)[1]>=0
if not rsiFilter
    okLong:=true
    okShort:=true
    
usiLong=input(true,title="Use long?")
usiShort=input(true,title="Use short?")

chiusuraShort=rsi<ovs or (aperturaLong)
chiusuraLong=rsi>ovb or (aperturashort)
if rsiTP
    aperturaLong := change(macd)>0 and change(macd)[1]<=0 and rsi<ovb//crossover(macd,signal)
    aperturashort:=change(macd)<0 and change(macd)[1]>=0 and rsi>ovs//crossunder(macd,signal)

if not rsiTP
    chiusuraShort:=okLong and aperturaLong
    chiusuraLong:=okShort and aperturashort
    
//if chiusuraShort 
//    strategy.close("SHORTISSIMO")
//if usiLong and strategy.position_size<=0 and okLong and aperturaLong
//    strategy.entry("LONGHISSIMO",true)
//if chiusuraLong 
//    strategy.close("LONGHISSIMO")
//if usiShort and strategy.position_size>=0 and okShort and aperturashort
//    strategy.entry("SHORTISSIMO",false)

// Strategy Actions
//Buy
if inDateRange and goodBuy
    strategy.entry("Good Buy", strategy.long, base_order_size, when = strategy.opentrades <= 0, alert_message=enter_msg)
if inDateRange and safety
    strategy.order("Good Buy", strategy.long, actual_safety_size, when = strategy.opentrades > 0, comment = "safety order", alert_message=enter_msg)

// Sell
if inDateRange and goodSell
    strategy.close_all(comment="Good sell point", alert_message=exit_msg)
if inDateRange and stoploss
    strategy.close_all(comment="Stoploss", alert_message=exit_msg)
//if inDateRange and takeprofit
//    strategy.close_all(comment="TP Target", alert_message=exit_msg)
if usiShort and strategy.position_size>=0 and okShort and aperturashort
    strategy.close_all(comment="SHORTISSIMO", alert_message=exit_msg)
//if chiusuraShort
//    strategy.close_all(comment="SHORTISSIMO1")

Thêm nữa