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

Когда я в первый раз разбирался, как тянуть котировки в Google Sheets, вокруг были сплошные IMPORTXML, XPath, парсинг HTML и прочая боль. Хотелось простого сценария: в ячейку передал тикер — получил актуальную цену с Мосбиржи. Тогда я собрал скрипт, который ходил напрямую в API MOEX из Google Apps Script, и всё это нормально жило какое‑то время.

С тех пор многое изменилось:

  • GOOGLEFINANCE по Мосбирже по‑прежнему мёртв.

  • Мосбиржа закрыла доступ к своему API с IP‑адресов Google (в том числе из Google Sheets / Apps Script).

  • Напрямую стучаться из таблиц в MOEX теперь нельзя — получаем ошибки/блокировку.

Поэтому я обновил решение:

поднял небольшой прокси‑сервер поверх официального ISS API Мосбиржи и переписал скрипт для таблиц, сделав его более универсальным — одна функция для ОФЗ, акций и фондов.

Как работает функиция
Как работает функиция

Что теперь происходит под капотом

Схема стала такой:

Google Таблица вызывает мою кастомную функцию, например:

   =GET_PRICE("OFZ"; "SU26238RMFS9")

   =GET_PRICE("STOCK"; "SBER")

   =GET_PRICE("FUND"; "FXRL")

Google Apps Script делает HTTP‑запрос не напрямую в MOEX, а на мой небольшой backend (прокси).

Прокси‑сервер (у меня это FastAPI + httpx):
— ходит в официальное ISS API Мосбиржи;
— аккуратно парсит довольно нетривольный JSON;
— учитывает, что биржа может быть закрыта и приходится брать последнюю доступную цену из истории/статистики;
— кеширует ответ на 10 минут в файле, чтобы не душить API и не упираться в лимиты.

В ответ отдает компактный JSON:

   { "ticker": "SBER", "price": 279.53 }

Google Таблица забирает поле price и подставляет его в ячейку.

Эндпоинты прокси

На стороне сервера у меня три ручки (все GET):

  • GET /bond/{ticker} — ОФЗ / облигации

  • GET /stock/{ticker} — акции

  • GET /fund/{ticker} — фонды / ETF / БПИФы


Универсальная функция для Google Sheets

Теперь вместо трёх разных функций под каждый тип активов достаточно одной — GET_PRICE(type, ticker).

В Google Таблице заходите в Расширения → Apps Script, создаёте скрипт и вставляете туда:

function GET_PRICE(type, ticker) { if (!type || !ticker) { throw new Error('Нужно указать тип и тикер: GET_PRICE(type, ticker)'); }

var typeStr = String(type).trim().toLowerCase();
var tickerStr = String(ticker).trim().toUpperCase();

function GET_PRICE(type, ticker) { if (!type !ticker) { throw new Error('Нужно указать тип и тикер: GET_PRICE(type, ticker)'); } var typeStr = String(type).trim().toLowerCase(); var tickerStr = String(ticker).trim().toUpperCase(); var path; if (typeStr === 'ofz' typeStr === 'bond') { path = 'bond'; } else if (typeStr === 'stock' typeStr === 'акция' typeStr === 'акции') { path = 'stock'; } else if (typeStr === 'fund' typeStr === 'фонд' typeStr === 'фонды') { path = 'fund'; } else { throw new Error('Неизвестный тип актива: ' + type + '. Ожидаю OFZ/STOCK/FUND (или bond/stock/fund).'); } var url = 'https://exchange.technoquant.ru/' + path + '/' + encodeURIComponent(tickerStr); var response = UrlFetchApp.fetch(url, { method: 'get', muteHttpExceptions: true }); var code = response.getResponseCode(); var text = response.getContentText(); if (code !== 200) { // Пробрасываем сообщение из твоего backend, если там HTTPException с detail try { var err = JSON.parse(text); if (err && err.detail) { throw new Error('Backend ' + code + ': ' + JSON.stringify(err.detail)); } } catch (e) { // не JSON — просто кидаем текст } throw new Error('Backend error: HTTP ' + code + ' — ' + text); } var data; try { data = JSON.parse(text); } catch (e) { throw new Error('Неверный JSON от backend: ' + text); } if (!data || typeof data.price !== 'number') { throw new Error('Неверный формат ответа (ожидаю {"price": number}): ' + text); } return data.price; }

Почему без прокси теперь никак

Раньше всё это можно было делать напрямую из Apps Script в API Мосбиржи. Сейчас:

  • IP‑пулы Google, с которых идут запросы из Google Sheets / Apps Script,

Мосбиржа фактически прикрыла.

  • В результате таблица напрямую в MOEX либо не достучится, либо упирается в блоки/ограничения.

Поэтому единственный нормальный вариант:

  • Либо держать свой прокси‑сервер, который:

  • живёт на «обычном» сервере с «нормальным» IP,

  • ходит в MOEX,

  • возвращает тебе аккуратный JSON.

  • Либо использовать какой‑то сторонний платный сервис‑агрегатор.

Я выбрал первый путь — поднял компактный FastAPI‑сервис, который:

  • знает три типа активов: ОФЗ, акции, фонды;

  • вытаскивает цену из всех нужных блоков ISS API;

  • кеширует ответы, чтобы не DDoSить биржу;

  • отдаёт простой { ticker, price }, с которым удобно работать в таблицах.