feat: consolidate ledger trade writes into portfolio_ledger
- add ledger consolidation migration and remove ledger_trades usage - return portfolio state after v1 ledger trade writes - deprecate legacy buy/sell endpoints with deprecation headers - add BAT-016, DCY-006, and REQ-036~038 documentation - update repository models and tests for unified ledger entries
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
"""consolidate ledger trades into portfolio ledger
|
||||
|
||||
Revision ID: 20260605_0025
|
||||
Revises: 20260601_0024
|
||||
Create Date: 2026-06-05 00:25:00.000000
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = "20260605_0025"
|
||||
down_revision: str | None = "20260601_0024"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("portfolio_ledger", sa.Column("ledger_entry_id", sa.BigInteger(), sa.Identity(always=False), nullable=True))
|
||||
op.add_column("portfolio_ledger", sa.Column("order_id", sa.Text(), nullable=True))
|
||||
op.add_column("portfolio_ledger", sa.Column("execution_id", sa.Text(), nullable=True))
|
||||
op.add_column("portfolio_ledger", sa.Column("side", sa.Text(), nullable=True))
|
||||
op.add_column("portfolio_ledger", sa.Column("executed_at", sa.DateTime(timezone=True), nullable=True))
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'portfolio_ledger' AND column_name = 'action'
|
||||
) THEN
|
||||
UPDATE portfolio_ledger SET side = lower(action) WHERE side IS NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
"""
|
||||
)
|
||||
op.execute("UPDATE portfolio_ledger SET side = 'manual' WHERE side IS NULL")
|
||||
op.execute("UPDATE portfolio_ledger SET executed_at = recorded_at WHERE executed_at IS NULL")
|
||||
op.alter_column("portfolio_ledger", "side", nullable=False)
|
||||
op.alter_column("portfolio_ledger", "executed_at", nullable=False)
|
||||
|
||||
for column in (
|
||||
"price",
|
||||
"quantity",
|
||||
"fee_amount",
|
||||
"tax_amount",
|
||||
"cash_balance_after",
|
||||
"asset_quantity_after",
|
||||
"total_value_after",
|
||||
"profit_buffer_after",
|
||||
):
|
||||
op.alter_column(
|
||||
"portfolio_ledger",
|
||||
column,
|
||||
existing_type=sa.Float(),
|
||||
type_=sa.Numeric(20, 8),
|
||||
postgresql_using=f"{column}::numeric(20,8)",
|
||||
)
|
||||
|
||||
op.drop_column("portfolio_ledger", "action")
|
||||
op.create_index("idx_portfolio_ledger_portfolio_executed", "portfolio_ledger", ["portfolio_id", sa.text("executed_at DESC")])
|
||||
op.drop_table("ledger_trades")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.create_table(
|
||||
"ledger_trades",
|
||||
sa.Column("ledger_trade_id", sa.BigInteger(), primary_key=True),
|
||||
sa.Column("portfolio_id", sa.Text(), nullable=False),
|
||||
sa.Column("order_id", sa.Text(), nullable=False),
|
||||
sa.Column("execution_id", sa.Text(), nullable=False),
|
||||
sa.Column("side", sa.Text(), nullable=False),
|
||||
sa.Column("quantity", sa.Float(), nullable=False),
|
||||
sa.Column("price", sa.Float(), nullable=False),
|
||||
sa.Column("fee_amount", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("tax_amount", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("executed_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("recorded_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.UniqueConstraint("portfolio_id", "order_id", "execution_id", name="uq_ledger_trades_idempotency"),
|
||||
)
|
||||
op.add_column("portfolio_ledger", sa.Column("action", sa.Text(), nullable=True))
|
||||
op.execute("UPDATE portfolio_ledger SET action = upper(side) WHERE action IS NULL")
|
||||
op.alter_column("portfolio_ledger", "action", nullable=False)
|
||||
op.drop_index("idx_portfolio_ledger_portfolio_executed", table_name="portfolio_ledger")
|
||||
op.drop_column("portfolio_ledger", "executed_at")
|
||||
op.drop_column("portfolio_ledger", "side")
|
||||
op.drop_column("portfolio_ledger", "execution_id")
|
||||
op.drop_column("portfolio_ledger", "order_id")
|
||||
op.drop_column("portfolio_ledger", "ledger_entry_id")
|
||||
Reference in New Issue
Block a user