feat: add standalone AI/Tech regime-change monitor tab
A new /regime tab scoring how far the AI/Tech bull regime has deteriorated toward a re-rating as a single 0-100 index with per-signal breakdown and a 7/30-day trend. Intentionally decoupled: nothing reads its output to gate or score trades — the daily-pipeline membership is scheduling only. - regime_monitor_service: price sub-scores (P1-P6 via Alpaca, like market_regime), VIX + HY credit spreads via a small FRED helper, weighted aggregation over available signals (missing source -> n/a, dropped from the denominator), one snapshot row/day, and a ~90-day history backfill by replaying the already-fetched series as-of each past day. - F1/F3 fundamentals proposed by the configured grounded LLM (reuses sentiment_provider_service config resolution), with a manual override + lock. - regime_snapshots table (migration 011); endpoints on the existing market router; admin-editable weights/threshold; standalone /regime page. Data needs: prices via Alpaca, VIX/credit via FRED (optional key — signals show n/a without it). No LLM needed for history. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"""add regime_snapshots table
|
||||
|
||||
Stores the daily AI/Tech regime-change index (one row per date) so the monitor
|
||||
tab can show a 7/30-day trend. Standalone, observational feature: no other table
|
||||
or job reads this.
|
||||
|
||||
Revision ID: 011
|
||||
Revises: 010
|
||||
Create Date: 2026-06-26 00:00:00.000000
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "011"
|
||||
down_revision: Union[str, None] = "010"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"regime_snapshots",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("date", sa.Date(), nullable=False),
|
||||
sa.Column("total_score", sa.Float(), nullable=False),
|
||||
sa.Column("band", sa.String(length=20), nullable=False),
|
||||
sa.Column("breakdown_json", sa.Text(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(
|
||||
"uq_regime_snapshots_date", "regime_snapshots", ["date"], unique=True
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index("uq_regime_snapshots_date", table_name="regime_snapshots")
|
||||
op.drop_table("regime_snapshots")
|
||||
Reference in New Issue
Block a user