Изучение изменения стоимости валют по данным ЦБ

Материал из Поле цифровой дидактики

Описание проекта

Анализ валютного рынка ЦБ РФ на основе R. Построение дашборда: дневные изменения, 30-дневная динамика и волатильность валют.

Назначение

Проект позволяет оценить характер изменения стоимости валют по данным ЦБ.


Общая концепция

Валютный дашборд ЦБ РФ (R + анализ)

Цель исследования

Проект направлен на анализ валютного рынка:

  • дневные изменения валют
  • динамика за 30 дней
  • волатильность валют
  • сравнение мировых и остальных валют

Источник данных

Данные берутся из API ЦБ РФ:

https://www.cbr-xml-daily.ru/daily_json.js

Исторические данные:

https://www.cbr-xml-daily.ru/archive/YYYY/MM/DD/daily_json.js

Архитектура обработки данных

📊 Структура данных

Загрузка и подготовка данных

library(httr)
library(jsonlite)
library(ggplot2)
library(patchwork)

options(encoding = "UTF-8")

url <- "https://www.cbr-xml-daily.ru/daily_json.js"
data <- fromJSON(content(GET(url), "text"))

valutes <- data$Valute

df <- data.frame(
  CharCode = sapply(valutes, function(x) x$CharCode),
  Value    = as.numeric(sapply(valutes, function(x) x$Value)),
  Previous = as.numeric(sapply(valutes, function(x) x$Previous))
)

df <- df[complete.cases(df), ]
df$change_pct <- (df$Value - df$Previous) / df$Previous * 100
df <- df[is.finite(df$change_pct), ]

Сегментация валют

Мировые валюты:

popular <- c("USD","EUR","GBP","JPY","CNY",
             "CHF","CAD","AUD","NZD","SEK")

Логика анализа

Дневной анализ

title Группы валют

rectangle "Мировые" as world
rectangle "Рост" as up
rectangle "Падение" as down

world --> "Сравнительный анализ"
up --> "Топ роста"
down --> "Топ падения"

30-дневная динамика

Алгоритм:

  1. загрузка архивных данных ЦБ
  2. формирование временного ряда
  3. расчёт накопленного изменения
change_30d_pct = (Value / first(Value) - 1) * 100

Волатильность

Формула:

[math]\displaystyle{ Volatility = sd(\log(Value)) \times 100 }[/math]

Визуализация системы

Основной график

  • дневные изменения валют
  • цветовая группировка: рост / падение / мировые

Волатильность

  • TOP-10 наиболее нестабильных валют

Итоговый дашборд

final_plot <- (plot_main | plot_vol) /
              (plot_growth | plot_month)

ggsave("currency_dashboard_full.png",
       final_plot,
       width = 14,
       height = 10)

Вывод

Проект демонстрирует:

  • работу с внешними API
  • обработку временных рядов
  • построение аналитических дашбордов в R
  • визуальную аналитику финансовых данных


Код на R

library(httr)
library(jsonlite)
library(ggplot2)
library(patchwork)

options(encoding = "UTF-8")

url <- "https://www.cbr-xml-daily.ru/daily_json.js"
data <- fromJSON(content(GET(url), "text"))

valutes <- data$Valute  # Извлекаем список валют из полученного объекта

df <- data.frame(  # Создаём таблицу
  CharCode = sapply(valutes, function(x) x$CharCode),  # Код валюты (USD, EUR и т.д.)
  Value    = as.numeric(sapply(valutes, function(x) x$Value)),  # Текущий курс валюты
  Previous = as.numeric(sapply(valutes, function(x) x$Previous))  # Предыдущий курс валюты
)

df <- df[complete.cases(df), ]  # Удаляем строки с пропущенными значениями

df$change_pct <- (df$Value - df$Previous) / df$Previous * 100  # Считаем процентное изменение курса валют
df <- df[is.finite(df$change_pct), ]  # Удаляем бесконечные значения

popular <- c("USD","EUR","GBP","JPY","CNY", # Список основных валют для отдельного анализа
             "CHF","CAD","AUD","NZD","SEK")

df_world <- df[df$CharCode %in% popular, ]  # Фильтруем только мировые валюты
df_world$group <- "Мировые"  # Добавляем метку группы

df_top <- df[!df$CharCode %in% popular, ] # Оставляем все валюты не из списка

df_up <- df_top[df_top$change_pct > 0, ]  # Фильтруем валюты, которые выросли
df_up <- df_up[order(df_up$change_pct, decreasing = TRUE), ] # Сортируем по убыванию роста
df_up <- head(df_up, 10) # Берём топ-10 валют роста
df_up$group <- "Рост"

df_down <- df_top[df_top$change_pct < 0, ]  # Фильтруем валюты, которые упали
df_down <- df_down[order(df_down$change_pct), ]  # Сортируем по сильнейшему падению
df_down <- head(df_down, 10)  # Берём топ-10 падений
df_down$group <- "Падение"

df_final <- rbind(df_world, df_up, df_down)  # Объединяем мировые + рост + падение в одну таблицу

# График за день
plot_main <- ggplot(df_final, aes(
  x = reorder(CharCode, change_pct),  # Сортируем валюты по изменению
  y = change_pct,  # Значение по оси Y (% изменения)
  fill = group  # Цвет по группе
)) +

  geom_col() +  # Столбчатый график

  geom_text(  # Подписи значений на столбцах
    aes(label = paste0(round(change_pct, 2), "%")),
    vjust = -0.5,
    size = 3
  ) +

  scale_fill_manual(values = c(  # Цвета для групп
    "Мировые" = "gray60",
    "Рост" = "green3",
    "Падение" = "red3"
  )) +

  theme_minimal() +

  labs(    # Подписи графика
    title = "Валютный рынок ЦБ РФ",
    x = "Валюта",
    y = "Изменение (%)",
    fill = "Группа"
  ) +

  expand_limits(y = max(df_final$change_pct) * 1.2)  # Запас по оси Y, чтобы подписи не обрезались


currencies <- c("USD", "EUR", "CNY", "JPY", "GBP")  # Список валют для анализа месяца
dates <- seq(Sys.Date() - 30, Sys.Date(), by = "day")  # Создаём список дат за последние 30 дней

get_day_data <- function(date) {  # Функция загрузки данных за конкретный день
  url <- paste0(
    "https://www.cbr-xml-daily.ru/archive/",
    format(date, "%Y/%m/%d"),
    "/daily_json.js"
  )

  tryCatch({
    data <- fromJSON(content(GET(url), "text")) # Загружаем данные дня

    data.frame(  # Создаём таблицу по выбранным валютам
      date = as.Date(date),
      CharCode = currencies,
      Value = sapply(currencies, function(x) data$Valute[[x]]$Value)  # Берём значение каждой валюты
    )
  }, error = function(e) NULL)  # Если ошибка
}

list_data <- lapply(dates, get_day_data)  # Получаем данные за все дни
list_data <- Filter(Negate(is.null), list_data)  # Удаляем пустые результаты

df_month <- do.call(rbind, list_data)  # объединяем в один график
df_month <- df_month[complete.cases(df_month), ]  # удаляем ненайденное

df_month <- df_month %>%
  group_by(CharCode) %>%
  arrange(date) %>%
  mutate(
    change_30d_pct = (Value / first(Value) - 1) * 100
  ) %>%
  ungroup()

plot_growth <- ggplot(df_month, aes(
  x = date,
  y = change_30d_pct,
  color = CharCode
)) +
  geom_line(linewidth = 1) +
  theme_minimal() +
  labs(
    title = "Накопленное изменение валют за 30 дней (%)",
    x = "Дата",
    y = "% изменения",
    color = "Валюта"
  )

get_all_currencies <- function(date) {

  url <- paste0(
    "https://www.cbr-xml-daily.ru/archive/",
    format(date, "%Y/%m/%d"),
    "/daily_json.js"
  )

  tryCatch({
    data <- fromJSON(content(GET(url), "text"))
    val <- data$Valute

    data.frame(
      date = as.Date(date),
      CharCode = names(val),
      Value = sapply(val, function(x) x$Value)
    )
  }, error = function(e) NULL)
}

list_all <- lapply(dates, get_all_currencies)
list_all <- Filter(Negate(is.null), list_all)

df_all <- do.call(rbind, list_all)
df_all <- df_all[complete.cases(df_all), ]

volatility <- df_all %>%
  group_by(CharCode) %>%
  summarise(
    mean_value = mean(Value, na.rm = TRUE),
    sd_value = sd(Value, na.rm = TRUE),
    volatility = sd(log(Value), na.rm = TRUE) * 100
  ) %>%
  arrange(desc(volatility)) %>%
  slice_head(n = 10)

plot_vol <- ggplot(volatility, aes(
  x = reorder(CharCode, volatility),
  y = volatility,
  fill = CharCode
)) +
  geom_col(width = 0.7) +
  
  geom_text(
    aes(label = round(volatility, 2)),
    hjust = -0.2,
    size = 3.5
  ) +
  
  coord_flip() +

scale_y_continuous(expand = expansion(mult = c(0, 0.25))) +
  
  theme_minimal() +
  theme(
    legend.position = "none"
  ) +
  
  labs(
    title = "ТОП-10 самых волатильных валют (30 дней)",
    x = "Валюта",
    y = "Волатильность (%)"
  )


# График за месяц
plot_month <- ggplot(df_month, aes(
  x = date,  # Ось X — дата
  y = Value,    # Ось Y — курс валюты
  color = CharCode,  # Цвет линий по валюте
  group = CharCode  # Группировка линий
)) +

  geom_line(linewidth = 1) + # Линия графика
  geom_point(size = 1.5) + # Точки на линии

  theme_minimal() +

  labs(  # Подписи
    title = "Динамика валют за 30 дней",
    x = "Дата",
    y = "Курс к RUB",
    color = "Валюта"
  )

# Объединение графиков
final_plot <- (plot_main | plot_vol) /
              (plot_growth| plot_month)

print(final_plot)  # Вывод на экран

ggsave("currency_dashboard_full.png",  # Сохранение в PNG файл
       final_plot,
       width = 14,
       height = 10)

Результаты