С чего всё началось
Начнём с дисклеймера. В тексте ниже не имеются в виду и не подразумеваются никакие крупные ИТ-компании, телеком-операторы, банки, брокеры и прочие конкретные юридические лица. Поговорим просто о каком-то крупном бизнесе, который инвестирует некоторые средства в развитие новых сервисов и направлений в рамках собственного акселератора под кодовым названием "Гараж".
Если вам интересно почитать только о самом прототипе и технике, то первые два раздела можно пропустить.
Когда-то давно я познакомился с рынком ценных бумаг и довольно быстро понял, что свои сделки всегда надо записывать. С чего я это взял? Ну во-первых о необходимости записывать что и зачем я купил на бирже и раньше говорили буквально из каждого утюга, в каждом первом пособии по торговле. Во-вторых когда теряешь кровные начинаешь очень быстро соображать, искать причины и следствия, анализировать в чём был не прав. Особенно, если желание торговать ценными бумагами долго вызревало и ты не относишься к заработку на бирже как к лёгким деньгам вроде казино. Когда ты изучаешь фундаментальный анализ, финансовые рынки, теоретические предпосылки рыночной экономики и тому подобное. В общем появилась экселька в которой я вёл реестр своих сделок с акциями и облигациями. Со временем эта табличка разрослась до сотен строк, десятка листов с вспомогательными табличками, а когда типов активов стало больше двух - до пяти экселек в каждой из которых тоже были листы и таблички на разные темы. Разобраться во всём этом массиве ценной информации уже стало мало возможно и я начал скажем так немного подзабивать на ведение этих реестров. Что тут же сказалось на качестве портфеля и его доходности. Я не мог сказать в каждый момент времени сколько у меня активов и сколько они мне принесли денег на сейчас. Возникла идея сервиса - Блокнота инвестора, который мог бы собрать все данные о моих инвестициях в кучу и выдать нужную мне информацию в любой момент времени. История началась.
Акселератор, который заставил воплотить мою идею в реальность или как стать стартапером за чужой счёт
Прошло примерно года три. Когда я работал в одной крупной компании у которой есть некоторое количество полезных и прибыльных сервисов, а также собственный акселератор, возникло желание вернуться к моей идее и попробовать таки воплотить её в жизнь. Тем более, что это было несложно. Требовалось лишь описать свою идею словами и попросить помощи "Гаража" сформулировав, что же конкретно мы от него хотим получить.
Опустим долгие прелюдии, касдевы и проверки гипотез по их результатам, неудачные опыты по нефункциональному прототипированию сервиса. Я не знаю какой продуктовый гений может на одних касдевах проверить будет ли новый сервис востребован и принесёт ли он прибыль. В какой-то момент нам пришло понимание, что без полноценного функционального прототипа никакую гипотезу мы не проверим и вряд ли сможем предложить крупной компании что-то стоящее в качестве инвестиционного проекта.
Но как сделать прототип? Самое очевидное было вооружиться давними знаниями PHP, JS, SQL, всяких там докеров и тому подобного. Но стоп. Откуда взять время на всё это, если через неделю трекшн-митинг на котором нужно показать результат, а "традиционными методами" результата может не быть неделями, месяцами, если не годами. Нужно было простое и быстрое решение нашей задачи. которое помогло бы нам сразу начать проверять гипотезы.
Как мы выбрали Glide и что было потом
Требования к решению были достаточно простые: облачный сервис, авторизованная зона, возможность привязки аккаунта к Telegram-боту для импорта сделок и коммуникации с пользователем, каталог сделок с возможностью ручного добавления, некий агрегированный "портфель" для каждого пользователя, объединяющий все сделки по эмитентам и типам активов, самая простая визуализация цифр портфеля, изначально мобильная версия. Прочие особенности появились уже в ходе реализации и отдельно я их описывать не буду.
Альтернатив у нас было несколько:
Уже упомянутый сервис написанный "с нуля". Изначально это показалось нам плохой идеей, поскольку тестировать гипотезы нужно реально быстро, иначе ещё быстрее вылетишь из акселератора и твой проект на этом останется только твоим
Простая CMS типа старой доброй, не раз послужившей S.Builder или Wordpress - позволила бы быстро сделать каталог сделок и их отображение. Работа в мобильном режиме была под вопросом, поскольку это адаптивная вёрстка в лучшем случае. Плюс к этому достаточно много пришлось бы писать код и делать полноценную БД с представлениями, хранимками и прочим, что та ещё задача
Zero-code типа Bubble. Последний мне посоветовал коллега по акселератору "Гаража", Владимир Соловьёв, основатель крутейшего сервиса мотивации Whipcake. Это был очень дельный совет, спасибо ему большое. В процессе исследования вопроса мы нашли значительно более простой вариант решающий нашу задачу - Glide.
Раньше мы кое-что слышали о zero-code (на самом деле не Zero, об этом речь пойдёт ниже) и программировании мышкой. Но даже не предполагали как далеко вперёд продвинулись технологии. Сервисы типа Glide заточены под очень быстрое создание продукта, изначально мобильного, максимально легко разворачиваемого PWA на базе обычных таблиц Google Sheets.
То есть там предусмотрена собственная БД Glide, но она довольно куцая в плане возможностей кастомизации, поэтому мы всё делали на Гугл Таблицах и скриптах. Немаловажным фактором сыгравшим в пользу Glide послужила его условная бесплатность. То есть прототип, который использует только автор вполне возможно сделать полностью функциональным бесплатно. Если хотим пустить пользователей, сделать авторизованную зону, быстро обновлять данные и так далее - придётся заплатить, но меньше чем за тот же Bubble в разы.
Архитектурно всё выглядит примерно следующим образом.
Сам Glide обладает довольно мощными возможностями при том, что кодить фронт действительно не придётся. К интерфейсу разработки надо привыкнуть. Изначально всё делается под мобильные устройства Apple и Android, но в платной версии одной кнопкой можно расширить использование сервиса и на все остальные устройства. Технически это PWA, которую вы можете добавить на главный экран смартфона и это практически ничем не будет отличаться от полноценного мобильного приложения. Правда пуши сервис пока не поддерживает, это в том числе способствовало созданию Telegram-бота.
Работа с "БД", если так можно выразиться, выглядит так, что Glide добавляет к слою данных Google-таблиц ещё один слой обработки представления этих данных. Про работу в самих таблицах рассказывать не буду, там всё понятно. В Glide вы видите те же самые данные, но можете поверх своей таблицы сделать специфичные колонки, которые будут содержать логику завязанную на возможностях самого сервиса. Например, так выглядит создание колонки с условным представлением:
Также, Glide умеет работать с шаблонами. Вы делаете "переменные" в виде колонок, которые затем можете по определённой логике отобразить в шаблоне:
Когда разобрались со слоем данных, можно приступать к их отображению на фронте. Тут всё максимально просто и zero-code. Сначала создаёте табы. Они бывают двух видов: кнопки внизу экрана, либо в меню слева при нажатии на бургер. Когда таба создана, выбираете подходящее представление для неё: List, Details или другое. Details - самое гибкое, поскольку позволяет добавить туда помимо кнопок ещё и контрол в виде List. Возможности довольно широкие, несмотря на жёсткое ограничение по расположению элементов: практически никакой свободы дизайна не предусмотрено, разве только цвета и иконки поменять, да ещё размер шрифта в некоторых случаях.
Зачем нам Telegram-бот и как он работает. Распознавалка сделок
Я уже писал ранее, что в Glide нет пушей. Есть плохо сделанный чат техподдержки, который на практике мало применим. Наш сервис предусматривает возможность импорта сделок в любом формате, а не только картинки без возможности обработки, как это реализовано в Glide. В целом практически сразу пришло понимание необходимости ещё одного интерфейса взаимодействия с пользователем, желательно привычного для него. Нет ничего более привычного для нашей ЦА, чем Telegram.
Бота сделали по привычной схеме: Docker + Telebot + Python. Там всё стандартно, повторяться не буду, есть множество прекрасных статей на эту тему. Из нестандартного - для минимального распознавания скриншотов со сделками из терминала любого брокера пришлось прикрутить EasyOCR. Сразу скажу, что идея распознавать "любые скриншоты" в реальности работает плохо. Слишком большое разнообразие терминалов, их версий и тому подобного. Мы совершенно уверены в возможности крупной ИТ-компании сделать идеально работающую распознавалку, которая полностью избавит от необходимости включения человека в процесс, но текущими средствами без оператора проверяющего качество не обойтись. Процент ошибок распознавания очень высок. Отчасти мы отдали контроль качества распознавания на откуп пользователям, которые видят распознанные сделки в специальном разделе сервиса "К добавлению" и уже сами принимают решение о включении сделки в Блокнот.
EasyOCR оказался довольно прожорлив в плане ресурсов. И вообще для более быстрого распознавания картинок сам модуль советует использовать GPU поддерживающий CUDA. Это очень дорогие ресурсы, которые далеко не так просто найти. На обычной виртуалке процесс распознавания текста с крошечного скриншота экрана смартфона занимает 15-20 секунд минимум. Само распознавание довольно чистое. Есть известная проблема, когда в режиме 'ru' EasyOCR путает нуль и букву "о", а также число три и букву "з", но это решается элементарным обработчиком. Код приводить не буду, он крайне прост. Суть в том, что в тексте ищем слова-маркеры по положению которых относительно друг-друга мы можем опознать терминал и брокера, далее исходя заранее известной модели достаём из распознанного текстового массива параметры сделок.
Excel, CSV и другие форматы парсятся стандартно по модели. Тут как раз всё очень легко и просто делается, нужно только задать огромное количество этих моделей, поскольку брокеров много. Но формат данных в отчёте брокера гораздо стабильнее вида скриншотов терминалов, которые меняются от версии к версии и часто радикально.
Google Sheets + Apps Script для целей прототипирования
Отдельно хочу остановиться и воздать самые хвалебные слова благодарности создателям Google Sheets. Это намного больше чем просто таблицы вроде Excel. На GS действительно можно сделать отличный прототип базы данных крупного проекта. Например, сервис предусматривает агрегированный портфель сделок. Это легко делается при помощи сводных таблиц. Котировки ценных бумаг можно получить встроенной функцией GOOGLEFINANCE. Если нужны котировки криптовалют, ничто не мешает сделать элементарный запрос к API Binance, причём можно даже формулой типа ImportJSON. Но формулами злоупотреблять не рекомендую, они довольно часто виснут в статусе "Loading...". Гораздо правильнее запрашивать средствами JS в Apps Script, далее кэшировать результат, обрабатывать и класть единым запросом уже готовый в таблицу.
Пример работы с котировками на криптовалютные пары может быть таким. Сначала получаем уникальные тикеры из таблицы сделок пользователей средствами формул встроенных в Google Sheets. Здесь мы объединяем найденные уникальные массивы тикеров (колонка "B") и валют (колонка "K") в сделках. Получаем список уникальных сочетаний тикеров и валют:
=UNIQUE({deals!B:B, deals!K:K})
Далее уже скриптами получаем текущие цены на эти активы и кладём в отдельную табличку собранный массив одной операцией записи. Функция "getArrayFromRange" превращает Range в массив значений, а загадочный параметр "D1" содержит в себе ссылку на посчитанное формулой число строк в колонке "A" (=СЧЁТЗ("A1:A")):
function updatePricesCacheCRY() {
//Получаем уникальные тикеры в сделках записанных пользователями из таблицы
var buffer_tickers_curs = getArrayFromRange("Prices", "A1:C", "D1");
var buffer_cry = [];
//Ищем среди них криптовалюты
buffer_cry = buffer_tickers_curs.filter(function(item){
return item[1] == 'CRY'
});
//Получаем одномерный массив тикеров
let buffer_cry_tickers = buffer_cry.map(item => item[0]);
//Делаем один запрос к Binance. Функция "fetchAPI" делает вызов и парсит JSON
let url = "https://api1.binance.com/api/v3/ticker/price";
let cur_prices = fetchAPI(url);
//Фильтруем полученные данные так, чтобы остались только нужные нам тикеры
let cur_prices_filtered = cur_prices.filter(item => buffer_cry_tickers.includes(item.symbol));
//И преобразуем их в двумерный массив для возможности записи в таблицу
let cur_prices_filtered_array = cur_prices_filtered.map(function(item){
return [item.symbol, item.price];
});
//Записываем полученные данные в табличку G1:H
var list = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Prices");
let cur_prices_cache_range = list.getRange("G1:H"+(cur_prices_filtered_array.length));
//Сначала очищаем эту табличку от данных
cur_prices_cache_range.clear();
//Записываем одной операцией - это важно тк Execution Time Exceeded не редкость
cur_prices_cache_range.setValues(cur_prices_filtered_array);
}
Таким образом, Google Sheets представляет собой мощный инструмент прототипирования модели данных. Средствами скриптов можно выполнить практически любые задачи связанные с обработкой этих данных. Да, вы не сделаете высоконагруженный сервис с миллионами и даже сотнями тысяч строк, но Google Sheets вполне хватит в качестве БД для простого функционального прототипа.
Заключение
Итак, мы рассмотрели пример создания работающего прототипа из практически подручных материалов. Это конечно не совсем zero-code. Суммарно тысячи полторы строчек кода пришлось написать. Хотя даже если бы автор этих строк не владел элементарными навыками программирования, то одной связкой Glide + Google Sheets вполне можно было сделать рабочий прототип с ограниченой функциональностью. Скрипты Apps Script позволили добавить функционал отображения котировок и простую аналитику балансовой стоимости активов в сравнении с рыночной, бот на python оживил общение с пользователями, добавил привычный им интерфейс для коммуникации и импорта сделок. В целом, работа над всем сервисом заняла не более 2-х недель, включая написание всех скриптов. Основной функционал ручной записи и отображения сделок был готов за пару дней. Буду рад, если опыт создания прототипа Блокнота инвестора пригодится вам и ускорит успешный вывод на рынок вашего продукта.
mixsture
Думаю, ограничений тут побольше, чем просто «не-высоконагруженный». Все-таки база данных — это больше про согласованность и сохранность данных, а уже потом про скорость.
Вот что будет, если запустить 2 updatePricesCacheCRY() параллельно? хороший шанс получить битые данные из 2х последних строчек про clear и setValues.
nsuvorov Автор
Так точно, согласованность прорабатывается только на уровне самих скриптов. Параллельное выполнение этой функции не допустимо. Мы по времени запускаем каждые 15 минут.