
ZSCORE, RSI, ATR, SMA, EMA
Это не обычная стратегия отслеживания тенденций. Статистическая стратегия арбитража XAG/XAU основана на одном ключевом допущении: существует долгосрочная средне-возвратная связь между ценой золота и серебра. Когда Z-Score превышает ±2 стандартного отклонения, цена отклоняется от достижения предельного значения в статистическом смысле, и тогда вступает в игру, чтобы поймать возможность возврата.
В основе стратегии лежит построение стандартизированной модели ценового соотношения. С помощью 20-циклического SMA стандартизируют XAG и XAU, затем рассчитывают соотношение и сглаживают его с помощью 3-циклической EMA. Такая обработка более стабильна, чем простое ценовое соотношение, и эффективно фильтрует краткосрочный шум.
В отличие от традиционного RSI-сигнала сверхпокупа и сверхпродажи, здесь используется RSI = 50 в качестве многопрофильного фильтра. RSI <50 позволяет делать больше, RSI >50 позволяет делать меньше. Логика этой конструкции ясна: покупать в ожидании отскока при относительной слабости, продавать в ожидании отскока при относительной силе.
Стоп-стоп устанавливается в 3 раза ATR, стоп-лосс - в 8 раз ATR, соотношение риска-прибыли достигает 1:2.67. Эта конструкция основана на статистическом артериальном характере: высокая вероятность возвращения средней стоимости, но необходимо предоставить достаточное пространство для допуска ошибок. 14-циклический ATR обеспечивает адаптацию уровня стоп-лосса к изменению волатильности рынка. Историческая проверка показывает, что этот соотношение позволяет достичь ожидаемой положительной прибыли в торговле драгоценными металлическими парами.
Статистическая стратегия арбитража лучше всего работает в условиях поперечного колебания, поскольку в это время более заметны характеристики среднемесячного возвращения. На рынке одностороннего тренда цены могут длительное время отклоняться от среднего значения, что приводит к большему риску для стратегии. Рекомендуется использовать его при умеренных колебаниях рынка и отсутствии очевидной односторонней тенденции.
Исторические статистические связи не гарантируют будущего продолжения. Долгосрочные отклонения в соотношении золота и серебра могут быть вызваны изменениями в структуре спроса и предложения, различиями в денежно-кредитной политике и другими факторами. Существует риск непрерывного потери стратегии, особенно во время структурных изменений на рынке. Рекомендуется строгое выполнение управления рисками, контроль риска одной сделки не более 2% от средств счета и регулярная оценка эффективности стратегии.
//@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")