feat(ledger): корпоративные действия из операций брокера

derive() выводит сплиты, амортизации и погашения из самого леджера и складывает их в
corporate_action; rewrite_splits схлопывает пару безденежных transfer_out/transfer_in
в синтетический split. Без этого перерегистрация бумаги реализовалась бы как фиктивный
round-trip: срок владения обнулился бы, а с ним и право на ЛДВ.

amount_per_unit считается пулом по инструменту и дате: одно действие эмитента приходит
отдельной операцией на каждый счёт, и делить надо на суммарную позицию. Позиция для
деления реплеится внутри модуля, потому что шаг обязан идти ДО лотов — именно лоты
потребляют то, что он пишет. Нет позиции — NULL и находка corporate_action_unpriced,
не подставленное число.

Модуль пишет и удаляет только split, amortization и repayment. Дивиденды и купоны он
не трогает: их календарь придёт из фида эмитента, и чистка снесла бы объявленные.

Живых сплитов и погашений в данных нет — эти ветки покрыты только синтетикой. Из
реального есть девять BOND_REPAYMENT, которые пулятся в семь амортизаций.
This commit is contained in:
Dmitry
2026-09-18 15:06:06 +03:00
parent ff4d63e829
commit eb4eae5beb
4 changed files with 676 additions and 2 deletions
+6 -1
View File
@@ -36,10 +36,11 @@ reported as a `Shortfall` for `metric_data_quality` to surface rather than guess
from __future__ import annotations
from collections import defaultdict
from collections.abc import Sequence
from collections.abc import Mapping, Sequence
from dataclasses import dataclass, field
from datetime import date
from decimal import Decimal
from typing import Any
from fintracker.models.ledger import EventKind
@@ -69,6 +70,10 @@ class LedgerEvent:
"""Split ratio; only read for `stock_split` events."""
exchange_traded: bool = True
"""False for OTC papers, which do not qualify for the LDV exemption."""
source: str = "tinvest"
"""Who reported it — carried so a derived corporate action can name its origin."""
meta: Mapping[str, Any] | None = None
"""Source payload leftovers; `ledger/corporate_actions.py` reads a split ratio from it."""
@dataclass