Chiến lược giao dịch đảo ngược dựa trên hỗ trợ/kháng cự chung

Tác giả:ChaoZhang, Ngày: 2023-10-30 11:23:25
Tags:

img

Tổng quan

Chiến lược này áp dụng giao dịch đảo ngược dựa trên các yếu tố tăng/giảm, với các mức thu lợi nhuận đã được đặt trước. Cốt lõi của các yếu tố là mô hình mở rộng Generalized Support/Resistance dựa trên khối lượng giao dịch, phù hợp với các cổ phiếu có khối lượng và biến động cao. Ưu điểm nằm trong việc nắm bắt các cơ hội đảo ngược lớn hơn trong thời gian trung bình ngắn và kiếm lợi nhuận nhanh chóng, trong khi nó chịu rủi ro bị mắc kẹt.

Chiến lược logic

  1. Xác định các yếu tố tăng/giảm dựa trên Generalised Support/Resistance với khối lượng

    • Sử dụng mô hình nến để xác định mức S / R cổ điển, lọc theo khối lượng đáng kể

    • S / R tổng quát có phạm vi phủ sóng tốt hơn so với các mô hình cổ điển

    • Phá vỡ các tín hiệu hỗ trợ tổng quát nhân dài, phá vỡ các tín hiệu kháng cự tổng quát nhân ngắn

  2. Giao dịch đảo ngược

    • Lấy vị trí ngược khi tín hiệu yếu tố kích hoạt

    • Nếu đã ở vị trí, giảm hoặc thêm vị trí ngược

  3. Đặt mục tiêu lợi nhuận

    • Đặt lệnh dừng lỗ dựa trên ATR

    • Đặt nhiều mức lợi nhuận như 1R, 2R, 3R

    • Lợi nhuận một phần khi đạt được các mục tiêu khác nhau

Ưu điểm

  • Nhận được sự đảo ngược trung hạn hợp lý

    S / R breakouts đại diện cho các tín hiệu đảo ngược mạnh mẽ với một số độ tin cậy, có thể bắt được sự đảo ngược trung hạn

  • Lợi nhuận nhanh chóng, rút tiền nhỏ

    Bằng cách thiết lập dừng lỗ và nhiều mục tiêu lợi nhuận, có thể đạt được lợi nhuận nhanh chóng và giới hạn drawdowns

  • Thích hợp cho các cổ phiếu có vốn tổ chức và biến động đáng kể

    Chiến lược dựa trên khối lượng, đòi hỏi sự tham gia của các tổ chức đáng kể; cũng cần biến động để kiếm lợi nhuận

Rủi ro

  • Bị mắc kẹt trong thị trường giới hạn phạm vi

    Thường xuyên dừng lỗ thoát và nhập lại theo hướng ngược lại có thể dẫn đến whipsaws

  • Sự cố của hỗ trợ / kháng cự

    S / R tổng quát không hoàn toàn đáng tin cậy, một số lỗi tồn tại

  • Rủi ro giữ một mặt

    Lý thuyết đảo ngược thuần túy có thể bỏ lỡ cơ hội xu hướng lớn

  • Quản lý rủi ro:

    • Điều kiện yếu tố nới lỏng, không đảo ngược trên mỗi breakout

    • Thêm các bộ lọc khác, ví dụ như chênh lệch giá / khối lượng

    • Tối ưu hóa chiến lược dừng lỗ để giảm bớt bẫy

Hướng dẫn cải thiện

  • Tối ưu hóa các thông số S/R

    Tìm các yếu tố đáng tin cậy hơn bằng cách điều chỉnh các cài đặt S / R tổng quát

  • Tối ưu hóa lợi nhuận

    Thêm nhiều mức lợi nhuận hơn, hoặc sử dụng các mục tiêu không cố định

  • Tối ưu hóa stop loss

    Điều chỉnh các tham số ATR hoặc sử dụng istics stop loss để giảm dừng không cần thiết

  • Bao gồm xu hướng và các yếu tố khác

    Thêm các bộ lọc xu hướng như trung bình động để tránh xung đột xu hướng lớn; cũng thêm các yếu tố hỗ trợ khác

Tóm lại

Lòng cốt lõi của chiến lược là nắm bắt những biến động trung hạn tốt thông qua giao dịch đảo ngược. Logic là đơn giản và trực tiếp, và có thể thực tế với điều chỉnh tham số. Nhưng bản chất hung hăng của đảo ngược dẫn đến một số rủi ro rút và bẫy.


/*backtest
start: 2023-09-29 00:00:00
end: 2023-10-29 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © DojiEmoji

//@version=5
strategy("Fractal Strat [KL] ", overlay=true, pyramiding=1, initial_capital=1000000000)
var string ENUM_LONG = "Long"
var string GROUP_ENTRY = "Entry"
var string GROUP_TSL = "Stop loss"
var string GROUP_TREND = "Trend prediction"
var string GROUP_ORDER = "Order size and Profit taking"

// backtest_timeframe_start = input.time(defval=timestamp("01 Apr 2000 13:30 +0000"), title="Backtest Start Time")
within_timeframe = true

// TSL: calculate the stop loss price. {
_multiple       = input(2.0, title="ATR Multiplier for trailing stop loss", group=GROUP_TSL)
ATR_TSL         = ta.atr(input(14, title="Length of ATR for trailing stop loss", group=GROUP_TSL, tooltip="Initial risk amount = atr(this length) x multiplier")) * _multiple
TSL_source      = low
TSL_line_color  = color.green
TSL_transp      = 100
var stop_loss_price = float(0)

var float initial_entry_p    = float(0)
var float risk_amt           = float(0)
var float initial_order_size = float(0)

if strategy.position_size == 0 or not within_timeframe
    TSL_line_color := color.black
    stop_loss_price := TSL_source - ATR_TSL
else if strategy.position_size > 0
    stop_loss_price := math.max(stop_loss_price, TSL_source - ATR_TSL)
    TSL_transp := 0

plot(stop_loss_price, color=color.new(TSL_line_color, TSL_transp))
// } end of "TSL" block


// Order size and profit taking {
pcnt_alloc = input.int(5, title="Allocation (%) of portfolio into this security", tooltip="Size of positions is based on this % of undrawn capital. This is fixed throughout the backtest period.", minval=0, maxval=100, group=GROUP_ORDER) / 100

// Taking profits at user defined target levels relative to risked amount (i.e 1R, 2R, 3R)
var bool  tp_mode = input(true, title="Take profit and different levels", group=GROUP_ORDER)
var float FIRST_LVL_PROFIT = input.float(1, title="First level profit", tooltip="Relative to risk. Example: entry at $10 and inital stop loss at $9. Taking first level profit at 1R means taking profits at $11", group=GROUP_ORDER)
var float SECOND_LVL_PROFIT = input.float(2, title="Second level profit", tooltip="Relative to risk. Example: entry at $10 and inital stop loss at $9. Taking second level profit at 2R means taking profits at $12", group=GROUP_ORDER)
var float THIRD_LVL_PROFIT = input.float(3, title="Third level profit", tooltip="Relative to risk. Example: entry at $10 and inital stop loss at $9. Taking third level profit at 3R means taking profits at $13", group=GROUP_ORDER)

// }

// Fractals {
// Modified from synapticEx's implementation: https://www.tradingview.com/script/cDCNneRP-Fractal-Support-Resistance-Fixed-Volume-2/

rel_vol_len = 6 // Relative volume is used; the middle candle has to have volume above the average (say sma over prior 6 bars)
rel_vol = ta.sma(volume, rel_vol_len)
_up = high[3]>high[4] and high[4]>high[5] and high[2]<high[3] and high[1]<high[2] and volume[3]>rel_vol[3]
_down = low[3]<low[4] and low[4]<low[5] and low[2]>low[3] and low[1]>low[2] and volume[3]>rel_vol[3]

fractal_resistance = high[3], fractal_support = low[3]   // initialize

fractal_resistance :=  _up ? high[3] : fractal_resistance[1]
fractal_support := _down ? low[3] : fractal_support[1]

plot(fractal_resistance, "fractal_resistance", color=color.new(color.red,50), linewidth=2, style=plot.style_cross, offset =-3, join=false)
plot(fractal_support, "fractal_support", color=color.new(color.lime,50), linewidth=2, style=plot.style_cross, offset=-3, join=false)
// }

// ATR diversion test {
// Hypothesis testing (2-tailed):
//
// Null hypothesis (H0) and Alternative hypothesis (Ha):
//     H0 : atr_fast equals atr_slow
//     Ha : atr_fast not equals to atr_slow; implies atr_fast is either too low or too high
len_fast    = input(5,title="Length of ATR (fast) for diversion test", group=GROUP_ENTRY)
atr_fast    = ta.atr(len_fast)
atr_slow    = ta.atr(input(50,title="Length of ATR (slow) for diversion test", group=GROUP_ENTRY, tooltip="This needs to be larger than Fast"))

// Calculate test statistic (test_stat)
std_error   = ta.stdev(ta.tr, len_fast) / math.pow(len_fast, 0.5)
test_stat = (atr_fast - atr_slow) / std_error

// Compare test_stat against critical value defined by user in settings
//critical_value = input.float(1.645,title="Critical value", tooltip="Strategy uses 2-tailed test to compare atr_fast vs atr_slow. Null hypothesis (H0) is that both should equal. Based on the computed test statistic value, if absolute value of it is +/- this critical value, then H0 will be rejected.", group=GROUP_ENTRY)
conf_interval = input.string(title="Confidence Interval", defval="95%", options=["90%","95%","99%"], tooltip="Critical values of 1.645, 1.96, 2.58, for CI=90%/95%/99%, respectively; Under 2-tailed test to compare atr_fast vs atr_slow. Null hypothesis (H0) is that both should equal. Based on the computed test statistic value, if absolute value of it is +/- critical value, then H0 will be rejected.")
critical_value = conf_interval == "90%" ? 1.645 : conf_interval == "95%" ? 1.96 : 2.58
reject_H0_lefttail = test_stat < -critical_value
reject_H0_righttail = test_stat > critical_value

// } end of "ATR diversion test" block

// Entry Signals
entry_signal_long = close >= fractal_support and reject_H0_lefttail

// MAIN {
// Update the stop limit if strategy holds a position.
if strategy.position_size > 0
    strategy.exit(ENUM_LONG, comment="SL", stop=stop_loss_price)

// Entry
if within_timeframe and entry_signal_long and strategy.position_size == 0
    initial_entry_p := close
    risk_amt := ATR_TSL
    initial_order_size := math.floor(pcnt_alloc * strategy.equity / close)
    strategy.entry(ENUM_LONG, strategy.long, qty=initial_order_size)

var int TP_taken_count = 0
if tp_mode and close > strategy.position_avg_price
    if close >= initial_entry_p + THIRD_LVL_PROFIT * risk_amt and TP_taken_count == 2
        strategy.close(ENUM_LONG, comment="TP Lvl3", qty=math.floor(initial_order_size / 3))
        TP_taken_count := TP_taken_count + 1
    else if close >= initial_entry_p + SECOND_LVL_PROFIT * risk_amt and TP_taken_count == 1
        strategy.close(ENUM_LONG, comment="TP Lvl2", qty=math.floor(initial_order_size / 3))
        TP_taken_count := TP_taken_count + 1
    else if close >= initial_entry_p + FIRST_LVL_PROFIT * risk_amt and TP_taken_count == 0
        strategy.close(ENUM_LONG, comment="TP Lvl1", qty=math.floor(initial_order_size / 3))
        TP_taken_count := TP_taken_count + 1
    
// Alerts
_atr = ta.atr(14)
alert_helper(msg) =>
    prefix = "[" + syminfo.root + "] "
    suffix = "(P=" + str.tostring(close, "#.##") + "; atr=" + str.tostring(_atr, "#.##") + ")"
    alert(str.tostring(prefix) + str.tostring(msg) + str.tostring(suffix), alert.freq_once_per_bar)

if strategy.position_size > 0 and ta.change(strategy.position_size)
    if strategy.position_size > strategy.position_size[1]
        alert_helper("BUY")
    else if strategy.position_size < strategy.position_size[1]
        alert_helper("SELL")

// Clean up - set the variables back to default values once no longer in use
if ta.change(strategy.position_size) and strategy.position_size == 0
    TP_taken_count := 0
    initial_entry_p := float(0)
    risk_amt := float(0)
    initial_order_size := float(0)
    stop_loss_price := float(0)
// } end of MAIN block


Thêm nữa