"""kol_divergence table — talks-vs-trades cross-signal Revision ID: 019 Revises: 018 Create Date: 2026-05-23 """ from alembic import op import sqlalchemy as sa revision = "019" down_revision = "018" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "kol_divergence", sa.Column("id", sa.Integer, primary_key=True), # Which KOL + asset sa.Column("handle", sa.String(64), nullable=False, index=True), sa.Column("ticker", sa.String(32), nullable=False), # Content side (B-tier) sa.Column("post_id", sa.Integer, sa.ForeignKey("kol_posts.id"), nullable=False), sa.Column("post_action", sa.String(16), nullable=False), # buy|sell|bullish|bearish sa.Column("post_conviction", sa.Float, nullable=True), sa.Column("post_at", sa.DateTime, nullable=False), # post published_at # On-chain side (A-tier) sa.Column("change_id", sa.Integer, sa.ForeignKey("kol_holding_changes.id"), nullable=False), sa.Column("onchain_action", sa.String(16), nullable=False), # new_position|increased|decreased|closed sa.Column("usd_before", sa.Float, nullable=True), sa.Column("usd_after", sa.Float, nullable=True), sa.Column("onchain_at", sa.DateTime, nullable=False), # Classification sa.Column("signal_type", sa.String(16), nullable=False), # divergence | alignment sa.Column("direction", sa.String(8), nullable=True), # long | short (net conclusion) sa.Column("days_apart", sa.Float, nullable=True), # abs days between post and onchain event sa.Column("created_at", sa.DateTime, nullable=False, server_default=sa.func.now()), # Unique: one row per (post, change) pair so re-runs are idempotent sa.UniqueConstraint("post_id", "change_id", name="uq_kol_divergence_pair"), ) op.create_index("ix_kol_divergence_handle_ticker", "kol_divergence", ["handle", "ticker"]) op.create_index("ix_kol_divergence_signal_type", "kol_divergence", ["signal_type"]) op.create_index("ix_kol_divergence_created_at", "kol_divergence", ["created_at"]) def downgrade() -> None: op.drop_table("kol_divergence")