Привет, Хабр!

Осцилляторы — это технические индикаторы, которые колеблются в пределах заданного диапазона, обычно от 0 до 100, и используются для определения состояния перекупленности или перепроданности рынка.

Осцилляторы способны предсказывать возможные изменения трендов до того, как эти изменения произойдут.

Принцип работы осцилляторов основан на сравнении текущей цены актива с его ценовыми уровнями за определенный период. Например, если текущая цена находится близко к верхнему диапазону за последний период, осциллятор покажет высокие значения, указывая на перекупленность. И наоборот, если цена находится близко к нижнему диапазону, это указывает на перепроданность.

Осцилляторы могут использоваться в различных рыночных условиях, включая трендовые и боковые рынки. В трендовых рынках осцилляторы помогают находить точки входа в направлении основного тренда, а в боковых — выявлять моменты разворота.

Relative Strength Index (RSI)

RSI был разработан Дж. Уэллсом Уайлдером и представлен в его книге "New Concepts in Technical Trading Systems" в 1978 году. RSI является осциллятором, который измеряет скорость и изменение ценовых движений актива, оценивая условия его перекупленности или перепроданности. Он колеблется между значениями от 0 до 100 и помогает трейдерам определять моменты для покупки или продажи активов.

Расчет RSI включает два шага:

  1. Расчет относительной силы (RS):


     [ RS = \frac{\text{Средний прирост за период}}{\text{Средняя потеря за период}} ]

  2. Расчет самого RSI:


     RSI = 100 - \left( \frac{100}{1 + RS} \right)

Стандартный период для расчета RSI составляет 14 дней. Например, если актив закрылся выше за 7 из последних 14 дней с средним приростом 1%, а остальные 7 дней закрылись ниже со средней потерей 0.8%, то начальное значение RS будет 1.25 (1% / 0.8%). Далее применяется формула для получения RSI.

Значения RSI интерпретируются так:

  • Выше 70 — актив перекуплен, возможно, будет коррекция вниз.

  • Ниже 30 — актив перепродан, возможно, ожидается рост цены.

RSI помогает трейдерам выявлять перекупленные и перепроданные состояния рынка, что может сигнализировать о возможных разворотах трендов. Например, если RSI превышает 70, это указывает на перекупленность актива, что может привести к его коррекции. Наоборот, если RSI падает ниже 30, это сигнализирует о перепроданности, что может привести к росту цены.

Дивергенции RSI происходят, когда тренд цен актива и тренд самого RSI расходятся, что может указывать на ослабление текущего тренда и возможный разворот.

  • Бычья дивергенция возникает, когда цена делает новый минимум, а RSI формирует более высокий минимум. Это указывает на ослабление нисходящего тренда и возможный разворот вверх.

  • Медвежья дивергенция происходит, когда цена делает новый максимум, а RSI формирует более низкий максимум. Это сигнализирует о слабости восходящего тренда и возможном развороте вниз.

Примеры кода для расчета RSI

Python

import pandas as pd

def calculate_rsi(data, period=14):
    delta = data['Close'].diff()
    gain = (delta.where(delta > 0, 0)).fillna(0)
    loss = (-delta.where(delta < 0, 0)).fillna(0)

    avg_gain = gain.rolling(window=period).mean()
    avg_loss = loss.rolling(window=period).mean()

    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

# Example usage
data = pd.DataFrame({
    'Close': [45.15, 46.23, 45.56, 46.33, 45.86, 46.71, 47.24, 46.69, 47.28, 47.88, 48.07, 47.86, 47.33, 47.59]
})

data['RSI'] = calculate_rsi(data)
print(data)

JavaScript

function calculateRSI(closes, period = 14) {
    let gains = [];
    let losses = [];

    for (let i = 1; i < closes.length; i++) {
        let change = closes[i] - closes[i - 1];
        gains.push(change > 0 ? change : 0);
        losses.push(change < 0 ? -change : 0);
    }

    let avgGain = gains.slice(0, period).reduce((a, b) => a + b, 0) / period;
    let avgLoss = losses.slice(0, period).reduce((a, b) => a + b, 0) / period;
    let rs = avgGain / avgLoss;
    let rsi = [100 - (100 / (1 + rs))];

    for (let i = period; i < gains.length; i++) {
        avgGain = (avgGain * (period - 1) + gains[i]) / period;
        avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
        rs = avgGain / avgLoss;
        rsi.push(100 - (100 / (1 + rs)));
    }

    return rsi;
}

// example
let closes = [45.15, 46.23, 45.56, 46.33, 45.86, 46.71, 47.24, 46.69, 47.28, 47.88, 48.07, 47.86, 47.33, 47.59];
console.log(calculateRSI(closes));

R

calculate_rsi <- function(prices, n = 14) {
    deltas <- diff(prices)
    seed <- deltas[1:n]
    up <- seed[seed >= 0]
    down <- -seed[seed < 0]
    up[is.na(up)] <- 0
    down[is.na(down)] <- 0

    avg_gain <- sum(up) / n
    avg_loss <- sum(down) / n
    rs <- avg_gain / avg_loss
    rsi <- 100 - (100 / (1 + rs))

    rsi_values <- c(rep(NA, n), rsi)
    
    for (i in (n+1):length(deltas)) {
        delta <- deltas[i]
        gain <- ifelse(delta > 0, delta, 0)
        loss <- ifelse(delta < 0, -delta, 0)

        avg_gain <- (avg_gain * (n - 1) + gain) / n
        avg_loss <- (avg_loss * (n - 1) + loss) / n
        rs <- avg_gain / avg_loss
        rsi <- 100 - (100 / (1 + rs))

        rsi_values <- c(rsi_values, rsi)
    }

    return(rsi_values)
}

# example
prices <- c(45.15, 46.23, 45.56, 46.33, 45.86, 46.71, 47.24, 46.69, 47.28, 47.88, 48.07, 47.86, 47.33, 47.59)
rsi <- calculate_rsi(prices)
print(rsi)

Стохастический осциллятор

Стохастический осциллятор был разработан Джорджем Лейном в конце 1950-х годов. Это технический индикатор, который измеряет отношение текущей цены закрытия к диапазону цен за определенный период времени. Основная идея стохастического осциллятора заключается в том, что в восходящем тренде цены закрытия обычно находятся ближе к верхней границе диапазона, а в нисходящем тренде — ближе к нижней границе.

Стохастический осциллятор состоит из двух основных компонентов: линии %K и линии %D.

  1. %K линия рассчитывается по формуле

    K=(HN​−LN​)(C−LN​)​×10
    где:

    • C — текущая цена закрытия,

    • L_{N} — самая низкая цена за последние N периодов,

    • H_{N} — самая высокая цена за последние N периодов.

  2. %D линия представляет собой скользящее среднее линии %K за три периода:

    D = \text{SMA}_{3}(K)

По дефолту используют период в 14 дней для %K и 3 дня для %D, но эти параметры могут быть изменены в зависимости от предпочтений.

Значения стохастического осциллятора колеблются от 0 до 100. Уровни выше 80 указывают на перекупленность актива, а уровни ниже 20 — на его перепроданность.

  • Перекупленность: когда осциллятор превышает 80, это может указывать на возможный разворот вниз. Трейдеры используют это как сигнал для продажи.

  • Перепроданность:когда осциллятор падает ниже 20, это может указывать на возможный разворот вверх. Это сигнал для покупки.

Стохастический осциллятор хорош в условиях бокового рынка, где он помогает выявлять краткосрочные колебания. В трендовых рынках осциллятор может оставаться в зоне перекупленности или перепроданности длительное время, что требует доп. индикаторов для подтверждения сигналов.

Дивергенции стохастического осциллятора возникают, когда направление осциллятора не совпадает с направлением цены актива.

Примеры рассчетов в коде

Python

import pandas as pd

def calculate_stochastic_oscillator(data, k_period=14, d_period=3):
    low_min = data['Low'].rolling(window=k_period).min()
    high_max = data['High'].rolling(window=k_period).max()

    data['%K'] = 100 * ((data['Close'] - low_min) / (high_max - low_min))
    data['%D'] = data['%K'].rolling(window=d_period).mean()
    return data

# example
data = pd.DataFrame({
    'High': [127.01, 128.48, 128.43, 128.72, 128.22, 128.43, 128.72, 128.21, 128.35, 128.75, 128.37, 128.57, 128.88, 128.78],
    'Low': [125.36, 126.41, 126.74, 126.82, 126.32, 126.74, 126.82, 126.41, 126.35, 126.82, 126.41, 126.72, 126.92, 126.72],
    'Close': [126.45, 127.98, 127.98, 128.43, 127.38, 127.68, 128.43, 127.88, 127.88, 128.28, 127.78, 128.15, 128.43, 128.23]
})

data = calculate_stochastic_oscillator(data)
print(data[['%K', '%D']])

JavaScript

function calculateStochasticOscillator(highs, lows, closes, kPeriod = 14, dPeriod = 3) {
    let stochastics = [];
    let percentK = [];
    let percentD = [];

    for (let i = 0; i < closes.length; i++) {
        if (i >= kPeriod - 1) {
            let highMax = Math.max(...highs.slice(i - kPeriod + 1, i + 1));
            let lowMin = Math.min(...lows.slice(i - kPeriod + 1, i + 1));
            let k = ((closes[i] - lowMin) / (highMax - lowMin)) * 100;
            percentK.push(k);

            if (percentK.length >= dPeriod) {
                let d = percentK.slice(-dPeriod).reduce((a, b) => a + b) / dPeriod;
                percentD.push(d);
            } else {
                percentD.push(null);
            }
        } else {
            percentK.push(null);
            percentD.push(null);
        }

        stochastics.push({
            K: percentK[percentK.length - 1],
            D: percentD[percentD.length - 1]
        });
    }

    return stochastics;
}

// example
let highs = [127.01, 128.48, 128.43, 128.72, 128.22, 128.43, 128.72, 128.21, 128.35, 128.75, 128.37, 128.57, 128.88, 128.78];
let lows = [125.36, 126.41, 126.74, 126.82, 126.32, 126.74, 126.82, 126.41, 126.35, 126.82, 126.41, 126.72, 126.92, 126.72];
let closes = [126.45, 127.98, 127.98, 128.43, 127.38, 127.68, 128.43, 127.88, 127.88, 128.28, 127.78, 128.15, 128.43, 128.23];

console.log(calculateStochasticOscillator(highs, lows, closes));

R

calculate_stochastic <- function(highs, lows, closes, k_period = 14, d_period = 3) {
    K <- rep(NA, length(closes))
    D <- rep(NA, length(closes))
    
    for (i in seq(k_period, length(closes))) {
        high_max <- max(highs[(i - k_period + 1):i])
        low_min <- min(lows[(i - k_period + 1):i])
        K[i] <- ((closes[i] - low_min) / (high_max - low_min)) * 100
    }
    
    for (i in seq((k_period + d_period - 1), length(closes))) {
        D[i] <- mean(K[(i - d_period + 1):i], na.rm = TRUE)
    }
    
    return(data.frame(K = K, D = D))
}

# example
highs <- c(127.01, 128.48, 128.43, 128.72, 128.22, 128.43, 128.72, 128.21, 128.35, 128.75, 128.37, 128.57, 128.88, 128.78)
lows <- c(125.36, 126.41, 126.74, 126.82, 126.32, 126.74, 126.82, 126.41, 126.35, 126.82, 126.41, 126.72, 126.92, 126.72)
closes <- c(126.45, 127.98, 127.98, 128.43, 127.38, 127.68, 128.43, 127.88, 127.88, 128.28, 127.78, 128.15, 128.43, 128.23)

stochastic <- calculate_stochastic(highs, lows, closes)
print(stochastic)

Оба индикатора могут быть использованы в сочетании с другими инструментами технического анализа для повышения точности сигналов. Например, использование RSI и стохастического осциллятора вместе с трендовыми линиями, скользящими средними или уровнями поддержки и сопротивления часто улучшает торговые стратегии.

Больше инструментов для анализа данных эксперты из OTUS разбирают в рамках практических онлайн-курсов. Подробнее в каталоге.

Комментарии (2)


  1. Format-X22
    23.06.2024 14:31
    +3

    В трендовых рынках осцилляторы помогают находить точки входа в направлении основного тренда, а в боковых — выявлять моменты разворота.

    Осталось только понять где тренд, а где бок. Потому что в зависимости от этого один и тот же сигнал говорит нам о том что надо купить на тренде, но продать если боковик. И при переходе из одного состояния в другое сначала идут потери, а только потом смена стратегии. Но самое веселье начнется когда тренд пошел после боковика, ты это понял, начал работать с индикатором уже как с трендовым… а тренд уже и закончился… и снова череда потерь.

    цены закрытия к диапазону цен за определенный период времени

    Главное научиться этот период определять. И вот ты определил, подобрал размер, посчитал по истории, получил результат, занес денег, и… а рынок изменился и тебе нужен уже другой диапазон. Только поймешь ты это уже после потерь. А если менять стратегию на первую же неудачную сделку, то потери будут ещё больше - метание туда-сюда ломает риск-менеджмент.

    Не существует способов достоверно предсказывать смену трендов и боковиков на основе периодов времени - любое совпадение это временная корелляция. Рынок не подчиняется законам подобранным по истории числам. Чем больше констант - тем ниже точность стратегии. Ну а про «используйте для трендов так, а для боковиков сяк» это вообще из области казино, лудомании и скама. Опасайтесь таких технологий, даже если интуитивно кажется что оно работает. Это временные корелляции.


    1. iShrimp
      23.06.2024 14:31
      +3

      Граница между закономерностью и шумом очень тонка. Если дать трейдеру для анализа график случайного блуждания, то он сможет в нём найти буквально всё - тренды, коридоры, пики, линии поддержки и т.д. Он может попытаться "спрогнозировать" дальнейшее поведение графика. Но мы-то знаем, что грош цена этим предсказаниям, а он - нет. Все выявленные им предикторы являются случайностью, шумом. Так же и в реальных рыночных графиках бо́льшая часть предикторов - шум.