
ZSCORE, RSI, ATR, SMA, EMA
Đây không phải là một chiến lược theo dõi xu hướng thông thường. Chiến lược đánh giá thống kê XAG/XAU dựa trên một giả định cốt lõi: giá vàng và bạc có mối quan hệ quay trở về giá trị trung bình trong thời gian dài. Khi Z-Score phá vỡ chênh lệch tiêu chuẩn ± 2, giá sẽ đi lạc đến cực điểm theo nghĩa thống kê, khi đó bắt đầu cơ hội quay trở lại. Dữ liệu đánh giá lại cho thấy phương pháp đánh giá thống kê này có lợi thế rõ ràng về lợi nhuận sau khi điều chỉnh rủi ro trong thị trường kim loại quý.
Cốt lõi của chiến lược là xây dựng mô hình tỷ lệ giá chuẩn hóa. Định chuẩn hóa cho XAG và XAU theo 20 chu kỳ SMA, sau đó tính tỷ lệ và làm mịn bằng 3 chu kỳ EMA. Cách xử lý này ổn định hơn tỷ lệ giá đơn giản và có thể lọc hiệu quả tiếng ồn ngắn hạn. Khi tỷ lệ chuẩn hóa Z-Score vượt quá khoảng ± 2, cho thấy giá hiện tại lệch khỏi trung bình lịch sử hơn 2 điểm chuẩn, theo thống kê là một sự kiện xác suất nhỏ, cung cấp thời gian để quay trở lại trung bình.
Khác với tín hiệu mua quá bán RSI truyền thống, RSI = 50 được sử dụng như một điều kiện lọc nhiều không gian. RSI <50 cho phép làm nhiều, RSI> 50 cho phép làm rỗng.
Đặt dừng là 3 lần ATR, đặt dừng là 8 lần ATR, tỷ lệ lợi nhuận rủi ro đạt 1:2.67. Thiết kế này dựa trên tính chất của tỷ lệ rủi ro thống kê: xác suất trở lại trung bình cao hơn, nhưng cần phải cho phép đủ không gian cho lỗi. ATR 14 chu kỳ đảm bảo mức dừng lỗ có thể thích nghi với sự thay đổi biến động của thị trường.
Chiến lược đánh giá thống kê hoạt động tốt nhất trong tình huống biến động ngang, vì tính năng quay trở lại của giá trị trung bình rõ ràng hơn. Trong thị trường xu hướng đơn phương, giá có thể lệch khỏi giá trị trung bình trong một thời gian dài, dẫn đến chiến lược đối mặt với rủi ro rút lui lớn hơn.
Các mối quan hệ thống kê lịch sử không đảm bảo sự tiếp tục trong tương lai. Giá vàng và bạc có thể bị lệch lâu dài do các yếu tố như thay đổi cấu trúc cung cầu, sự khác biệt về chính sách tiền tệ. Chiến lược có nguy cơ mất mát liên tục, đặc biệt là trong thời gian thay đổi cấu trúc thị trường.
//@version=6
strategy("Stat Arb(xag & xau)")
// ══════════════════════════════════════════════════════════════
// BENCHMARK DATA
// ══════════════════════════════════════════════════════════════
float benchClose = request.security("XAG_USDT.swap", timeframe.period, close)
// ══════════════════════════════════════════════════════════════
// HELPER FUNCTIONS
// ══════════════════════════════════════════════════════════════
f_cov(float src1, float src2, int len) =>
ta.sma(src1 * src2, len) - ta.sma(src1, len) * ta.sma(src2, len)
f_var(float src, int len) =>
ta.sma(src * src, len) - math.pow(ta.sma(src, len), 2)
// ══════════════════════════════════════════════════════════════
// SPREAD ENGINE — NORMALIZED RATIO
// ══════════════════════════════════════════════════════════════
int lookback = 20
float pairSma = ta.sma(close, lookback)
float benchSma = ta.sma(benchClose, lookback)
float pairNorm = pairSma != 0 ? close / pairSma * 100.0 : 100.0
float benchNorm = benchSma != 0 ? benchClose / benchSma * 100.0 : 100.0
float modelRaw = benchNorm != 0 ? pairNorm / benchNorm : 1.0
float model = ta.ema(modelRaw, 3)
float zMean = ta.sma(model, lookback)
float zStd = ta.stdev(model, lookback)
float zScore = zStd != 0 ? (model - zMean) / zStd : 0.0
// ══════════════════════════════════════════════════════════════
// RSI FILTER — BELOW / ABOVE 50
// ══════════════════════════════════════════════════════════════
float rsiVal = ta.rsi(close, 14)
bool rsiLongOk = rsiVal < 50.0
bool rsiShortOk = rsiVal > 50.0
// ══════════════════════════════════════════════════════════════
// ENTRY SIGNALS
// Z crosses below -2 = long, above +2 = short
// ══════════════════════════════════════════════════════════════
bool enterLong = ta.crossunder(zScore, -2.0) and rsiLongOk
bool enterShort = ta.crossover(zScore, 2.0) and rsiShortOk
// ══════════════════════════════════════════════════════════════
// ATR STOP + TAKE PROFIT
// Stop: 8x ATR from entry (hardcoded)
// TP: 3x ATR from entry (hardcoded), stamped at entry
// ══════════════════════════════════════════════════════════════
float atrVal = ta.atr(14)
var float tpLevel = na
var float slLevel = na
var float entryPrice = na
bool isNewEntry = strategy.position_size != 0 and strategy.position_size[1] == 0
if isNewEntry
entryPrice := strategy.position_avg_price
if strategy.position_size > 0
tpLevel := entryPrice + atrVal * 3.0
slLevel := entryPrice - atrVal * 8.0
else
tpLevel := entryPrice - atrVal * 3.0
slLevel := entryPrice + atrVal * 8.0
if strategy.position_size == 0
tpLevel := na
slLevel := na
entryPrice := na
// ══════════════════════════════════════════════════════════════
// EXIT CONDITIONS — high/low for intrabar touch
// ══════════════════════════════════════════════════════════════
bool tpHitLong = strategy.position_size > 0 and not na(tpLevel) and high >= tpLevel
bool tpHitShort = strategy.position_size < 0 and not na(tpLevel) and low <= tpLevel
bool slHitLong = strategy.position_size > 0 and not na(slLevel) and low < slLevel
bool slHitShort = strategy.position_size < 0 and not na(slLevel) and high > slLevel
// ══════════════════════════════════════════════════════════════
// EXECUTION
// ══════════════════════════════════════════════════════════════
if enterLong
strategy.close("Short", comment="Flip")
strategy.entry("Long", strategy.long)
if enterShort
strategy.close("Long", comment="Flip")
strategy.entry("Short", strategy.short)
if tpHitLong
strategy.close("Long", comment="TP")
if tpHitShort
strategy.close("Short", comment="TP")
if slHitLong
strategy.close("Long", comment="SL")
if slHitShort
strategy.close("Short", comment="SL")
// ══════════════════════════════════════════════════════════════
// VISUALS
// ══════════════════════════════════════════════════════════════
hline( 2.0, "+2", color=color.new(color.red, 20), linestyle=hline.style_dashed)
hline(-2.0, "-2", color=color.new(color.teal, 20), linestyle=hline.style_dashed)
hline( 0.0, "Mid", color=color.gray, linestyle=hline.style_solid)
color zCol = zScore >= 0 ? color.new(color.red, 10) : color.new(color.teal, 10)
plot(zScore, title="Z Score", color=zCol, linewidth=3)
bgcolor(zScore > 2.0 ? color.new(color.red, 90) : na, title="Overbought Zone")
bgcolor(zScore < -2.0 ? color.new(color.teal, 90) : na, title="Oversold Zone")
bgcolor(strategy.position_size > 0 ? color.new(color.teal, 93) : na, title="In Long")
bgcolor(strategy.position_size < 0 ? color.new(color.red, 93) : na, title="In Short")
plotshape(enterLong, style=shape.triangleup, location=location.bottom, color=color.teal, size=size.small)
plotshape(enterShort, style=shape.triangledown, location=location.top, color=color.red, size=size.small)
plotshape(tpHitLong or tpHitShort, style=shape.flag, location=location.top, color=color.yellow, size=size.tiny, text="TP")
plotshape(slHitLong or slHitShort, style=shape.xcross, location=location.top, color=color.orange, size=size.tiny, text="SL")