"""KOL A-tier: on-chain wallet tracking + daily holdings snapshots. Three new tables (standalone, no FK into Trump tables): kol_wallets — curated KOL wallet addresses (one row per chain-addr pair) kol_holdings_snapshots — daily portfolio snapshot per wallet (holdings_json) kol_holding_changes — detected significant moves (new / closed / ±25%) Source column distinguishes where the snapshot came from: "arkham" — Arkham Intelligence API portfolio endpoint "hl" — Hyperliquid public clearinghouseState (perps only) "manual" — hand-entered for testing Revision ID: 018 Revises: 017 Create Date: 2026-05-23 """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "018" down_revision: Union[str, None] = "017" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.create_table( "kol_wallets", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("handle", sa.String(64), nullable=False, index=True), sa.Column("chain", sa.String(16), nullable=False), # ethereum|solana|hl sa.Column("address", sa.String(128), nullable=False), sa.Column("label", sa.String(128), nullable=True), # e.g. "Arthur Hayes hot" sa.Column("source_url", sa.String(256), nullable=True), # Arkham entity URL for reference sa.Column("active", sa.Boolean(), nullable=False, server_default="1"), sa.Column("added_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.UniqueConstraint("chain", "address", name="uq_kol_wallet_chain_addr"), ) op.create_table( "kol_holdings_snapshots", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("wallet_id", sa.Integer(), sa.ForeignKey("kol_wallets.id"), nullable=False, index=True), sa.Column("snapshot_date",sa.String(10), nullable=False), # YYYY-MM-DD UTC sa.Column("holdings_json",sa.Text(), nullable=False), # [{ticker,amount,usd_value,chain}] sa.Column("total_usd", sa.Float(), nullable=True), sa.Column("source", sa.String(16), nullable=False), # arkham|hl|manual sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.UniqueConstraint("wallet_id", "snapshot_date", name="uq_kol_snapshot_wallet_date"), ) op.create_table( "kol_holding_changes", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("wallet_id", sa.Integer(), sa.ForeignKey("kol_wallets.id"), nullable=False, index=True), sa.Column("detected_at", sa.DateTime(), nullable=False, index=True), sa.Column("ticker", sa.String(32), nullable=False), # new_position | closed | increased | decreased sa.Column("change_type", sa.String(16), nullable=False), sa.Column("usd_before", sa.Float(), nullable=True), sa.Column("usd_after", sa.Float(), nullable=True), sa.Column("pct_change", sa.Float(), nullable=True), # signed % sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), ) def downgrade() -> None: op.drop_table("kol_holding_changes") op.drop_table("kol_holdings_snapshots") op.drop_table("kol_wallets")