Chiến lược này kết hợp các tín hiệu đảo ngược xu hướng tiềm năng từ chỉ số Supertrend và chỉ số MACD, cùng với các tín hiệu mua quá mức / bán quá mức từ chỉ số RSI, để tạo thành một hệ thống tương đối ổn định và hiệu quả cho các tín hiệu vào và ra.
Lý thuyết cốt lõi của chiến lược này nằm trong việc sử dụng kết hợp chỉ số Supertrend và chỉ số MACD làm tiêu chí cho các tín hiệu đầu vào.
Về phần Supertrend, chiến lược sử dụng sự thay đổi hướng của chỉ số Supertrend làm tín hiệu đảo ngược tiềm năng. Khi hướng Supertrend quay từ trên xuống, một tín hiệu mua được tạo ra. Khi hướng quay từ dưới lên, một tín hiệu bán được tạo ra.
Về phần MACD, chiến lược sử dụng độ dốc và đường không chéo của chỉ số MACD trên khung thời gian thấp hơn (ngày) để xác định các cơ hội đảo ngược tiềm năng. Khi giá trị tuyệt đối của độ dốc MACD lớn (trên ngưỡng) và độ dốc duy trì xu hướng tăng, một tín hiệu được tạo ra. Nếu đường MACD vượt qua đường không, một tín hiệu phụ được tạo ra.
Đối với tín hiệu nhập cảnh, chiến lược yêu cầu tín hiệu Supertrend và tín hiệu MACD ở cùng một hướng trước khi gửi lệnh giao dịch.
Ngoài ra, đối với tín hiệu thoát, chiến lược cũng áp dụng các tín hiệu mua quá mức / bán quá mức từ chỉ số RSI. Khi RSI vượt trên 80, một tín hiệu bán được tạo ra. Khi RSI giảm xuống dưới 20, một tín hiệu mua được tạo ra. Chúng giúp xác định thời gian đảo ngược.
Ưu điểm lớn nhất của chiến lược này là sự đa dạng của các tín hiệu chỉ số.
Các tín hiệu đảo ngược siêu xu hướng có thể nắm bắt các xu hướng ngắn hạn tương đối mạnh; độ dốc MACD có thể đánh giá sức mạnh xu hướng trung dài hạn để tránh bị sai lệch bởi các sự đảo ngược sai; chỉ số RSI có thể cung cấp thời gian vào và ra tốt nhất trong thị trường giới hạn phạm vi bằng cách chỉ ra mức mua quá mức / bán quá mức. Đặt các tín hiệu từ nhiều chỉ số có thể lọc ra một số giao dịch ồn ào và đạt tỷ lệ thắng cao hơn.
Ngoài ra, thiết kế khung thời gian cũng hợp lý. Supertrend sử dụng khung thời gian hàng giờ trong khi MACD sử dụng khung thời gian hàng ngày. Điều này đảm bảo cả tần suất giao dịch và sự ổn định trong phán đoán xu hướng.
Rủi ro chính của chiến lược này là khả năng nhầm lẫn tín hiệu giữa các chỉ số khác nhau. Ví dụ, Supertrend có thể đưa ra sự đảo ngược sai trong khi tín hiệu MACD không đồng bộ. Điều này có thể dẫn đến tổn thất không cần thiết.
Ngoài ra, chỉ số RSI để xác định thời gian thoát cũng có thể quá sớm hoặc quá muộn, ngăn chặn thời gian giữ tối đa.
Cuối cùng, ngưỡng độ dốc MACD quá lớn cũng có thể bỏ lỡ các cơ hội đảo ngược yếu hơn.
Chiến lược này có thể được tối ưu hóa thêm từ các khía cạnh sau:
Thiết lập cơ chế dừng lỗ khi lỗ vượt quá một tỷ lệ nhất định.
Thêm ngưỡng động cho đánh giá độ dốc MACD. Tăng ngưỡng độ dốc khi biến động thị trường cao và giảm ngưỡng khi thị trường ổn định.
Thêm điều kiện pullback cho phán đoán thoát RSI. Yêu cầu một cuộc gọi trở lại đáng kể sau khi RSI vượt quá 80 trước khi xem xét đóng vị trí.
Kiểm tra MACD với khối lượng và xem liệu nó có cải thiện độ tin cậy tín hiệu không
Cố gắng điều chỉnh tham số tự động để tìm các thiết lập tối ưu
Chiến lược định lượng
/*backtest start: 2022-12-19 00:00:00 end: 2023-12-25 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ //@version=5 strategy("SuperTrend.MACD Strategy", overlay=false, default_qty_type=strategy.percent_of_equity, default_qty_value=100, initial_capital=100000, pyramiding=5, process_orders_on_close=true) // ---------------- Utility Functions ---------------- getArrayValue(float[] arr, int ago) => if ago >= 0 array.get(arr, ago >= array.size(arr) ? na: array.size(arr) + -1 * ago -1) else na filterNA(float[] a, s, int y) => int x = 0 if not na(s[0]) array.push(a, s[0]) if array.size(a) > y array.shift(a) a pine_rsi(float[] x, int y) => x0 = getArrayValue(x, 0) x1 = getArrayValue(x, 1) u = math.max(x0 - x1, 0) // upward ta.change d = math.max(x1 - x0, 0) // downward ta.change rs = ta.rma(u, y) / ta.rma(d, y) res = 100 - 100 / (1 + rs) res turnAround(float[] arr) => int isTurnAround = 0 now = getArrayValue(arr, 0) p1 = getArrayValue(arr, 1) p2 = getArrayValue(arr, 2) if p1 > now and p1 > p2 isTurnAround := -1 else if p1 < now and p1 < p2 isTurnAround := 1 intergerizeSignal(i) => i>0 ? 1 : i<0 ? -1 : 0 linreg(float[] y, int n, int offset=0) => float slope = na float intercept = na int endcursor = offset + n - 1 if array.size(y) > endcursor float sumX = 0 float sumX2 = 0 float sumY = 0 float sumY2 = 0 float sumXY = 0 for i=offset to endcursor yv = array.get(y, i) sumY += yv sumY2 += math.pow(yv, 2) sumX += i sumX2 += math.pow(i, 2) sumXY += i*yv // Pearson correlation coefficient r = (n * sumXY - sumX * sumY) / math.sqrt((n * sumY2 - math.pow(sumY, 2)) * (n * sumX2 - math.pow(sumX, 2))) // Coefficient of determination r2 = math.pow(r, 2) meanX = sumX / n meanY = sumY / n slope := (n * sumXY - sumX * sumY) / (n * sumX2 - math.pow(sumX, 2)) intercept := meanY - slope * meanX [slope, intercept] isStartOfDay() => dayofweek != dayofweek[1] // ---------------- Variables ---------------- varip float st_signal = 0 varip float macd_signal = 0 varip float macd_close_signal = 0 varip float histo_signal = 0 var int openSignal = 0 var int closeSignal = 0 // -------------------------------- Supertrend Signal (Open) -------------------------------- // ST calculation atrPeriod = input(10, "Supertrend ATR Length") factor = input.float(2.0, "Supertrend Factor", step = 0.01) [_, direction] = ta.supertrend(factor, atrPeriod) st_direction_change = ta.change(direction) if st_direction_change < 0 st_signal := 4 if st_direction_change > 0 st_signal := -4 // -------------------------------- MACD Signal (Open + Close) -------------------------------- // MACD Calculation fastLength = input(12, title="MACD Fast Length") slowLength = input(26, title="MACD Slow Length") signalLength = input(9, title="MACD Signal Length") macdSlowTimeframe = input.timeframe("D", "MACD Timeframe") macdSlopeLookbackOpen = input(7, title="MACD Slope Lookback - Open") macdSlopeLookbackClose = input(3, title="MACD Slope Lookback - Close") dailyClose = request.security(syminfo.tickerid, macdSlowTimeframe, close, barmerge.gaps_on) [macdLine, signalLine, _] = ta.macd(dailyClose, fastLength, slowLength, signalLength) // MACD Slope calculation varip macdHistory = array.new<float>(0) varip macdSlowSlopeArr = array.new<float>(0) varip float macdSlowSlope = na varip float macdCloseSlope = na if not na(macdLine[0]) array.push(macdHistory, macdLine[0]) if array.size(macdHistory) > macdSlopeLookbackOpen array.shift(macdHistory) [s1, _] = linreg(macdHistory, macdSlopeLookbackOpen) macdSlowSlope := s1 array.push(macdSlowSlopeArr, macdSlowSlope) if array.size(macdSlowSlopeArr) > macdSlopeLookbackClose array.shift(macdSlowSlopeArr) [s2, _] = linreg(macdSlowSlopeArr, macdSlopeLookbackClose) macdCloseSlope := s2 // MACD Signal Calculation // > open signal threshold_macdSlowSlope = input.float(0.75, "MACD Slope Open Threshold", step = 0.05) macdSlowSlopeOverThreshold = math.abs(macdSlowSlope) >= threshold_macdSlowSlope macdSlowSlopeTrend = macdSlowSlope - getArrayValue(macdSlowSlopeArr, 1) macdSlowSlopeTrendConfirm = macdSlowSlope*macdSlowSlopeTrend >0 if (macdSlowSlopeOverThreshold and macdSlowSlopeTrendConfirm) macd_signal := 3*macdSlowSlope/math.abs(macdSlowSlope) else macd_signal := 0 // > close signal int macdCloseSignal = 0 macdCloseSignal := intergerizeSignal(macdCloseSlope) // Histogram signal Calculation histSlow = macdLine - signalLine if (ta.crossover(histSlow, 0)) histo_signal := 2 if (ta.crossunder(histSlow, 0)) histo_signal := -2 // -------------------------------- RSI Signal (Close) -------------------------------- int rsiCloseSignal = 0 varip float rsiSlow = na rsiPeriod = input(14, title="RSI Period") varip dailyCloseRSIFilter = array.new_float() // rewrite pine_rsi to remove NaN value from series at calculation dailyCloseRSIFilter := filterNA(dailyCloseRSIFilter, dailyClose, rsiPeriod) if not na(dailyClose[0]) rsiSlow := pine_rsi(dailyCloseRSIFilter, rsiPeriod) if rsiSlow > 80 rsiCloseSignal := -1 else if rsiSlow < 20 rsiCloseSignal := 1 else rsiCloseSignal := 0 // -------------------------------- Overall Signal -------------------------------- // Close signal closeSignals = array.from(macdCloseSignal, rsiCloseSignal) closeSignal := array.includes(closeSignals, 1) ? 1 : array.includes(closeSignals, -1) ? -1 : 0 closeSignal := closeSignal * 5 // Open signal if (macd_signal * st_signal > 0) and (macd_signal * macd_close_signal >= 0) openSignal := intergerizeSignal(st_signal) openSignal := openSignal * 6 else openSignal := 0 // -------------------------------- Order -------------------------------- // if strategy.position_size == 0 if openSignal * closeSignal >=0 if openSignal > 0 strategy.entry("Long Entry", strategy.long) else if openSignal < 0 strategy.entry("Short Entry", strategy.short) if strategy.position_size != 0 if closeSignal < 0 strategy.close("Long Entry") if closeSignal > 0 strategy.close("Short Entry") // -------------------------------- Plot -------------------------------- plot(closeSignal, title="Close Signal", color=color.red, linewidth = 1, style=plot.style_area) plot(openSignal, title="Open Signal", color=color.green, linewidth = 1, style=plot.style_area) plot(st_signal, title="ST Signal", color=color.black, linewidth = 1, style=plot.style_circles) plot(macd_signal, title="MACD Signal", color=color.blue, linewidth = 1, style=plot.style_circles) // plot(macdSlowSlope, title="macd slow slope", color=color.purple, linewidth = 1, style=plot.style_line) // plot(macdCloseSlope, title="macd slow slope", color=color.lime, linewidth = 1, style=plot.style_line) hline(0, "Zero Line", color=color.gray)