Ранний выход на пенсию является горячей темой в последнее время. Отчасти это связано с движением 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 Задач


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


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


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


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


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


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


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


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


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

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


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


Итог


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


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

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


  1. vazerdim
    28.11.2019 17:52

    Регулярно инвестирую средства, учет личных финансов начал ввести еще с 2013 года, введу все до копейки по статьям расходов просто в Excel таблице. Соответственно там же подгружаются и актуальные цены макросом по моим активам (у меня сделано просто по кнопке) и вся аналитика. Времени занимает не много это, так как в жизни я минималист. В планах также есть, выйти на пенсию в 50 лет.

    Разным сервисам с одной стороны не доверяю, если завтра закроется, то будет больно)


    1. empenoso Автор
      29.11.2019 06:02

      А как у Вас макрос организован — с одного или с разных сайтов скачивает?


      1. vazerdim
        01.12.2019 23:05

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


    1. vis_inet
      01.12.2019 17:20

      Во что инвестируете, можете рассказать?


      1. empenoso Автор
        01.12.2019 18:11

        В разное, тут индивидуально.


      1. vazerdim
        01.12.2019 23:14

        В основном World Large-cap, но также в портфеле есть и фонды облигаций. Также я иногда вкладываю средства в те компании, продуктами которых я сам пользуюсь в жизни, это в основном из сегмента high-quality, где сам бренд играет роль, но также есть варианты инвестиций, где можно получить налоговые вычеты, там тоже есть иногда интересные возможности.


  1. faoriu
    28.11.2019 20:01

    А Bloomberg Terminal чем не устраивает?


    1. empenoso Автор
      29.11.2019 06:01

      Это сильно для профессионалов, а я пишу с точки зрения частного инвестора. Лично у меня, например, тоже нет доступа к Блумбергскому терминалу


  1. VGusev2007
    29.11.2019 10:51

    Михаил, добрый день! Вопрос очень важный имею: Вам удалось решить вопрос с учетом доходностей по облигациям с амортизацией долга (в идеале в KMyMoney)? Я так и не смог понять даже примерно как это можно реализовать. Чтобы было в локальной программе. Почти все муниципальные бумаги с амортизацией долга…


    1. empenoso Автор
      29.11.2019 11:14

      Это сложный вопрос, но на гугл скриптах вроде удалось сделать — дело в том, что и в отчете брокера это не всегда это корректно отображается :(
      Вы ведь про отображение выплат номинала бумаги и корректировки начальной стоимости покупки спрашиваете?


      1. VGusev2007
        29.11.2019 14:49

        Вы ведь про отображение выплат номинала бумаги и корректировки начальной стоимости покупки спрашиваете?


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


        1. empenoso Автор
          29.11.2019 15:41

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


          1. VGusev2007
            29.11.2019 16:07

            В общем, это самый сложный и достойный решения вопрос. ИМХО, без решения сложных вопросов с облигациями, говорить о реально годном ПО, пока преждевременно. Был товарищ, который написал конфигурацию 1С с этим функционалом, но он забросил поддержку этого решения.


            1. empenoso Автор
              29.11.2019 16:17

              Ну в статье описываю опыт для частного лица и БЕЗ бухгалтерского учета. Именно управленческий — то есть общая картина и легкость управления данными. А также предстоящие выплаты — очень важно для бюджета — это тоже есть.
              А выплаты номинала не так уж и сложно учесть — если это на самом деле необходимо — такой функционал можно сделать.


  1. zhulan0v
    30.11.2019 12:21

    Тема интересная, у самого есть небольшие инвестиции и учёт расходов.

    Самые действенные меры, которые прокачали мое финансовое состояние:
    — Ограничить ежемесячные расходы, например, максимум 50к или 100к (ваш комфортный уровень) и привыкнуть жить на эту сумму. При этом если ваш доход вырос, то сумма расходов не должна расти, т.е. вы просто начинаете больше направлять на инвестиции.
    — Учёт расходов, сейчас это можно делать на 99% в автоматическом режиме, если не юзать нал.
    — Бюджет, хотя бы крупными мазками, приучает мыслить глобально и лучше понимать свои возможности.

    К этим мерам я пришёл не сразу. Сначала просто стало интересно куда уходят деньги и можно ли наладить автоматизированный учет. Протестировал месяц, второй и привык. Проанализировал расходы, что-то сократил и высвобожденные средства решил направлять в копилку. Как-то так начал вырабатываться осознанный подход к тратам, начал смотреть на ценники, думать действительно ли мне что-то нужно и т.д. Дальше появилось планирование крупных трат и на этой почве простенький бюджет начал складываться сам собой.

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

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

    Так что мой совет тем, кто только вступает на эту дорогу: не торопитесь внедрить всё и сразу, начните с малого и всё само сложится если вы действительно этого хотите.

    Инвестиции учитываю в intelinvest (рефка), в целом пока устраивает, сервис развивается, кое чего не хватает по мелочи, но не критично. Если кому надо промокод, то пишите. Пробовал юзать гугл таблицы, но на моем уровне с ними больше гемороя, чем профита.

    Учет расходов и ведение бюджета делаю в zenmoney. Как и везде есть небольшие недочёты, но меня устраивает. Для работы с бюджетом юзаю веб-версию, т.к. на большом экране с ним удобней работать. В приложение захожу супер редко, в основном категорию поправить, если что-то неправильно проставилось автоматически.


    1. empenoso Автор
      01.12.2019 12:58

      Спасибо за Ваш комментарий zhulan0v!