Finance in IT
Lifehacks for geeks
The future is here
28 November 2019

Практические аспекты ранней пенсии FIRE movement в России

From Sandbox

Ранний выход на пенсию является горячей темой в последнее время. Отчасти это связано с движением FIRE: «финансовая независимость и ранняя пенсия» — зарабатывать больше, тратить меньше и рано сбросить оковы работы.



Казалось бы причем здесь FIRE и habr.com? А при том, что невозможно добиться этой ранней пенсии НЕ имея и НЕ учитывая собственных диверсифицированных активов. А как показала многолетняя практика адекватных инструментов для учета акций, облигаций, недвижимости, монет и валют вкупе со страновой диверсификацией в одном окне практически нет. В небольшой предыстории рассказывал, что если у Вас есть, что учитывать, то сам учёт может занимать неоправданно много времени. Но тут на помощь приходит парсинг сайтов и это выводит управленческий учет собственного портфеля на новый уровень и, конечно же, приближает раннюю пенсию.


Важное замечание: эта статья для начинающего раннего пенсионера и новичка-парсера :). Мой опыт подразумевает использование данных с сайтов только для личных целей. Коснусь использования Microsoft Excel и Google Таблиц и совсем чуть-чуть KMyMoney. Начну в том порядке, в котором я сам столкнулся со всеми продуктами:


Часть 1. KMyMoney


Добавление нового актива в KMyMoney
Добавление нового актива в KMyMoney


Об этой программе незаслуженно мало информации на просторах интернета и особенно мало про её компонент парсинга цен. А ведь в инвестициях очень важно иметь актуальную картину перед глазами.


Настройка парсера в KMyMoney
Настройка парсера в KMyMoney


Также любопытно, что из версии в версию KMyMoney (я пользовался программой несколько лет) в настройках парсера кочуют какие-то неработающие ссылки, которыми практически невозможно воспользоваться и нет какого-то внятного описания компонента.


Указание использовать шаблон парсинга для конкретной бумаги
Указание использовать шаблон парсинга для конкретной бумаги


Предлагаю для примера какую-нибудь облигацию федерального займа (ОФЗ) — это рублёвые облигации, выпускаемые Министерством финансов Российской Федерации. Данные по этим облигациям можно посмотреть на множестве сайтов или через API Московской Биржи. Возьму ОФЗ-ПК 29012 (SU29012RMFS0), а цену буду смотреть на сайте одного из брокеров. Идентификатором станет не код ценной бумаги с биржи, а внутренний идентификатор с сайта. Так выглядит окно с уже скаченной котировкой предыдущего дня:


Результат работы парсера для бумаги
Результат работы парсера для бумаги


В свое время составил подобные шаблоны для парсинга с помощью KMyMoney под множество активов для разных сайтов — надеюсь это будет полезно.


Конечно, KMyMoney позволяет иметь автоматические обновление курсов любых активов и это наверное единственная программа которая позволяет это делать, но как и у всех подобных учетных программ у неё есть определенные недостатки. А именно: раздражающий “бухгалтерский поиск” не сходящихся копеек (мне нужен обзорный инструмент) и огромное количество кликанья мышкой (в два клика ничего не сделать с инвестицией). Но эта программа наверное лучшее, если Вы хотите иметь учет личных финансов и инвестиций в одном окне и при этом иметь автоматические обновление курсов любых активов.


Часть 2. Microsoft Excel


Встроенных функций для парсинга в Excel нет и поэтому пользовался VBA, на основе найденных в интернете примеров (сам не особо разбирался в то время, чтобы написать с нуля) — код вероятно не самый лаконичный, но свои функции выполнял:


Редактор кода в Microsoft Excel
Редактор кода в Microsoft Excel


VBA код для парсинга цен облигаций федерального займа.
Public Function FinamPriceBondsCorporate(Optional ByVal ISIN) As Double
    '//объявляем переменные
    Dim sURI As String
    Dim oHttp As Object
    Dim htmlcode, outstr As String
    Dim Num As Double

    '//обращаемся к сайту
    sURI = "https://bonds.finam.ru/issue/" & ISIN & "/default.asp?resultsType=5"
    On Error Resume Next
    Set oHttp = CreateObject("MSXML2.XMLHTTP")
    If Err.Number <> 0 Then
        Set oHttp = CreateObject("MSXML.XMLHTTPRequest")
    End If
    On Error GoTo 0
    If oHttp Is Nothing Then
        Exit Function
    End If
    oHttp.Open "GET", sURI, False
    oHttp.Send
    htmlcode = oHttp.responseText

    '//попробовать regex!
    outstr = Mid(htmlcode, InStr(1, htmlcode, "Сlose:</td>") + 57, 6) '//ищем значение в тексте
    Set oHttp = Nothing

    '//удаляем куски html разметки, если нужно
    outstr = Replace(outstr, "&", "")
    outstr = Replace(outstr, "n", "")
    outstr = Replace(outstr, "b", "")
    outstr = Replace(outstr, "s", "")
    outstr = Replace(outstr, "p", "")
    outstr = Replace(outstr, ";", "")
    outstr = Replace(outstr, "-", "0")
    Num = CDbl(outstr) '//число в текст

    If Num = 0 Then  '//если нет цены закрытия берем другую
    outstr = Mid(htmlcode, InStr(1, htmlcode, "Bid:</td>") + 54, 6) '//ищем значение в тексте
    outstr = Replace(outstr, "&", "")
    outstr = Replace(outstr, "n", "")
    outstr = Replace(outstr, "b", "")
    outstr = Replace(outstr, "s", "")
    outstr = Replace(outstr, "p", "")
    outstr = Replace(outstr, ";", "")
    outstr = Replace(outstr, "-", "0")
    Num = CDbl(outstr) '//число в текст
    End If

    FinamPriceBondsCorporate = Num

    Exit Function
ErrorHandler: 'Обработчик ошибок
    FinamPriceBondsCorporate = 0
    Err.Clear
End Function

Для того, чтобы воспользоваться кодом, в самом Excel надо вызвать эту, только что написанную функцию FinamPriceBondsCorporate:


Excel и использование дополнительной функции парсинга
Excel и использование дополнительной функции парсинга


Таблица в Excel (и учётный шаблон на её основе) оставалась удобным локальным вариантом, если хотел открыть её и что-то в ней посмотреть. Но котировки активов не могли обновляться без участия человека и уж тем более эта таблица не могла утром в понедельник послать отчет и еще создать мероприятия в календаре о выплатах на неделю вперед. В общем, пришлось двинуться дальше.


Часть 3. Google Таблицы


Котировки через IMPORTXML


Google Таблицы как оказалось представляют гораздо более широкий диапазон возможностей для парсинга. И первое с чем столкнулся — это была функция IMPORTXML, которая позволяла взять почти любую строку из веб-страницы. А для использования надо только было научиться писать xpath запрос. С ее помощью можно собирать нужные котировки и другую информацию без скриптов и интеграции с API.



Мой шаблон отчетной таблицы, которому дал название SilverFir: Investment Report


Примерно за полгода изысканий в 2018 году родилась окончательная версия моего шаблона, который стал отправной точкой для взрослого учёта диверсифицированных активов “в одном окне”.


Распределение активов в отчетной таблице SilverFir: Investment Report
Распределение активов в отчетной таблице SilverFir: Investment Report


К сожалению, когда в Таблице использовалось множество IMPORTXML приходилось слишком долго ждать загрузки всех результатов, а также обнаружилась невозможность использования данных полученных через IMPORTXML по расписанию в гугл скриптах (потому что они могли просто не успеть подгрузиться к моменту их использования).


Котировки через Google Apps Script


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


Google Apps Script приходит на помощь в FIRE movement
Google Apps Script приходит на помощь в FIRE movement


Поскольку всегда считал, что FIRE movement — это свобода заниматься тем что нравится, а не бухгалтерский поиск копеек и уж конечно не копипаста котировок с разных сайтов. Структура и компоновка отчетной таблицы получилась очень удачная (это отмечали и другие люди), а вот метод получения котировок пришлось опять менять.


На гугл скриптах парсинг, на мой взгляд, выглядит лаконичнее, чем на VBA.


Google Apps Script для парсинга имени и котировки Pitney-Bowes-Anleihe (3,875% до 01.10.2021) с ISIN US724479AK60 и подобных.
function GetFinanzenNet(ISIN, id) {
    var url = 'https://www.finanzen.net/suchergebnis.asp?_search=' + ISIN;
    try {
        var html = UrlFetchApp.fetch(url).getContentText();
    } catch (error) {
        Logger.log("GetFinanzenNet. Symbol = " + ISIN + ".\nОшибка чтения данных. URL: " + url + ". HTML:\n" + html);
        return ""
    }
    if (id == "Price") { //цена акции - Quote/Price
        var searchstring = 'text-sm-right text-nowrap">';
        var index = html.search(searchstring);
        if (index >= 0) {
            var pos = index + searchstring.length
            var rate = html.substring(pos, pos + 50);
            rate = rate.split('<')[0];
            rate = rate.replace(/\,/g, ".");
            rate = rate.replace(/\%/g, "");
            rate = +rate;
            Logger.log("GetFinanzenNet. ID = " + id + ".\nPrice = " + rate + ". URL: " + url);
            return rate
        }
    }
    if (id == "Name") { //имя
        var searchstring5 = '<title>';
        var index5 = html.search(searchstring5);
        if (index5 >= 0) {
            var pos5 = index5 + searchstring5.length
            var rate5 = html.substring(pos5, pos5 + 100);
            try {
                rate5 = rate5.split(' | ')[1];
                rate5 = rate5.split(' | ')[0];
                var res = rate5.substring(0, 1); //первый символ
                if (res == "(") {
                    rate5 = html.substring(pos5, pos5 + 100);
                    rate5 = rate5.split(' | ')[0];
                    rate5 = rate5.substring(0, 18);
                }
            } catch (error) {
                return "";
            }
            Logger.log("GetFinanzenNet. Найден Name для Symbol " + ISIN + ".\nName = " + rate5 + ". URL: " + url);
            return rate5
        }
    }
    Logger.log("GetFinanzenNet. Symbol = " + ISIN + ".\nОшибка чтения данных. URL: " + url);
    return ""
}

Уже тогда стало понятно, что можно очень сильно облегчить себе весь учет, указывая только начальную сумму, количество и дату покупки актива, а все остальные параметры (включая название) скачивать с других (разных) сайтов.


Экосистема


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


Вот функции, которые можно реализовать на скриптах.
  • еженедельные отчёты на почту;


Пример фрагмента отчета на почте


  • события с выплатами в календаре;


Пример тестового события в Google Календарь


  • задачи посмотреть, что не так с теми или иными бумагами;


Пример Google Задач


  • ежемесячная презентация с итогами месяца;


Пример Google Презентации с изменениями на скриптах


  • Asset Allocation на основе данных с множества сайтов;


Промежуточные шаги для расчета диверсификации: типы и страны


  • дополнительные диаграммы и графики, которых нет в Таблицах;


Пример сгенерированного Word Trees Charts — очень наглядно раскидать собственный портфель (на скриншоте НЕ он) по валютам и классам активов.


  • напоминания об офертах и выплатах в телеграм;


Бот с напоминаниями


  • импорт данных от брокеров (у Российских брокеров как-то мне показалось сложно с этим, поскольку у каждого брокера свое видение отчёта, причем в большинстве случаев для инвестора недружественное).

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


Активом при этом может выступать что угодно: облигация, акция, физическая недвижимость, монеты, вклады — нет какого-то ограничителя и есть созданный шаблон, который можно кастомизировать под любой конкретный запрос.


Итог


Еще раз возвращаясь к практическим аспектам ранней пенсии FIRE movement в России — учёт имеет к ранней пенсии самое прямое отношение и именно грамотно поставленный учет позволяет держать руку на пульсе, в то же самое время не скатываясь до частого просмотра котировок и текущих цен.


Автор: Михаил Шардин,
25 ноября 2019 г.

Only registered users can participate in poll.Log in, please.
Опрос №1. Прямо сейчас у Вас есть инвестиции?
53.85% Да 21
46.15% Нет 18
39 users voted. 5 users abstained.
Опрос №2. Как Вы думаете, какой из вариантов выглядит оптимальным для многолетнего использования и учета всех инвестиций в одном окне?
26.92% Онлайн-сервис [нашёл множество отличных сервисов, но все они локальные для конкретных рынков и ни в одном нельзя добавить что-то своё (в первую очередь страдает недвижимость)] 7
53.85% Локальная программа 14
19.23% Программа табличных данных 5
26 users voted. 11 users abstained.

+7
3.9k 35
Comments 20