"""Add invalidation_price fields for BTC bottom-reversal trades. Revision ID: 010 Revises: 009 Create Date: 2026-05-16 00:00:00.000000 NOTE: `bot_trades.eff_invalidation_price` is ALSO created by migration 009 (two_system_and_frozen_exits). The original 010 added it unconditionally, which made `alembic upgrade head` on a FRESH database fail at 010 with "duplicate column name: eff_invalidation_price" (009 already added it). This revision is now IDEMPOTENT: each column is added only if it is not already present, so it applies cleanly on both fresh DBs (009 already made eff_invalidation_price → skip; posts.invalidation_price is new → add) and DBs that pre-date 009's column. The genuinely-new column introduced by 010 is `posts.invalidation_price`; `eff_invalidation_price` is owned by 009 and left to 009's downgrade. """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op from sqlalchemy import inspect revision: str = "010" down_revision: Union[str, None] = "009" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def _columns(table: str) -> set: return {c["name"] for c in inspect(op.get_bind()).get_columns(table)} def upgrade() -> None: if "invalidation_price" not in _columns("posts"): with op.batch_alter_table("posts") as b: b.add_column(sa.Column("invalidation_price", sa.Float(), nullable=True)) # eff_invalidation_price may already exist (created by 009). Only add it # if some older DB reached 010 without it. if "eff_invalidation_price" not in _columns("bot_trades"): with op.batch_alter_table("bot_trades") as b: b.add_column(sa.Column("eff_invalidation_price", sa.Float(), nullable=True)) def downgrade() -> None: # Only undo 010's genuine addition. eff_invalidation_price is owned by # 009 — dropping it here would double-drop in `downgrade base`. if "invalidation_price" in _columns("posts"): with op.batch_alter_table("posts") as b: b.drop_column("invalidation_price")