COMPLETE: Real-time unusual activity detection for congressional tickers
New Database Model:
- MarketAlert: Stores unusual market activity alerts
* Tracks volume spikes, price movements, volatility
* JSON details field for flexible data storage
* Severity scoring (1-10 scale)
* Indexed for efficient queries by ticker/timestamp
New Modules:
- src/pote/monitoring/market_monitor.py: Core monitoring engine
* get_congressional_watchlist(): Top 50 most-traded tickers
* check_ticker(): Analyze single stock for unusual activity
* scan_watchlist(): Batch analysis of multiple tickers
* Detection logic:
- Unusual volume (3x average)
- Price spikes/drops (>5%)
- High volatility (2x normal)
* save_alerts(): Persist to database
* get_recent_alerts(): Query historical alerts
- src/pote/monitoring/alert_manager.py: Alert formatting & filtering
* format_alert_text(): Human-readable output
* format_alert_html(): HTML email format
* filter_alerts(): By severity, ticker, type
* generate_summary_report(): Text/HTML reports
Scripts:
- scripts/monitor_market.py: CLI monitoring tool
* Continuous monitoring mode (--interval)
* One-time scan (--once)
* Custom ticker lists or auto-detect congressional watchlist
* Severity filtering (--min-severity)
* Report generation and saving
Migrations:
- alembic/versions/f44014715b40_add_market_alerts_table.py
Documentation:
- docs/11_live_market_monitoring.md: Complete explanation
* Why you can't track WHO is trading
* What IS possible (timing analysis)
* How hybrid monitoring works
* Data sources and APIs
Usage:
# Monitor congressional tickers (one-time scan)
python scripts/monitor_market.py --once
# Continuous monitoring (every 5 minutes)
python scripts/monitor_market.py --interval 300
# Monitor specific tickers
python scripts/monitor_market.py --tickers NVDA,MSFT,AAPL --once
Next Steps (Phase 2):
- Disclosure correlation engine
- Timing advantage calculator
- Suspicious trade flagging
54 lines
2.2 KiB
Python
54 lines
2.2 KiB
Python
"""Add market_alerts table
|
|
|
|
Revision ID: f44014715b40
|
|
Revises: 099810723175
|
|
Create Date: 2025-12-15 15:08:35.934280
|
|
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = 'f44014715b40'
|
|
down_revision: Union[str, Sequence[str], None] = '099810723175'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
"""Upgrade schema."""
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
op.create_table('market_alerts',
|
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
|
sa.Column('ticker', sa.String(length=20), nullable=False),
|
|
sa.Column('alert_type', sa.String(length=50), nullable=False),
|
|
sa.Column('timestamp', sa.DateTime(), nullable=False),
|
|
sa.Column('details', sa.JSON(), nullable=True),
|
|
sa.Column('price', sa.DECIMAL(precision=15, scale=4), nullable=True),
|
|
sa.Column('volume', sa.Integer(), nullable=True),
|
|
sa.Column('change_pct', sa.DECIMAL(precision=10, scale=4), nullable=True),
|
|
sa.Column('severity', sa.Integer(), nullable=True),
|
|
sa.Column('source', sa.String(length=50), nullable=False),
|
|
sa.Column('created_at', sa.DateTime(), nullable=False),
|
|
sa.PrimaryKeyConstraint('id')
|
|
)
|
|
op.create_index('ix_market_alerts_alert_type', 'market_alerts', ['alert_type'], unique=False)
|
|
op.create_index(op.f('ix_market_alerts_ticker'), 'market_alerts', ['ticker'], unique=False)
|
|
op.create_index('ix_market_alerts_ticker_timestamp', 'market_alerts', ['ticker', 'timestamp'], unique=False)
|
|
op.create_index(op.f('ix_market_alerts_timestamp'), 'market_alerts', ['timestamp'], unique=False)
|
|
# ### end Alembic commands ###
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Downgrade schema."""
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
op.drop_index(op.f('ix_market_alerts_timestamp'), table_name='market_alerts')
|
|
op.drop_index('ix_market_alerts_ticker_timestamp', table_name='market_alerts')
|
|
op.drop_index(op.f('ix_market_alerts_ticker'), table_name='market_alerts')
|
|
op.drop_index('ix_market_alerts_alert_type', table_name='market_alerts')
|
|
op.drop_table('market_alerts')
|
|
# ### end Alembic commands ###
|