Files
specs/openspec/changes/persist-tbank-data/specs/tbank-data-sync/spec.md
T
ksv741 2f5814abdd
Conventional commits / commits (pull_request) Successful in 12s
Specs security / security (pull_request) Successful in 7s
Specs verify / verify (pull_request) Successful in 11s
Specs security / security (push) Successful in 7s
Specs verify / verify (push) Successful in 9s
spec: complete persist-tbank-data implementation
Record the approved proposal, delta specifications, design and 27 verified
implementation tasks. Keep the change active; archiving and main-spec synchronization
are deferred.
2026-09-26 21:29:24 +03:00

33 KiB

Purpose

Определяет постоянное хранение данных T-Bank, локальное чтение и синхронизацию по запросу с проверяемыми полнотой, свежестью и изоляцией подключения.

ADDED Requirements

Requirement: Все экраны читают сохранённые данные без ожидания T-Bank

Система SHALL постоянно сохранять уже загруженные счета, последний полный портфель, исполненные операции, данные инструментов, текущие котировки, дневные свечи и календари выплат. Доходы, личные позиции и прогнозные выплаты SHALL рассчитываться по сохранённым источникам. Все соответствующие экраны, включая режим «Все счета», поиск, сведения о компании и зависимости котировок, MUST читать доступные данные без ожидания ответа T-Bank. Перезапуск backend MUST NOT требовать повторной загрузки сохранённых полных интервалов.

Scenario: Брокер медленно отвечает при заполненном хранилище

  • WHEN User открывает любой экран с сохранёнными данными текущего подключения, а T-Bank задерживает ответ
  • THEN обычный серверный ответ содержит сохранённые данные и их состояние, не ожидая T-Bank, включая проверку доступных счетов

Scenario: Перезапуск сохраняет историю

  • WHEN backend перезапущен и User запрашивает ранее полностью загруженный прошлый период
  • THEN система возвращает его из постоянного хранилища без повторного получения операций или завершённых свечей этого периода

Scenario: Данные ещё не загружены

  • WHEN User впервые запрашивает отсутствующие данные
  • THEN ответ различает отсутствие загруженного результата и подтверждённый пустой результат, сообщает о запрошенной загрузке и не ожидает её завершения

Requirement: Обновление запускается только запросом с учётом свежести

Система MUST запускать синхронизацию только запросом экрана либо ручным обновлением. Обычный запрос SHALL планировать только отсутствующие зависимости, пропуски и данные с истекшим сроком свежести: портфель и текущие цены — 60 секунд; операции текущего месяца и доступные счета — 300 секунд; справочник инструментов, включая сведения о компании, и календари выплат — 24 часа; незавершённая дневная свеча — 60 секунд. Свежесть MUST отсчитываться от последнего успешного получения соответствующего источника, а не чтения ответа или расчёта производного представления. Полнота и свежесть MUST оставаться независимыми признаками. Периодические запуски MUST NOT выполняться. Для текущего изменяемого периода полнота SHALL относиться к состоянию на последний успешный asOf: рост to=now внутри TTL не является новым пропуском, требующим RPC. Настоящие пропуски до asOf и незавершённый хвост при переходе месяца MUST NOT маскироваться этим правилом.

Scenario: Данные ещё свежие

  • WHEN экран повторно запрошен до истечения сроков всех его полных зависимостей
  • THEN запрос не создаёт новых обращений к T-Bank

Scenario: Текущий период запрошен через секунду

  • WHEN текущий месяц полностью загружен на asOf и повторный запрос через секунду имеет более поздний to=now
  • THEN система возвращает сохранённый результат с исходным asOf без нового RPC до истечения пяти минут, не утверждая наблюдение новых операций после asOf

Scenario: Истекла только свежесть портфеля

  • WHEN портфель получен 60 секунд назад, а полные счета и metadata ещё свежие
  • THEN обычный запрос инициирует обновление портфеля и только иных необходимых устаревших зависимостей, сохраняя прежний ответ доступным

Scenario: Пользователь не обращается к системе

  • WHEN запрошенная работа завершилась и сроки свежести истекают без новых запросов
  • THEN никакая синхронизация не запускается, в том числе при запуске backend

Requirement: Ручное обновление ограничено текущим экраном

Действие «Обновить» SHALL обходить давность данных и запрашивать обновление экрана с его зависимостями: текущего счёта либо доступных счетов в режиме «Все счета», применённого периода и сравнений доходов, либо инструмента с показанными свечами, выплатами и личной историей. Сервер MUST определять разрешённые зависимости сам, сохраняя проверку входа и доступа. Обновление MUST NOT перечитывать всю накопленную историю других периодов и инструментов. Подтверждение приёма работы MUST NOT означать успешное обновление данных.

Scenario: Обновление старого месяца

  • WHEN User нажимает «Обновить» на журнале за полностью загруженный прошлый месяц
  • THEN перечитывается показанный период счёта и необходимые зависимости без принудительного перечитывания остальных полных месяцев

Scenario: Обновление доходов

  • WHEN User обновляет аналитику за выбранный период
  • THEN обновление охватывает выбранный и требуемые периоды сравнения, а результаты разных периодов не подменяют друг друга

Scenario: Обновление инструмента

  • WHEN User обновляет инструмент с выбранным диапазоном графика и показанной личной историей
  • THEN обновляются зависимости этого инструмента, диапазон свечей и показанный период личной истории разрешённых счетов без обновления всего каталога

Requirement: Первая загрузка приоритизирует экран и продолжает историю

Система SHALL сначала загружать данные нужного экрану периода, включая периоды сравнения доходов, затем всю доступную историю запрошенного счёта. Для режима «Все счета» это применяется к каждому доступному счёту. В рамках уже запрошенной работы SHALL заранее загружаться доступная рыночная история бумаг текущего портфеля; для остальных инструментов — при первом открытии. Новый запрос нужного экрану пропуска MUST получать приоритет перед ещё не начатой догрузкой старой истории. Начатая работа MAY завершиться после ухода со страницы. Недоказанная нижняя граница доступной истории MUST NOT выдаваться за подтверждение её полной загрузки.

Scenario: Сначала сравнения доходов

  • WHEN история счёта отсутствует и User открывает доходы за месяц
  • THEN выбранный месяц и доступные обязательные периоды сравнения загружаются перед остальной историей; состояние старой истории не задерживает уже полный результат экрана

Scenario: Уход со страницы

  • WHEN User уходит после начала догрузки истории
  • THEN запрошенная работа может закончиться и сохранить результат, но не создаёт расписание последующих обновлений

Scenario: Инструмент отсутствует в портфеле

  • WHEN User впервые открывает такой инструмент
  • THEN система запрашивает и сохраняет его историю, не предзагружая весь каталог

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

Система MUST считать историю счёта полной за период только после успешного получения всех доступных исполненных операций всего этого интервала на момент сверки. Полный период MAY быть пустым. Фильтр категорий, инструмента, размер страницы, отсутствие записей, максимальная дата операции и наличие курсора MUST NOT служить доказательством полноты. Производное представление «Все счета» MUST учитывать покрытие каждого входящего счёта. Полные прошлые месяцы SHALL сохраняться без автоматической повторной сверки; пропуски SHALL догружаться при запросе. Смена месяца MUST NOT автоматически закрывать незагруженный хвост прошлого месяца. Существующая семантика входных дат журнала и доходов MUST сохраняться без пропусков и двойного учёта на границах.

Scenario: Пустой полный интервал

  • WHEN все страницы интервала успешно получены и операций нет
  • THEN период обозначается полным и пустым; последующее обычное чтение прошлого месяца не обращается к брокеру за этим интервалом

Scenario: Сбой промежуточной страницы

  • WHEN одна из нескольких страниц интервала недоступна
  • THEN интервал остаётся неполным, сохранённый результат не удаляется, частичные записи не выдаются за полный период

Scenario: Граница месяца

  • WHEN последний успешный запрос прошлого месяца закончился до его конца
  • THEN первый подходящий запрос нового месяца догружает недостающий хвост; смена даты сама не подтверждает полноту прошлого месяца

Scenario: Неполон один из счетов

  • WHEN все счета, кроме одного, полностью покрывают период общего дохода
  • THEN общий период не считается полным, а отсутствующий вклад счёта не заменяется нулём

Requirement: Сверка операций сохраняет идентичность и прежний результат

Система SHALL использовать стабильный ID брокерской операции в границах пользователя, подключения и счёта как принятое допущение проекта, а не гарантию API. Повторное получение ID MUST обновлять поля той же операции без дубля и двойного финансового учёта. Ручная сверка MUST согласовывать состав и поля исполненных операций показанного интервала: отсутствующие в полном новом ответе записи могут быть удалены только вместе с успешным завершением всех страниц этого интервала. Частичный либо ошибочный новый ответ MUST NOT изменять ранее опубликованный полный финансовый результат и время его успешной сверки. Автоматическое обнаружение изменений полной старой истории и смены ID не требуется.

Scenario: Поля операции исправлены

  • WHEN успешная повторная сверка получает прежний ID с другой суммой или статусом, меняющим вхождение в исполненную историю
  • THEN итоговый набор отражает новый полный ответ без дублирования ID, а доходы пересчитываются из согласованного набора

Scenario: Операция исчезла при ручной сверке

  • WHEN все страницы перечитанного интервала успешно завершились и прежняя операция отсутствует
  • THEN операция исключается из опубликованного набора этого интервала и производных финансовых результатов

Scenario: Перезагрузка полного периода оборвалась

  • WHEN после нескольких страниц ручной сверки возникает ошибка
  • THEN прежний полный набор, его суммы, полнота и время успешной сверки остаются доступными вместе с ошибкой обновления

Scenario: Сбой во втором месяце перечитываемого квартала

  • WHEN ручная сверка полного квартала получила первый месяц, но оборвалась на странице второго
  • THEN весь прежний результат показанного квартала, включая режим «Все счета», остаётся опубликованным; успешный новый первый месяц не смешивается с прежними остальными месяцами

Requirement: Прогресс переживает перезапуск и одновременные запросы

Система MUST сохранять завершённые интервалы, незавершённую работу и её прогресс. После перезапуска следующий подходящий запрос экрана или ручное обновление SHALL возобновлять запрошенную работу, повторяя незавершённый интервал безопасно и без повторной загрузки полных старых интервалов. Одновременные запросы одной границы данных SHALL объединять одинаковую работу, а пересекающиеся интервалы SHALL использовать общую загрузку пересечения. Ошибка MUST оставлять возможность следующего явного запроса, не создавая бесконечный цикл повторов. Поздняя публикация устаревшей попытки MUST NOT перезаписывать результат новой попытки.

Scenario: Двадцать одновременных запросов

  • WHEN двадцать запросов требуют один отсутствующий интервал одного счёта
  • THEN выполняется один проход страниц этого интервала с разрешёнными транспортными повторами, а все запросы видят общее состояние работы

Scenario: Перезапуск между страницами

  • WHEN процесс остановлен после сохранения прогресса, но до полного завершения интервала
  • THEN до следующего запроса работа не возобновляется; после запроса интервал перечитывается без дублей и без ложного признака полноты

Scenario: Ошибка завершила попытку

  • WHEN запрошенная синхронизация исчерпала допустимые транспортные повторы
  • THEN она показывает безопасную ошибку и прекращает попытки до следующего запроса; наблюдение её состояния не запускает её заново

Requirement: Общие данные инструментов отделены от личных

Система SHALL совместно использовать неперсональные metadata, котировки, свечи и календари одного инструмента между пользователями. Общие записи MUST NOT содержать аккаунты, личные позиции, остатки, операции, оценки выплат пользователя, token либо персональные ошибки. Личные данные и вычисления MUST оставаться ограничены пользователем, доступным счётом и текущим подключением. Успешный общий результат MAY переиспользоваться между счетами без повторного получения того же источника.

Scenario: Два пользователя открывают инструмент

  • WHEN оба имеют право пользоваться интеграцией и общие данные инструмента уже загружены
  • THEN они используют одни общие данные, но видят только собственные позиции, операции и оценки выплат

Scenario: Ошибка token одного пользователя

  • WHEN источник отклоняет token одного пользователя при загрузке общего инструмента
  • THEN личная ошибка не становится состоянием инструмента для другого пользователя и не удаляет ранее сохранённые общие данные

Requirement: Локальная проверка доступа сохраняет границу подключения

Каждое чтение и действие синхронизации MUST проверять сессию, текущее подключение и локально подтверждённую принадлежность счёта этому подключению. Отсутствие ранее подтверждённого списка SHALL означать загрузку доступа, а не доступ к произвольному accountId или доказанный пустой список. Устаревший подтверждённый список MAY использоваться до фоновой сверки; успешная сверка MUST отзывать доступ к исчезнувшим, закрытым или неподдержанным счетам. Известный отказ token MUST сохранять существующую ошибку INVALID_TBANK_TOKEN, отсутствие подключения — TBANK_NOT_CONNECTED, недоступный счёт — 404. Хранение не вводит архив без подключения. Успешная замена или удаление подключения MUST блокировать выдачу прежних личных данных и публикацию любых поздних результатов прежней работы. Неуспешная замена MUST сохранять действующую границу.

Scenario: Подтверждённый счёт проверяется локально

  • WHEN User читает заполненные данные доступного счёта текущего подключения
  • THEN проверка принадлежности не обращается синхронно к T-Bank и не использует список другого пользователя или подключения

Scenario: Прямой переход до загрузки счетов

  • WHEN список текущего подключения ещё неизвестен и User запрашивает конкретный accountId
  • THEN система сообщает о загрузке проверки доступа, не выдаёт личных данных этого ID и загружает доступные счета

Scenario: Доступ к счёту отозван

  • WHEN успешная фоновая сверка больше не подтверждает доступ к ранее видимому счёту
  • THEN последующие запросы этого счёта возвращают 404, а клиент прекращает показывать его как доступный

Scenario: Поздний результат прежнего подключения

  • WHEN старый запрос заканчивается после успешной замены или удаления подключения
  • THEN его данные, прогресс, ошибки и признаки свежести не публикуются и не становятся результатом нового подключения

Requirement: Завершённость свечей подтверждается источником

Система SHALL сохранять дневные свечи по инструменту и времени источника. Завершённые свечи MUST NOT перечитываться обычным запросом при полном покрытии диапазона. Незавершённая свеча SHALL обновляться при запросе после 60 секунд до явного подтверждения завершённости источником, даже если её календарный день прошёл. Успешное пустое окно MAY подтверждать отсутствие свечей только в завершённом диапазоне и MUST NOT создавать синтетические свечи. Открытое окно текущего дня, в том числе пока пустое, SHALL оставаться изменяемым с минутным сроком свежести. Ручное обновление SHALL перечитывать показанный диапазон, сохраняя прежние данные при ошибке.

Scenario: Наступил новый день

  • WHEN сохранённая свеча предыдущего дня не подтверждена как завершённая источником
  • THEN новый день не делает её окончательной, и подходящий запрос продолжает обновлять её с минутным сроком свежести

Scenario: Выходной без свечей

  • WHEN источник успешно подтвердил пустое окно без торговых свечей
  • THEN окно сохраняется как проверенное без выдуманных цен и повторной обычной загрузки завершённого диапазона

Scenario: До начала торгов свеча отсутствует

  • WHEN запрос текущего дня вернул пустое окно, а после начала торгов User снова открывает график по истечении минуты
  • THEN система запрашивает изменяемое окно и сохраняет появившуюся свечу; утренний ответ не закрепляет окончательное отсутствие свечи

Requirement: Интерфейс различает свежесть полноту и состояние работы

На всех экранах T-Bank клиент MUST показывать время последнего успешного получения, свежесть, загрузку и безопасную ошибку обновления применимых данных. Полнота MUST относиться к показанному периоду и его зависимостям; общий прогресс истории счёта MUST NOT подменять полноту экрана. Отсутствующие данные SHALL иметь состояние первичной загрузки; доступные данные SHALL оставаться видимыми во время обновления. Неполные totals/comparisons MUST NOT показываться как окончательные или как ноль; неполный пустой журнал MUST NOT сообщать, что операций нет. Наблюдение запрошенной работы MUST прекращаться после завершения либо ухода и MUST NOT запускать синхронизацию. Известные ошибки доступа MUST NOT маскироваться сохранённым результатом.

Scenario: Недавний период готов раньше всей истории

  • WHEN показанный период и его зависимости полны, но старая история ещё загружается
  • THEN пользователь видит готовый результат периода и отдельное состояние догрузки истории

Scenario: Полнота изменилась без новых записей

  • WHEN источник подтвердил пустой период экрана, а остальная история ещё загружается
  • THEN наблюдающий клиент получает изменение полноты и показывает подтверждённый пустой результат, не ожидая конца всей истории

Scenario: Ответы наблюдения завершились в обратном порядке

  • WHEN поздний ответ чтения содержит прежние данные или доступ, а клиент уже принял более новое состояние этого ресурса либо отзыв доступа
  • THEN прежний ответ не откатывает показанные данные и не восстанавливает недоступный личный результат

Scenario: Источник временно недоступен

  • WHEN обновление сохранённого полного результата завершается временной ошибкой
  • THEN прежние данные и время сохраняются, ошибка видима и доступно ручное повторное обновление; ошибка не обновляет время успешного получения и не считается успешной сверкой

Scenario: Неполное сравнение дохода

  • WHEN выбранный период готов, а необходимый период сравнения ещё не загружен
  • THEN сравнение обозначается как незавершённое без нулевой базы или окончательной разницы

Requirement: Быстродействие и сокращение запросов проверяются измерениями

Для обычных чтений заполненной БД система SHALL иметь целевой бюджет серверного ответа до 500 мс в зафиксированном профиле нагрузки. Выполнение цели MUST подтверждаться измерениями, а не предполагаться из наличия БД. Свежие полные зависимости MUST давать ноль новых RPC T-Bank; двадцать одновременных запросов одного устаревшего набора MUST создавать не более одной логической загрузки каждой зависимости. Полные старые операции и завершённые свечи MUST давать ноль повторных RPC для своих интервалов при обычном чтении, включая чтение после перезапуска.

Scenario: Измеряется заполненное хранилище

  • WHEN проверяется согласованный набор обычных запросов при задержанном или недоступном T-Bank
  • THEN фиксируются распределение серверных задержек, число RPC и параметры нагрузки; цель 500 мс не объявляется достигнутой без этих результатов

Scenario: Повторные открытия не перечитывают историю

  • WHEN один и тот же полный прошлый период читается повторно без ручного обновления
  • THEN сохранённые данные выдаются без RPC этого интервала, независимо от числа страниц клиентской выдачи