
ZSCORE, RSI, ATR, SMA, EMA
Ini bukan strategi pelacakan tren biasa. Strategi arbitrage statistik XAG/XAU didasarkan pada asumsi utama: bahwa ada hubungan pengembalian nilai rata-rata jangka panjang antara harga emas dan perak. Ketika Z-Score menembus ± 2 standar deviasi, harga menyimpang mencapai batas dalam arti statistik, dan pada saat itu kesempatan untuk menangkap pengembalian masuk. Data retrospektif menunjukkan bahwa metode arbitrage statistik ini memiliki keuntungan yang jelas setelah penyesuaian risiko di pasar logam mulia.
Inti dari strategi ini adalah untuk membangun model rasio harga standar. Rasio tersebut kemudian dipadatkan dengan XAG dan XAU dengan 20 siklus SMA, lalu dihitung dan dipadatkan dengan 3 siklus EMA. Proses ini lebih stabil daripada rasio harga sederhana dan efektif untuk menyaring kebisingan jangka pendek.
Berbeda dengan sinyal overbought/oversold RSI tradisional, RSI=50 digunakan sebagai kondisi penyaringan kosong. RSI < 50 memungkinkan overbought, dan RSI > 50 memungkinkan overbought. Logika desain ini jelas: membeli untuk menunggu bouncing ketika relatif lemah, menjual untuk menunggu pemulihan ketika relatif kuat.
Stop-loss diatur untuk 3 kali ATR, stop-loss diatur untuk 8 kali ATR, dan rasio risiko-keuntungan mencapai 1: 2.67. Desain ini didasarkan pada karakteristik statistical arbitrage: probabilitas yang tinggi untuk pengembalian rata-rata, tetapi perlu diberikan ruang toleransi yang cukup untuk kesalahan. 14-siklus ATR memastikan bahwa tingkat stop-loss dapat beradaptasi dengan perubahan volatilitas pasar.
Strategi statistical arbitrage bekerja paling baik dalam situasi pergerakan horizontal, karena pada saat ini karakteristik nilai rata-rata yang kembali lebih jelas. Dalam pasar tren unilateral, harga mungkin menyimpang dari nilai rata-rata untuk waktu yang lama, menyebabkan strategi menghadapi risiko penarikan yang lebih besar. Disarankan untuk digunakan ketika volatilitas pasar sedang, tidak ada tren unilateral yang jelas.
Hubungan statistik historis tidak menjamin kelangsungan di masa depan. Perbandingan harga emas dan perak mungkin mengalami penyimpangan jangka panjang karena perubahan struktur penawaran dan permintaan, perbedaan kebijakan moneter, dan faktor-faktor lainnya. Strategi memiliki risiko kerugian berkelanjutan, terutama selama perubahan struktural pasar.
//@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")