Перезалив. Хабр в очередной раз посчитал две ссылки на рабочий прототип рекламным объявлением — спасибо! Удалены все ссылки на продукт, за исключением исходников на githubе. Приятного чтения.
Салют, Хабр.
Зачем предприятия ведут учёт? Отчётность перед ФНС? Или может воронка клиентов? Сотни ответов российского рынка учётных систем на этот простой вопрос.
-
1С говорит владельцам: мы дадим вам идеальную, вылизанную до последней проводки отчётность перед налоговой.
-
Битрикс24, Amo… говорят: мы поможем вам сохранить каждого клиента, ведь не будет клиента — не будет денег.
Притянуто за уши, но картина понятна — среди учётных систем можно выделить два лагеря:
-
БУХ-центричные;
-
CRM-центричные.
Вместо начала
Меня зовут Серафим Недошивин, я скромный Go разработчик, и меня всегда тревожил один вопрос: разве не финансы как таковые должны являться центром учёта предприятий?
Какой толк от зелёной воронки лидов, каждый из которых в сухом остатке является убыточным? Какой смысл в причесанной отчётности без банального понимания рентабельности вашего дела?
Год назад я невольно стал заложником терзающих мой разум раздумий о смысле учётных систем. Так был написан первый, второй… пятый модуль мультитенантной ERP-подобной системы для малого бизнеса (Go + pgx | Next.js + TS), вопреки рынку ставящей именно модуль финансов в центр всей системы.
А работая с финансовым модулем, то есть с бесчисленным множеством временных рядов, вы неизбежно спросите себя — а что если?.. добавить простейшее прогнозирование финансов на основе фактических данных конкретной организации?
Об этом и пойдёт речь в этой статье. Прогнозирование, Theta, Holt-Winters, SES модели и где заканчивается точность?
Github | Apache 2.0 client+server
База
Начнём с базы — структуры данных финансового модуля. В Kroncl реализована транзакционная модель учёта: каждая операция — отдельная запись в базе данных с привязкой к направлению — трата / доход. Удаление операций невозможно (ибо грех), только сторнирование.
Финансовый модуль тесно интегрирован со всеми остальными модулями: например, каждая конкретная сделка имеет свою историю трат / доходов, льющихся в единую базу операций.
Таким образом, каждая операция (транзакция) характеризуется прежде всего:
-
суммой;
-
направлением;
-
временной меткой.
Набор таких записей представляет собой не что иное как временной ряд, а сам факт существования временного ряда в системе открывает возможности анализа и прогнозирования.
В рамках такого временного ряда точкой разумнее всего считать день, а в качестве значений брать балансы, рассчитанные как доходы — расходы.
Итак, мы знаем, с чем работаем, теперь задача попроще — как прогнозировать?
Прогнозирование
На первый взгляд задача прогнозирования ряда не является чем-то невероятным — берём ряд, находим тренд, примешиваем сезонность и экстраполируем на горизонт прогноза.
Существуют десятки моделей, учитывающих разный набор параметров и требующих разный объём данных для анализа при составлении прогноза, так вот задача выбрать модель гораздо сложнее, чем реализовать.
Большинство моделей опираются на три основных компонента:
-
уровень (среднее значение ряда в данный момент);
-
тренд (устойчивый рост или падение);
-
сезонность (повторяющийся паттерн фиксированной длины).
Давайте взглянем на фаворитов:
1. Простое экспоненциальное сглаживание (SES) (только уровень)
Выкидываем сезонность и тренд. Иными словами, просто находим среднее значение временного ряда, вокруг которого колеблются все остальные.
прогноз = α × сегодняшнее_фактическое + (1-α) × вчерашний_прогноз
Казалось бы, просто и делать 2 минуты, но даже здесь мы нехотя будем перебирать коэффициент сглаживания (α), минимизирующий ошибку прогноза.
2. Holt-Winters (старая гвардия)
Три компонента: уровень, тренд, сезонность. Для каждого компонента свой коэффициент сглаживания соответственно: α, β, γ. Каждый рассчитывается по своей формуле.
Не будем расписывать все формулы. Можно почитать тут.
Просто заметим, что для нормальной работы модели нужно как минимум:
-
длина сезона известна заранее: это не что иное как статичное значение в конфиге конкретной организации, а это всегда минус, ибо владельцы сами зачастую понятия не имеют о точной сезонности предприятия;
-
минимум 2 сезона: в случае с недельным сезоном — 14 дней, в случае с месячным — 60. Хотите, чтобы работало? — ведите учёт минимум N дней;
-
перебор 3 параметров (α, β, γ) или использование стандартных -> снижение точности.
3. Theta (спасибо)
Разложение временного ряда на две линии:
-
первую сглаживаем и экстраполируем (консервативная): простое экспоненциальное сглаживание. Берём исходный ряд и прогоняем через SES (первая модель) с коэффициентом
α = 0.3(начальное приближение в большинстве библиотек). После этого считаем тренд — на сколько в среднем росла или падала эта плавная кривая. Экстраполируем:SES_прогноз = последнее_значение + тренд × h; -
вторую экстраполируем без сглаживания (агрессивная): проводим прямую через все точки методом наименьших квадратов. Считаем наклон (slope) и пересечение (intercept). Экстраполируем:
регрессия_прогноз = slope × (n + h) + intercept.
Итоговый прогноз — среднее между двумя полученными линиями:
прогноз = (SES_прогноз + (θ - 1) × регрессия_прогноз) / θ
Стандарт θ = 2, тогда:
прогноз = (SES_прогноз + регрессия_прогноз) / 2
У модели Theta есть одна очень важная в случае с Kroncl особенность: прогнозирование возможно уже начиная со второй точки, то есть достаточно 2 дней ведения финансовой отчётности. Для малого бизнеса — это слишком хорошо. Да, мы теряем сезонность, да, точность прогнозирования всё ещё напрямую зависит от количества точек анализа, но это решается добавлением уровня точности в ответ клиенту.
Существуют методы вроде Prophet, добавляющих дополнительные параметры при расчёте прогноза, но, повторюсь, в случае с Kroncl именно Theta даёт разумную точность на коротких рядах без ручного конфигурирования сезонности.
Снова пишем
Замечу, что кодовая база сервера Kroncl превысила 40 тысяч строк кода, включая конфигурации разрешений и прочего добра, но интегрировать forecast_ подсистему в модуль fm (управление финансами) не составило большого труда.
Сделаем два базовых метода:
-
forecast/timeline— базовое получение ряда прогноза, полученного с помощью Theta; -
forecast/summary— агрегация поверхtimeline, динамический подсчёт баланса, трат, доходов, чистого потока к концу горизонта прогноза.
Реализация расчётов упакована тут.
Ограничиваем возможный горизонт прогноза для сохранения точности: горизонт <= 2 × кол-во точек анализа.
Оппа
И конечно, испытаем оргазм от результата, рассмотрим прогноз на 1400 операциях (около 200 точек анализа), преимущественно траты, с горизонтом в 100 дней. Автор лентяй и не будет проводить детальное сравнение рядов разной длины, думаю, принцип понятен.

В итоге, достаточно 2(!) точек для построения минимально точного прогноза. В идеале, прогнать аналогичные ряды через все три модели: SES, Theta, Holt-Winters и сравнить степень сглаживания и точность. Если вам нечем заняться — вперёд.
В заключение
В заключение ничего не будет. Просто Theta прогнозирование финансового модуля на Go. Kroncl — облачная ERP система для малого бизнеса. Физическая изоляция данных, ролевая модель на 70+ разрешениях, 5 модулей, 130 тысяч кода.
ссылка на оригинал статьи https://habr.com/ru/articles/1045092/