masyasmv / aton-statement-parser
Parser and normalizer for ATON broker XML statements. Converts raw XML reports into structured DTOs for convenient data access.
Requires
- php: ^8.0
- ext-dom: *
- ext-iconv: *
- ext-libxml: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.60
- infection/infection: ^0.26
- phpstan/phpstan: ^1.11
- phpunit/phpcov: ^8.2
- phpunit/phpunit: ^9.6
- qossmic/deptrac-shim: ^0.24
- vimeo/psalm: ^5.26
This package is auto-updated.
Last update: 2026-03-30 23:26:04 UTC
README
Пакет для парсинга XML-отчётов брокера Атон в удобную доменную структуру данных. Сейчас библиотека ориентирована на поддержку:
- старого BIS-формата
BIS:BISPeriod, - нового XML-формата на основе
<root><source name="...">.
Цель та же: привести оба формата к единой модели отчёта, чтобы в проекте можно было:
- передать путь до XML-файла,
- получить объект отчёта (
ReportInterface), - быстро выбрать нужный блок (
Trades,MoneyInOut,StockInOut,TradesRegRepo,ClientMoneyConvert,StockPayingOff, …), - найти операцию по
OperID, - собрать список всех
OperID(например, для сверки с БД), - работать с атрибутами строк через удобные геттеры (строки/даты/числа) без «магических» индексов массива,
- получать диагностику по новым или неожиданным структурам отчёта,
- сохранять derived legacy-совместимые секции для modern XML там, где прямого источника нет.
Цель: минимальная боль при работе с реальными отчётами старого и нового форматов, сохраняя единый API.
Быстрый старт
use MasyaSmv\AtonStatementParser\AtonStatementParser; $report = AtonStatementParser::fromFile('/path/to/report.xml'); // Доступ к секциям (универсально) $trades = $report->section('Trades')->rows(); // RowCollection $money = $report->section('MoneyInOut')->rows(); // RowCollection // DTO-уровень для типовых сценариев $commonData = $report->commonData(); // ?CommonData $tradeDtos = $report->trades(); // TradeCollection $moneyDtos = $report->moneyInOut(); // MoneyOperationCollection $moneyState = $report->moneyOnDate(); // MoneyBalanceCollection $stockState = $report->stockOnDate(); // StockBalanceCollection $fxDtos = $report->moneyConvert(); // MoneyConvertCollection $stockDtos = $report->stockInOut(); // StockTransferCollection $payoffDtos = $report->stockPayingOff(); // StockPayingOffCollection $corpInDtos = $report->corporateActionsIn(); // CorporateActionCollection $corpOutDtos = $report->corporateActionsOut(); // CorporateActionCollection // Поиск по OperID $row = $report->findOperId('567890123'); // Row|null // Список всех OperID (для сверки) $ids = $report->operIds(); // OperIdCollection // Диагностика неизвестных структур и ключей $diagnostics = $report->diagnostics(); // DiagnosticCollection $hasWarnings = $report->hasDiagnostics(); // bool // Удобные геттеры атрибутов $type = $report->section('Trades')->rows()->get(0)->getString('TradeType'); $date = $report->section('Trades')->rows()->get(0)->getDate('OperDateSort'); // DateTimeImmutable|null
Проблемы, которые решает пакет
1) Два формата отчёта
Старые отчёты используют BIS:BISPeriod, новые идут через <root><source name="...">. Библиотека должна уметь читать оба формата и сводить их к одной модели.
2) Namespace BIS
XML использует xmlns:BIS=..., значит нельзя просто «по имени тега» — нужен XPath с корректной регистрацией namespace.
3) Кодировка windows-1251 и UTF-8 BOM
В старых отчётах встречается encoding="windows-1251", а в новых возможен utf-8 с BOM. Пакет должен безопасно привести документ к корректному виду перед парсингом.
4) Разные форматы дат
В отчётах встречаются:
29.12.202302.10.2416.11.1826.12.24 /(мусорные хвосты)
Нужен нормализатор даты: трим + поддержка d.m.Y и d.m.y.
В новом формате встречаются и ISO-значения, например 2024-02-01T00:00:00.
5) Десятичные числа
Количество и суммы — строки с точностью (100000.00000000). Базовый слой хранит как string, а геттеры умеют приводить к float/int при необходимости.
6) Новые структуры и неожиданные ключи
Если брокер меняет XML и в отчёте появляется:
- новый
sourceв modern-формате, - неизвестная legacy-секция,
- неожиданный ключ внутри уже известной структуры,
библиотека не теряет данные молча. Она продолжает парсинг и добавляет предупреждения в Report->diagnostics().
Архитектура (как планируем)
Основные сущности
- Report — весь отчёт, доступ к секциям и общим данным.
- Section — один блок внутри отчёта (например
Trades,MoneyInOut). - Row — одна строка секции в каноническом виде. Хранит имя секции, исходный тип записи, immutable-атрибуты и типовые геттеры.
- RowCollection / OperIdCollection / AttributeBag — immutable коллекции и value objects для публичной модели.
Уровни удобства
- Базовый (универсальный):
Report/Section/Row— работает для любых секций без генерации десятков DTO. - Продвинутый (DTO для популярных секций):
CommonData,Trade,MoneyOperationи т.п. — добавляются постепенно поверх канонической модели. На текущем этапе доступны также DTO дляMoneyOnDate,StockOnDate*,MoneyConvert,StockInOut,StockPayingOff,CorpActionIn,CorpActionOut.
Структура проекта (план)
src/
AtonStatementParser.php // fromFile/fromString
Collections/
CorporateActionCollection.php // immutable коллекция корпоративных действий
MoneyBalanceCollection.php // immutable коллекция денежных остатков
MoneyConvertCollection.php // immutable коллекция конверсионных операций
MoneyOperationCollection.php // immutable коллекция денежных операций
RowCollection.php // immutable коллекция строк
OperIdCollection.php // immutable коллекция OperID
StockBalanceCollection.php // immutable коллекция остатков по бумагам
StockPayingOffCollection.php // immutable коллекция погашений/выплат
StockTransferCollection.php // immutable коллекция переводов бумаг
TradeCollection.php // immutable коллекция сделок
Contracts/
Mappers/
...MapperInterface.php // интерфейсы DTO-мапперов
Dto/
CommonData.php // DTO общих данных отчёта
CorporateAction.php // DTO корпоративного действия
MoneyBalance.php // DTO денежного остатка
MoneyConvertOperation.php // DTO валютной конверсии
Trade.php // DTO сделки
MoneyOperation.php // DTO денежной операции
StockBalance.php // DTO остатка по бумаге
StockPayingOff.php // DTO погашения/выплаты по бумаге
StockTransfer.php // DTO перевода/списания бумаги
Mappers/
CommonDataMapper.php // Row -> CommonData
CorporateActionMapper.php // Row -> CorporateAction
MoneyBalanceMapper.php // Row -> MoneyBalance
MoneyConvertMapper.php // Row -> MoneyConvertOperation
TradeMapper.php // Row -> Trade
MoneyOperationMapper.php // Row -> MoneyOperation
StockBalanceMapper.php // Row -> StockBalance
StockPayingOffMapper.php // Row -> StockPayingOff
StockTransferMapper.php // Row -> StockTransfer
Parsing/
ReportParserResolver.php // выбор парсера по формату документа
LegacyBisReportParser.php // старый BIS-формат
ModernXmlReportParser.php // новый root/source формат
KnownLegacySchema.php // known-schema legacy секций и ключей
KnownModernSchema.php // known-schema modern source и ключей
SectionNameResolver.php // канонизация имён секций
Report/
Report.php // секции отчёта
Section.php // имя секции + RowCollection
Row.php // строка секции + геттеры
AttributeBag.php // immutable-атрибуты строки
ParseDiagnostic.php // диагностика структуры парсинга
DiagnosticCollection.php // immutable коллекция диагностик
Support/
DecimalStringMath.php // точная строковая арифметика для derived aggregate-секций
Xml/
XmlLoader.php // чтение файла, encoding -> utf8, DOM
XPathFactory.php // DOMXPath + регистрация namespace
Normalizers/
DateNormalizer.php // даты (d.m.y / d.m.Y, мусорные хвосты)
NumberNormalizer.php // числа/десятичные строки
StringNormalizer.php // trim/cleanup
Exceptions/
ParseException.php
InvalidXmlException.php
tests/
Fixtures/
aton/
...xml
...
Roadmap / План работ
✅ Уже сделано
- Базовый каркас пакета (autoload, тесты)
- Fixtures подключены в тесты
- Скрипты composer для
test,cs:*,stan(если настроено) - Поддержка старого BIS-формата
- Базовая поддержка нового root/source формата
- Immutable collections в публичной модели
🚧 Этап A — «сразу полезно»
-
AtonStatementParser::fromFile(string $path): ReportInterface -
AtonStatementParser::fromString(string $xml): ReportInterface -
Report->section(string $name): Section -
Section->rows(): RowCollection -
Report->operIds(): OperIdCollection -
Report->findOperId(string $id): ?Row - Unit-тесты на fixtures (operIds/findOperId/section)
🚧 Этап B — нормализация типов
-
Row->getString($key)/getInt($key)/getFloat($key) -
Row->getDecimalString($key)(без потери точности) -
Row->getDate($key): ?DateTimeImmutable(с поддержкой форматов) - Базовые тесты на даты/числа
- Фактический coverage доведён до
100%по строкам/методам/классам - Подтверждена паритетная канонизация core-секций на парных real fixtures
old/new -
PortfolioMoneyнового формата разложен наMoneyOnDate,MoneyOnDate_MarketPrc,MoneyOnDate_ByOperPlace - Для
MoneyInOut_ioдобавлено схлопывание строго симметричных+/-дублей до legacy-compatible результата -
StockOnDate_Exg_Sumнового формата вычисляется как derived aggregate по строкамStockOnDate_Exg - Synthetic legacy-совместимые секции помечаются диагностикой
synthetic_legacy_section - Уточнить бизнес-смысл
MoneyOnDate_single, если для него появится прямой modern-источник или подтверждённая формула
🚧 Этап C — DTO (точечно, только нужное)
-
Report->trades(): TradeCollection -
Report->moneyInOut(): MoneyOperationCollection -
Report->moneyOnDate(): MoneyBalanceCollection -
Report->stockOnDate(): StockBalanceCollection -
Report->moneyConvert(): MoneyConvertCollection -
Report->stockInOut(): StockTransferCollection -
Report->stockPayingOff(): StockPayingOffCollection -
Report->corporateActionsIn(): CorporateActionCollection -
Report->corporateActionsOut(): CorporateActionCollection - Базовые мапперы секций → DTO
- Расширить DTO-покрытие на дополнительные секции
- Довести базовые тесты DTO-маппинга
🚧 Этап D — удобства для больших отчётов
- Фильтры/поиск:
section()->where(fn(Row $r) => ...) - Индексация по
OperID(лениво или при первом вызове) - Сортировка операций по дате/времени
- Поиск по бумаге по названию и
ISIN - Collection-style методы в духе ORM/Laravel:
count(),sum(),filter(),map(),firstWhere(),sortBy() - Улучшение ошибок парсинга (контекст, позиция)
Fixtures и безопасность
- В репозиторий коммитим только обезличенные XML.
- Реальные отчёты для локальной отладки — держать в
tests/FixturesLocal/(и добавить в.gitignore).
Рекомендуется добавить .gitattributes, чтобы не ловить «невидимые» правки концов строк (LF/CRLF).
Installation
composer require masyasmv/aton-statement-parser
Development
composer install
composer test
composer deptrac
composer cs:fix
composer cs:check
composer stan
composer psalm
composer test:coverage
composer coverage:check
Mutation testing вынесен в отдельный workflow и отдельную команду:
composer infection
Локально composer infection и composer test:coverage требуют coverage driver (pcov или xdebug).
В CI они запускаются через setup-php с pcov.
Текущее состояние локального test coverage:
- строки:
100% - методы:
100% - классы:
100%
Documentation
Подробная карта библиотеки, слоёв, канонических секций, DTO API и точек расширения:
Versioning
Проект следует Semantic Versioning.
MAJORдля несовместимых изменений APIMINORдля обратно совместимого функционалаPATCHдля обратно совместимых исправлений
Формат релизного тега:
vX.Y.Z
Releases
CHANGELOG.mdведётся в репозитории и должен обновляться перед релизом.- GitHub Release создаётся автоматически при push тега формата
v*.*.*. - Для выпуска релиза достаточно создать и отправить тег:
git tag v0.1.1 git push origin v0.1.1
License
MIT. См. файл LICENSE.