Изучение изменения стоимости валют по данным ЦБ
Материал из Поле цифровой дидактики
Описание проекта
Анализ валютного рынка ЦБ РФ на основе 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-дневная динамика
Алгоритм:
- загрузка архивных данных ЦБ
- формирование временного ряда
- расчёт накопленного изменения
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)
Результаты


