Files
stonks-oracle/tests/integration/seed_sandbox.py
T
Celes Renata 5acb2fb43e fix: resolve 6 integration test failures
1. patterns endpoint: fix query referencing non-existent column
   di.catalyst_type → dir.catalyst_type (column is on document_impact_records)
2. lockouts seed: use relative timestamps (now + 7d) so active lockout
   is always in the future regardless of when tests run
3. create_agent: make slug optional with auto-generation from name
4. create_source: json.dumps(config) + ::jsonb cast for asyncpg JSONB compat
5. approval_expiry: return count as int (len(expired)) not the list itself
6. metrics_consistency: fix test assertion to match API contract
   (total >= active + reserve, not total == active + reserve + unrealized)
2026-04-20 04:30:13 +00:00

1197 lines
57 KiB
Python

"""Deterministic seed data for integration test sandbox.
All UUIDs and timestamps are hardcoded for reproducible assertions.
No external API calls — all data is synthetic.
Usage:
python -m tests.integration.seed_sandbox
Environment variables:
POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD
"""
import asyncio
import json
import os
from datetime import date, datetime, timedelta, timezone
from uuid import UUID
import asyncpg
# ── Fixed base timestamp ─────────────────────────────────────
BASE_TS = datetime(2025, 1, 15, 12, 0, 0, tzinfo=timezone.utc)
BASE_DATE = date(2025, 1, 15)
# ── Deterministic UUIDs ──────────────────────────────────────
# Companies
COMPANY_AAPL = UUID("00000000-0000-4000-a000-000000000001")
COMPANY_MSFT = UUID("00000000-0000-4000-a000-000000000002")
COMPANY_JPM = UUID("00000000-0000-4000-a000-000000000003")
COMPANY_JNJ = UUID("00000000-0000-4000-a000-000000000004")
COMPANY_XOM = UUID("00000000-0000-4000-a000-000000000005")
# Sources (one per company)
SOURCE_AAPL = UUID("00000000-0000-4000-b000-000000000001")
SOURCE_MSFT = UUID("00000000-0000-4000-b000-000000000002")
SOURCE_JPM = UUID("00000000-0000-4000-b000-000000000003")
SOURCE_JNJ = UUID("00000000-0000-4000-b000-000000000004")
SOURCE_XOM = UUID("00000000-0000-4000-b000-000000000005")
# Company aliases
ALIAS_AAPL = UUID("00000000-0000-4000-b100-000000000001")
ALIAS_MSFT = UUID("00000000-0000-4000-b100-000000000002")
ALIAS_JPM = UUID("00000000-0000-4000-b100-000000000003")
ALIAS_JNJ = UUID("00000000-0000-4000-b100-000000000004")
ALIAS_XOM = UUID("00000000-0000-4000-b100-000000000005")
# Competitor relationships
COMPETITOR_REL_1 = UUID("00000000-0000-4000-b200-000000000001")
COMPETITOR_REL_2 = UUID("00000000-0000-4000-b200-000000000002")
# Documents (10)
DOC_01 = UUID("00000000-0000-4000-c000-000000000001")
DOC_02 = UUID("00000000-0000-4000-c000-000000000002")
DOC_03 = UUID("00000000-0000-4000-c000-000000000003")
DOC_04 = UUID("00000000-0000-4000-c000-000000000004")
DOC_05 = UUID("00000000-0000-4000-c000-000000000005")
DOC_06 = UUID("00000000-0000-4000-c000-000000000006")
DOC_07 = UUID("00000000-0000-4000-c000-000000000007")
DOC_08 = UUID("00000000-0000-4000-c000-000000000008")
DOC_09 = UUID("00000000-0000-4000-c000-000000000009")
DOC_10 = UUID("00000000-0000-4000-c000-000000000010")
# Document intelligence (one per document)
INTEL_01 = UUID("00000000-0000-4000-c100-000000000001")
INTEL_02 = UUID("00000000-0000-4000-c100-000000000002")
INTEL_03 = UUID("00000000-0000-4000-c100-000000000003")
INTEL_04 = UUID("00000000-0000-4000-c100-000000000004")
INTEL_05 = UUID("00000000-0000-4000-c100-000000000005")
INTEL_06 = UUID("00000000-0000-4000-c100-000000000006")
INTEL_07 = UUID("00000000-0000-4000-c100-000000000007")
INTEL_08 = UUID("00000000-0000-4000-c100-000000000008")
INTEL_09 = UUID("00000000-0000-4000-c100-000000000009")
INTEL_10 = UUID("00000000-0000-4000-c100-000000000010")
# Document impact records
IMPACT_01 = UUID("00000000-0000-4000-c200-000000000001")
IMPACT_02 = UUID("00000000-0000-4000-c200-000000000002")
IMPACT_03 = UUID("00000000-0000-4000-c200-000000000003")
IMPACT_04 = UUID("00000000-0000-4000-c200-000000000004")
IMPACT_05 = UUID("00000000-0000-4000-c200-000000000005")
IMPACT_06 = UUID("00000000-0000-4000-c200-000000000006")
IMPACT_07 = UUID("00000000-0000-4000-c200-000000000007")
IMPACT_08 = UUID("00000000-0000-4000-c200-000000000008")
IMPACT_09 = UUID("00000000-0000-4000-c200-000000000009")
IMPACT_10 = UUID("00000000-0000-4000-c200-000000000010")
# Document company mentions
MENTION_01 = UUID("00000000-0000-4000-c300-000000000001")
MENTION_02 = UUID("00000000-0000-4000-c300-000000000002")
MENTION_03 = UUID("00000000-0000-4000-c300-000000000003")
MENTION_04 = UUID("00000000-0000-4000-c300-000000000004")
MENTION_05 = UUID("00000000-0000-4000-c300-000000000005")
MENTION_06 = UUID("00000000-0000-4000-c300-000000000006")
MENTION_07 = UUID("00000000-0000-4000-c300-000000000007")
MENTION_08 = UUID("00000000-0000-4000-c300-000000000008")
MENTION_09 = UUID("00000000-0000-4000-c300-000000000009")
MENTION_10 = UUID("00000000-0000-4000-c300-000000000010")
# Trend windows (5)
TREND_01 = UUID("00000000-0000-4000-d000-000000000001")
TREND_02 = UUID("00000000-0000-4000-d000-000000000002")
TREND_03 = UUID("00000000-0000-4000-d000-000000000003")
TREND_04 = UUID("00000000-0000-4000-d000-000000000004")
TREND_05 = UUID("00000000-0000-4000-d000-000000000005")
# Trend projections (one per trend)
PROJECTION_01 = UUID("00000000-0000-4000-d100-000000000001")
PROJECTION_02 = UUID("00000000-0000-4000-d100-000000000002")
PROJECTION_03 = UUID("00000000-0000-4000-d100-000000000003")
PROJECTION_04 = UUID("00000000-0000-4000-d100-000000000004")
PROJECTION_05 = UUID("00000000-0000-4000-d100-000000000005")
# Recommendations (5)
REC_01 = UUID("00000000-0000-4000-e000-000000000001")
REC_02 = UUID("00000000-0000-4000-e000-000000000002")
REC_03 = UUID("00000000-0000-4000-e000-000000000003")
REC_04 = UUID("00000000-0000-4000-e000-000000000004")
REC_05 = UUID("00000000-0000-4000-e000-000000000005")
# Recommendation evidence (one per recommendation)
REC_EV_01 = UUID("00000000-0000-4000-e100-000000000001")
REC_EV_02 = UUID("00000000-0000-4000-e100-000000000002")
REC_EV_03 = UUID("00000000-0000-4000-e100-000000000003")
REC_EV_04 = UUID("00000000-0000-4000-e100-000000000004")
REC_EV_05 = UUID("00000000-0000-4000-e100-000000000005")
# Risk evaluations
RISK_EVAL_01 = UUID("00000000-0000-4000-e200-000000000001")
# Broker account
BROKER_ACCT_01 = UUID("00000000-0000-4000-f000-000000000001")
# Orders (3)
ORDER_01 = UUID("00000000-0000-4000-f100-000000000001")
ORDER_02 = UUID("00000000-0000-4000-f100-000000000002")
ORDER_03 = UUID("00000000-0000-4000-f100-000000000003")
# Order events
ORDER_EVT_01 = UUID("00000000-0000-4000-f200-000000000001")
ORDER_EVT_02 = UUID("00000000-0000-4000-f200-000000000002")
ORDER_EVT_03 = UUID("00000000-0000-4000-f200-000000000003")
ORDER_EVT_04 = UUID("00000000-0000-4000-f200-000000000004")
ORDER_EVT_05 = UUID("00000000-0000-4000-f200-000000000005")
# Positions (2)
POSITION_01 = UUID("00000000-0000-4000-f300-000000000001")
POSITION_02 = UUID("00000000-0000-4000-f300-000000000002")
# Global events (2)
GLOBAL_EVT_01 = UUID("00000000-0000-4000-a100-000000000001")
GLOBAL_EVT_02 = UUID("00000000-0000-4000-a100-000000000002")
# Macro impact records (4 — 2 per global event, across multiple companies)
MACRO_IMPACT_01 = UUID("00000000-0000-4000-a200-000000000001")
MACRO_IMPACT_02 = UUID("00000000-0000-4000-a200-000000000002")
MACRO_IMPACT_03 = UUID("00000000-0000-4000-a200-000000000003")
MACRO_IMPACT_04 = UUID("00000000-0000-4000-a200-000000000004")
# Exposure profiles (2)
EXPOSURE_01 = UUID("00000000-0000-4000-a300-000000000001")
EXPOSURE_02 = UUID("00000000-0000-4000-a300-000000000002")
# Competitive signal records (2)
COMP_SIGNAL_01 = UUID("00000000-0000-4000-a400-000000000001")
COMP_SIGNAL_02 = UUID("00000000-0000-4000-a400-000000000002")
# Trading decisions
TRADING_DECISION_01 = UUID("00000000-0000-4000-a500-000000000001")
# Portfolio snapshot
PORTFOLIO_SNAP_01 = UUID("00000000-0000-4000-a600-000000000001")
# AI agents (use slugs to match migration 026 seed rows)
AGENT_EXTRACTOR = UUID("00000000-0000-4000-a700-000000000001")
AGENT_CLASSIFIER = UUID("00000000-0000-4000-a700-000000000002")
AGENT_THESIS = UUID("00000000-0000-4000-a700-000000000003")
# Agent variants (1 per agent)
VARIANT_EXTRACTOR = UUID("00000000-0000-4000-a800-000000000001")
VARIANT_CLASSIFIER = UUID("00000000-0000-4000-a800-000000000002")
VARIANT_THESIS = UUID("00000000-0000-4000-a800-000000000003")
# Agent performance log entries
PERF_LOG_01 = UUID("00000000-0000-4000-a900-000000000001")
PERF_LOG_02 = UUID("00000000-0000-4000-a900-000000000002")
PERF_LOG_03 = UUID("00000000-0000-4000-a900-000000000003")
# Risk config
RISK_CONFIG_01 = UUID("00000000-0000-4000-aa00-000000000001")
# Audit events
AUDIT_01 = UUID("00000000-0000-4000-ab00-000000000001")
AUDIT_02 = UUID("00000000-0000-4000-ab00-000000000002")
AUDIT_03 = UUID("00000000-0000-4000-ab00-000000000003")
# Watchlists
WATCHLIST_01 = UUID("00000000-0000-4000-ac00-000000000001")
WATCHLIST_02 = UUID("00000000-0000-4000-ac00-000000000002")
# Watchlist members
WL_MEMBER_01 = UUID("00000000-0000-4000-ac10-000000000001")
WL_MEMBER_02 = UUID("00000000-0000-4000-ac10-000000000002")
WL_MEMBER_03 = UUID("00000000-0000-4000-ac10-000000000003")
# Operator Approvals
APPROVAL_PENDING = UUID("00000000-0000-4000-ad00-000000000001")
APPROVAL_APPROVED = UUID("00000000-0000-4000-ad00-000000000002")
# Symbol Lockouts
LOCKOUT_ACTIVE = UUID("00000000-0000-4000-ae00-000000000001")
LOCKOUT_EXPIRED = UUID("00000000-0000-4000-ae00-000000000002")
# Notifications
NOTIFICATION_01 = UUID("00000000-0000-4000-af00-000000000001")
# Ingestion Runs
INGESTION_RUN_01 = UUID("00000000-0000-4000-b300-000000000001")
INGESTION_RUN_02 = UUID("00000000-0000-4000-b300-000000000002")
# Saved Queries
SAVED_QUERY_01 = UUID("00000000-0000-4000-b400-000000000001")
# Daily Risk Snapshot
RISK_SNAPSHOT_01 = UUID("00000000-0000-4000-b500-000000000001")
# ── Exported lookup dicts for test imports ────────────────────
SEED_COMPANY_IDS = {
"AAPL": str(COMPANY_AAPL),
"MSFT": str(COMPANY_MSFT),
"JPM": str(COMPANY_JPM),
"JNJ": str(COMPANY_JNJ),
"XOM": str(COMPANY_XOM),
}
SEED_DOCUMENT_IDS = {
f"DOC_{i:02d}": str(uid)
for i, uid in enumerate(
[DOC_01, DOC_02, DOC_03, DOC_04, DOC_05,
DOC_06, DOC_07, DOC_08, DOC_09, DOC_10],
start=1,
)
}
SEED_TREND_IDS = {
f"TREND_{i:02d}": str(uid)
for i, uid in enumerate(
[TREND_01, TREND_02, TREND_03, TREND_04, TREND_05], start=1
)
}
SEED_RECOMMENDATION_IDS = {
f"REC_{i:02d}": str(uid)
for i, uid in enumerate(
[REC_01, REC_02, REC_03, REC_04, REC_05], start=1
)
}
SEED_ORDER_IDS = {
f"ORDER_{i:02d}": str(uid)
for i, uid in enumerate([ORDER_01, ORDER_02, ORDER_03], start=1)
}
SEED_POSITION_IDS = {
f"POS_{i:02d}": str(uid)
for i, uid in enumerate([POSITION_01, POSITION_02], start=1)
}
SEED_GLOBAL_EVENT_IDS = {
f"EVT_{i:02d}": str(uid)
for i, uid in enumerate([GLOBAL_EVT_01, GLOBAL_EVT_02], start=1)
}
SEED_AGENT_IDS = {
"extractor": str(AGENT_EXTRACTOR),
"classifier": str(AGENT_CLASSIFIER),
"thesis": str(AGENT_THESIS),
}
SEED_VARIANT_IDS = {
"extractor": str(VARIANT_EXTRACTOR),
"classifier": str(VARIANT_CLASSIFIER),
"thesis": str(VARIANT_THESIS),
}
SEED_BROKER_ACCOUNT_ID = str(BROKER_ACCT_01)
SEED_TRADING_DECISION_ID = str(TRADING_DECISION_01)
SEED_PORTFOLIO_SNAPSHOT_ID = str(PORTFOLIO_SNAP_01)
SEED_RISK_CONFIG_ID = str(RISK_CONFIG_01)
SEED_WATCHLIST_IDS = {
"WL_01": str(WATCHLIST_01),
"WL_02": str(WATCHLIST_02),
}
SEED_APPROVAL_IDS = {
"PENDING": str(APPROVAL_PENDING),
"APPROVED": str(APPROVAL_APPROVED),
}
SEED_LOCKOUT_IDS = {
"ACTIVE": str(LOCKOUT_ACTIVE),
"EXPIRED": str(LOCKOUT_EXPIRED),
}
SEED_NOTIFICATION_IDS = {"NOTIF_01": str(NOTIFICATION_01)}
SEED_INGESTION_RUN_IDS = {
"RUN_01": str(INGESTION_RUN_01),
"RUN_02": str(INGESTION_RUN_02),
}
SEED_SAVED_QUERY_IDS = {"SQ_01": str(SAVED_QUERY_01)}
SEED_RISK_SNAPSHOT_IDS = {"SNAP_01": str(RISK_SNAPSHOT_01)}
# ── Seed function ─────────────────────────────────────────────
async def seed() -> None:
"""Populate the database with deterministic test data."""
dsn = (
f"postgresql://{os.environ['POSTGRES_USER']}"
f":{os.environ['POSTGRES_PASSWORD']}"
f"@{os.environ['POSTGRES_HOST']}"
f":{os.environ.get('POSTGRES_PORT', '5432')}"
f"/{os.environ['POSTGRES_DB']}"
)
conn = await asyncpg.connect(dsn)
try:
await _seed_companies(conn)
await _seed_sources(conn)
await _seed_aliases(conn)
await _seed_competitor_relationships(conn)
await _seed_documents(conn)
await _seed_document_mentions(conn)
await _seed_document_intelligence(conn)
await _seed_document_impact_records(conn)
await _seed_trend_windows(conn)
await _seed_trend_projections(conn)
await _seed_recommendations(conn)
await _seed_recommendation_evidence(conn)
await _seed_risk_evaluations(conn)
await _seed_broker_accounts(conn)
await _seed_orders(conn)
await _seed_order_events(conn)
await _seed_positions(conn)
await _seed_global_events(conn)
await _seed_macro_impact_records(conn)
await _seed_exposure_profiles(conn)
await _seed_competitive_signals(conn)
await _seed_trading_engine_config(conn)
await _seed_trading_decisions(conn)
await _seed_portfolio_snapshots(conn)
await _seed_ai_agents(conn)
await _seed_agent_variants(conn)
await _seed_agent_performance_log(conn)
await _seed_risk_configs(conn)
await _seed_audit_events(conn)
await _seed_watchlists(conn)
await _seed_operator_approvals(conn)
await _seed_symbol_lockouts(conn)
await _seed_notifications(conn)
await _seed_ingestion_runs(conn)
await _seed_saved_queries(conn)
await _seed_daily_risk_snapshots(conn)
finally:
await conn.close()
# ── Companies ─────────────────────────────────────────────────
async def _seed_companies(conn: asyncpg.Connection) -> None:
companies = [
(COMPANY_AAPL, "AAPL", "Apple Inc", "NASDAQ", "Technology", "Consumer Electronics", "mega", True, BASE_TS),
(COMPANY_MSFT, "MSFT", "Microsoft Corp", "NASDAQ", "Technology", "Software - Infrastructure", "mega", True, BASE_TS),
(COMPANY_JPM, "JPM", "JPMorgan Chase & Co", "NYSE", "Financial Services", "Banks - Diversified", "mega", True, BASE_TS),
(COMPANY_JNJ, "JNJ", "Johnson & Johnson", "NYSE", "Healthcare", "Drug Manufacturers", "mega", True, BASE_TS),
(COMPANY_XOM, "XOM", "Exxon Mobil Corp", "NYSE", "Energy", "Oil & Gas Integrated", "mega", True, BASE_TS),
]
await conn.executemany(
"""INSERT INTO companies (id, ticker, legal_name, exchange, sector, industry, market_cap_bucket, active, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT DO NOTHING""",
companies,
)
# ── Sources ───────────────────────────────────────────────────
async def _seed_sources(conn: asyncpg.Connection) -> None:
sources = [
(SOURCE_AAPL, COMPANY_AAPL, "news", "Polygon News", json.dumps({"provider": "polygon"}), 0.8, True, BASE_TS),
(SOURCE_MSFT, COMPANY_MSFT, "news", "Polygon News", json.dumps({"provider": "polygon"}), 0.8, True, BASE_TS),
(SOURCE_JPM, COMPANY_JPM, "filing", "SEC EDGAR", json.dumps({"cik": "0000019617"}), 0.95, True, BASE_TS),
(SOURCE_JNJ, COMPANY_JNJ, "news", "Polygon News", json.dumps({"provider": "polygon"}), 0.8, True, BASE_TS),
(SOURCE_XOM, COMPANY_XOM, "news", "Polygon News", json.dumps({"provider": "polygon"}), 0.8, True, BASE_TS),
]
await conn.executemany(
"""INSERT INTO sources (id, company_id, source_type, source_name, config, credibility_score, active, created_at)
VALUES ($1, $2, $3, $4, $5::jsonb, $6, $7, $8)
ON CONFLICT DO NOTHING""",
sources,
)
# ── Aliases ───────────────────────────────────────────────────
async def _seed_aliases(conn: asyncpg.Connection) -> None:
aliases = [
(ALIAS_AAPL, COMPANY_AAPL, "Apple", "brand", BASE_TS),
(ALIAS_MSFT, COMPANY_MSFT, "Microsoft", "brand", BASE_TS),
(ALIAS_JPM, COMPANY_JPM, "JP Morgan", "brand", BASE_TS),
(ALIAS_JNJ, COMPANY_JNJ, "J&J", "brand", BASE_TS),
(ALIAS_XOM, COMPANY_XOM, "Exxon", "brand", BASE_TS),
]
await conn.executemany(
"""INSERT INTO company_aliases (id, company_id, alias, alias_type, created_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT DO NOTHING""",
aliases,
)
# ── Competitor Relationships ──────────────────────────────────
async def _seed_competitor_relationships(conn: asyncpg.Connection) -> None:
rels = [
(COMPETITOR_REL_1, COMPANY_AAPL, COMPANY_MSFT, "direct_rival", 0.85, True, "manual", True, BASE_TS),
(COMPETITOR_REL_2, COMPANY_JPM, COMPANY_JNJ, "same_sector", 0.3, True, "inferred", True, BASE_TS),
]
await conn.executemany(
"""INSERT INTO competitor_relationships
(id, company_a_id, company_b_id, relationship_type, strength, bidirectional, source, active, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT DO NOTHING""",
rels,
)
# ── Documents (10) ────────────────────────────────────────────
async def _seed_documents(conn: asyncpg.Connection) -> None:
# Mix: 6 news, 2 filings, 2 macro_event
docs = [
(DOC_01, "news", "news", "Reuters", "https://example.com/aapl-1", "Apple Q4 Earnings Beat", BASE_TS - timedelta(days=5), "hash_doc_01", "ingested", BASE_TS),
(DOC_02, "news", "news", "Bloomberg", "https://example.com/msft-1", "Microsoft Cloud Revenue Surges", BASE_TS - timedelta(days=4), "hash_doc_02", "processed", BASE_TS),
(DOC_03, "filing", "filing", "SEC EDGAR", "https://sec.gov/jpm-10k", "JPM Annual Report 10-K", BASE_TS - timedelta(days=10), "hash_doc_03", "processed", BASE_TS),
(DOC_04, "news", "news", "CNBC", "https://example.com/jnj-1", "J&J Drug Trial Results", BASE_TS - timedelta(days=3), "hash_doc_04", "processed", BASE_TS),
(DOC_05, "news", "news", "Reuters", "https://example.com/xom-1", "Exxon Oil Production Update", BASE_TS - timedelta(days=2), "hash_doc_05", "processed", BASE_TS),
(DOC_06, "news", "news", "WSJ", "https://example.com/aapl-2", "Apple Vision Pro Sales", BASE_TS - timedelta(days=1), "hash_doc_06", "processed", BASE_TS),
(DOC_07, "filing", "filing", "SEC EDGAR", "https://sec.gov/msft-10q", "MSFT Quarterly Filing 10-Q", BASE_TS - timedelta(days=8), "hash_doc_07", "processed", BASE_TS),
(DOC_08, "macro_event", "news", "Reuters", "https://example.com/fed-1", "Fed Rate Decision January 2025", BASE_TS - timedelta(days=6), "hash_doc_08", "processed", BASE_TS),
(DOC_09, "macro_event", "news", "Bloomberg", "https://example.com/trade-1", "US-China Trade Tensions Escalate", BASE_TS - timedelta(days=7), "hash_doc_09", "processed", BASE_TS),
(DOC_10, "news", "news", "MarketWatch", "https://example.com/jpm-2", "JPM Investment Banking Revenue", BASE_TS - timedelta(days=1), "hash_doc_10", "processed", BASE_TS),
]
await conn.executemany(
"""INSERT INTO documents
(id, document_type, source_type, publisher, url, title, published_at, content_hash, status, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT DO NOTHING""",
docs,
)
# ── Document Company Mentions ─────────────────────────────────
async def _seed_document_mentions(conn: asyncpg.Connection) -> None:
mentions = [
(MENTION_01, DOC_01, COMPANY_AAPL, "AAPL", "direct", 0.95, BASE_TS),
(MENTION_02, DOC_02, COMPANY_MSFT, "MSFT", "direct", 0.95, BASE_TS),
(MENTION_03, DOC_03, COMPANY_JPM, "JPM", "direct", 0.90, BASE_TS),
(MENTION_04, DOC_04, COMPANY_JNJ, "JNJ", "direct", 0.90, BASE_TS),
(MENTION_05, DOC_05, COMPANY_XOM, "XOM", "direct", 0.90, BASE_TS),
(MENTION_06, DOC_06, COMPANY_AAPL, "AAPL", "direct", 0.95, BASE_TS),
(MENTION_07, DOC_07, COMPANY_MSFT, "MSFT", "direct", 0.90, BASE_TS),
(MENTION_08, DOC_08, COMPANY_JPM, "JPM", "indirect", 0.60, BASE_TS),
(MENTION_09, DOC_09, COMPANY_AAPL, "AAPL", "indirect", 0.50, BASE_TS),
(MENTION_10, DOC_10, COMPANY_JPM, "JPM", "direct", 0.90, BASE_TS),
]
await conn.executemany(
"""INSERT INTO document_company_mentions
(id, document_id, company_id, ticker, mention_type, confidence, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT DO NOTHING""",
mentions,
)
# ── Document Intelligence ─────────────────────────────────────
async def _seed_document_intelligence(conn: asyncpg.Connection) -> None:
intels = [
(INTEL_01, DOC_01, "Apple beats Q4 expectations with strong iPhone sales.", 0.85, "ollama", "qwen3.5:9b", "document-intel-v2", "2.0.0", "valid", BASE_TS),
(INTEL_02, DOC_02, "Microsoft Azure revenue grows 29% year-over-year.", 0.90, "ollama", "qwen3.5:9b", "document-intel-v2", "2.0.0", "valid", BASE_TS),
(INTEL_03, DOC_03, "JPMorgan reports record annual profit driven by investment banking.", 0.88, "ollama", "qwen3.5:9b", "document-intel-v2", "2.0.0", "valid", BASE_TS),
(INTEL_04, DOC_04, "J&J Phase 3 trial shows positive results for new cancer drug.", 0.82, "ollama", "qwen3.5:9b", "document-intel-v2", "2.0.0", "valid", BASE_TS),
(INTEL_05, DOC_05, "Exxon increases Permian Basin output by 15%.", 0.78, "ollama", "qwen3.5:9b", "document-intel-v2", "2.0.0", "valid", BASE_TS),
(INTEL_06, DOC_06, "Apple Vision Pro sees moderate adoption in enterprise segment.", 0.75, "ollama", "qwen3.5:9b", "document-intel-v2", "2.0.0", "valid", BASE_TS),
(INTEL_07, DOC_07, "Microsoft quarterly filing shows strong cloud and AI growth.", 0.87, "ollama", "qwen3.5:9b", "document-intel-v2", "2.0.0", "valid", BASE_TS),
(INTEL_08, DOC_08, "Fed holds rates steady, signals potential cuts in Q2.", 0.92, "ollama", "qwen3.5:9b", "event-classification-v1", "1.0.0", "valid", BASE_TS),
(INTEL_09, DOC_09, "US-China trade tensions rise with new tariff proposals.", 0.88, "ollama", "qwen3.5:9b", "event-classification-v1", "1.0.0", "valid", BASE_TS),
(INTEL_10, DOC_10, "JPM investment banking fees up 20% in Q4.", 0.80, "ollama", "qwen3.5:9b", "document-intel-v2", "2.0.0", "valid", BASE_TS),
]
await conn.executemany(
"""INSERT INTO document_intelligence
(id, document_id, summary, confidence, model_provider, model_name, prompt_version, schema_version, validation_status, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT DO NOTHING""",
intels,
)
# ── Document Impact Records ───────────────────────────────────
async def _seed_document_impact_records(conn: asyncpg.Connection) -> None:
impacts = [
(IMPACT_01, INTEL_01, COMPANY_AAPL, "AAPL", 0.9, "positive", 0.8, "short_term", "earnings", json.dumps(["Strong iPhone sales"]), json.dumps([]), json.dumps(["beat expectations"]), BASE_TS),
(IMPACT_02, INTEL_02, COMPANY_MSFT, "MSFT", 0.9, "positive", 0.85, "medium_term", "growth", json.dumps(["Azure 29% growth"]), json.dumps([]), json.dumps(["cloud revenue surge"]), BASE_TS),
(IMPACT_03, INTEL_03, COMPANY_JPM, "JPM", 0.85, "positive", 0.7, "short_term", "earnings", json.dumps(["Record profit"]), json.dumps(["Rate sensitivity"]), json.dumps(["investment banking"]), BASE_TS),
(IMPACT_04, INTEL_04, COMPANY_JNJ, "JNJ", 0.8, "positive", 0.75, "long_term", "product", json.dumps(["Phase 3 success"]), json.dumps(["Regulatory risk"]), json.dumps(["cancer drug trial"]), BASE_TS),
(IMPACT_05, INTEL_05, COMPANY_XOM, "XOM", 0.8, "positive", 0.6, "medium_term", "operational", json.dumps(["Production increase"]), json.dumps(["Oil price risk"]), json.dumps(["Permian Basin"]), BASE_TS),
(IMPACT_06, INTEL_06, COMPANY_AAPL, "AAPL", 0.7, "neutral", 0.4, "medium_term", "product", json.dumps(["Enterprise adoption"]), json.dumps(["Consumer demand weak"]), json.dumps(["Vision Pro"]), BASE_TS),
(IMPACT_07, INTEL_07, COMPANY_MSFT, "MSFT", 0.85, "positive", 0.8, "medium_term", "growth", json.dumps(["AI and cloud growth"]), json.dumps([]), json.dumps(["quarterly filing"]), BASE_TS),
(IMPACT_08, INTEL_08, COMPANY_JPM, "JPM", 0.6, "positive", 0.5, "medium_term", "macro", json.dumps(["Rate cut signal"]), json.dumps(["Inflation risk"]), json.dumps(["Fed decision"]), BASE_TS),
(IMPACT_09, INTEL_09, COMPANY_AAPL, "AAPL", 0.5, "negative", -0.3, "short_term", "geopolitical", json.dumps(["Supply chain risk"]), json.dumps(["Tariff impact"]), json.dumps(["trade tensions"]), BASE_TS),
(IMPACT_10, INTEL_10, COMPANY_JPM, "JPM", 0.85, "positive", 0.65, "short_term", "earnings", json.dumps(["IB fees up 20%"]), json.dumps([]), json.dumps(["investment banking"]), BASE_TS),
]
await conn.executemany(
"""INSERT INTO document_impact_records
(id, intelligence_id, company_id, ticker, relevance, sentiment, impact_score,
impact_horizon, catalyst_type, key_facts, risks, evidence_spans, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, $11::jsonb, $12::jsonb, $13)
ON CONFLICT DO NOTHING""",
impacts,
)
# ── Trend Windows (5) ────────────────────────────────────────
async def _seed_trend_windows(conn: asyncpg.Connection) -> None:
# entity_id is VARCHAR, not UUID
trends = [
(TREND_01, "company", str(COMPANY_AAPL), "7d", "bullish", 0.72, 0.80,
json.dumps([{"fact": "Strong iPhone sales", "weight": 0.9}]),
json.dumps([{"fact": "Trade tensions", "weight": 0.3}]),
json.dumps(["earnings", "product"]), json.dumps(["tariff_risk"]),
0.15, json.dumps({"market_phase": "expansion"}), BASE_TS, BASE_TS),
(TREND_02, "company", str(COMPANY_MSFT), "7d", "bullish", 0.85, 0.88,
json.dumps([{"fact": "Azure growth 29%", "weight": 0.95}]),
json.dumps([]),
json.dumps(["growth", "cloud"]), json.dumps([]),
0.05, json.dumps({"market_phase": "expansion"}), BASE_TS, BASE_TS),
(TREND_03, "company", str(COMPANY_JPM), "7d", "bullish", 0.60, 0.70,
json.dumps([{"fact": "Record profit", "weight": 0.8}]),
json.dumps([{"fact": "Rate sensitivity", "weight": 0.4}]),
json.dumps(["earnings"]), json.dumps(["interest_rate_risk"]),
0.25, json.dumps({"market_phase": "stable"}), BASE_TS, BASE_TS),
(TREND_04, "company", str(COMPANY_JNJ), "30d", "bullish", 0.55, 0.65,
json.dumps([{"fact": "Drug trial success", "weight": 0.75}]),
json.dumps([{"fact": "Regulatory risk", "weight": 0.3}]),
json.dumps(["product"]), json.dumps(["regulatory"]),
0.20, json.dumps({"market_phase": "stable"}), BASE_TS, BASE_TS),
(TREND_05, "company", str(COMPANY_XOM), "7d", "mixed", 0.40, 0.55,
json.dumps([{"fact": "Production increase", "weight": 0.6}]),
json.dumps([{"fact": "Oil price volatility", "weight": 0.5}]),
json.dumps(["operational"]), json.dumps(["commodity_risk"]),
0.35, json.dumps({"market_phase": "uncertain"}), BASE_TS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO trend_windows
(id, entity_type, entity_id, "window", trend_direction, trend_strength, confidence,
top_supporting_evidence, top_opposing_evidence, dominant_catalysts, material_risks,
contradiction_score, market_context, generated_at, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb, $10::jsonb, $11::jsonb,
$12, $13::jsonb, $14, $15)
ON CONFLICT DO NOTHING""",
trends,
)
# ── Trend Projections ─────────────────────────────────────────
async def _seed_trend_projections(conn: asyncpg.Connection) -> None:
projections = [
(PROJECTION_01, TREND_01, "bullish", 0.70, 0.75, "7d", json.dumps(["earnings_momentum"]), 0.10, False, BASE_TS),
(PROJECTION_02, TREND_02, "bullish", 0.88, 0.85, "7d", json.dumps(["cloud_growth"]), 0.05, False, BASE_TS),
(PROJECTION_03, TREND_03, "bullish", 0.55, 0.60, "7d", json.dumps(["banking_recovery"]), 0.20, False, BASE_TS),
(PROJECTION_04, TREND_04, "bullish", 0.50, 0.58, "30d", json.dumps(["drug_pipeline"]), 0.15, False, BASE_TS),
(PROJECTION_05, TREND_05, "bearish", 0.45, 0.50, "7d", json.dumps(["oil_price_decline"]), 0.30, True, BASE_TS),
]
await conn.executemany(
"""INSERT INTO trend_projections
(id, trend_window_id, projected_direction, projected_strength, projected_confidence,
projection_horizon, driving_factors, macro_contribution_pct, diverges_from_current, computed_at)
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9, $10)
ON CONFLICT DO NOTHING""",
projections,
)
# ── Recommendations (5) ──────────────────────────────────────
async def _seed_recommendations(conn: asyncpg.Connection) -> None:
recs = [
(REC_01, "AAPL", COMPANY_AAPL, "buy", "autonomous", 0.80, "7d", "Strong earnings momentum with iPhone sales beat.", json.dumps(["Trade war escalation"]), 0.03, 0.01, "v1.0", BASE_TS, BASE_TS),
(REC_02, "MSFT", COMPANY_MSFT, "buy", "autonomous", 0.88, "14d", "Azure cloud growth accelerating with AI tailwinds.", json.dumps(["Cloud competition"]), 0.04, 0.008, "v1.0", BASE_TS, BASE_TS),
(REC_03, "JPM", COMPANY_JPM, "watch", "informational", 0.65, "7d", "Mixed signals: strong IB but rate uncertainty.", json.dumps(["Rate hike"]), 0.02, 0.005, "v1.0", BASE_TS, BASE_TS),
(REC_04, "JNJ", COMPANY_JNJ, "buy", "paper", 0.70, "30d", "Positive drug trial results support long-term thesis.", json.dumps(["FDA rejection"]), 0.025, 0.007, "v1.0", BASE_TS, BASE_TS),
(REC_05, "XOM", COMPANY_XOM, "sell", "informational", 0.55, "7d", "Oil price headwinds outweigh production gains.", json.dumps(["Oil price spike"]), 0.02, 0.01, "v1.0", BASE_TS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO recommendations
(id, ticker, company_id, action, mode, confidence, time_horizon, thesis,
invalidation_conditions, portfolio_pct, max_loss_pct, model_version, generated_at, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10, $11, $12, $13, $14)
ON CONFLICT DO NOTHING""",
recs,
)
# ── Recommendation Evidence ───────────────────────────────────
async def _seed_recommendation_evidence(conn: asyncpg.Connection) -> None:
evidence = [
(REC_EV_01, REC_01, DOC_01, INTEL_01, "supporting", 0.9, BASE_TS),
(REC_EV_02, REC_02, DOC_02, INTEL_02, "supporting", 0.95, BASE_TS),
(REC_EV_03, REC_03, DOC_03, INTEL_03, "supporting", 0.7, BASE_TS),
(REC_EV_04, REC_04, DOC_04, INTEL_04, "supporting", 0.8, BASE_TS),
(REC_EV_05, REC_05, DOC_05, INTEL_05, "opposing", 0.6, BASE_TS),
]
await conn.executemany(
"""INSERT INTO recommendation_evidence
(id, recommendation_id, document_id, intelligence_id, evidence_type, weight, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT DO NOTHING""",
evidence,
)
# ── Risk Evaluations ──────────────────────────────────────────
async def _seed_risk_evaluations(conn: asyncpg.Connection) -> None:
await conn.execute(
"""INSERT INTO risk_evaluations
(id, recommendation_id, eligible, allowed_mode, rejection_reasons, risk_checks, evaluated_at)
VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, $7)
ON CONFLICT DO NOTHING""",
RISK_EVAL_01, REC_01, True, "autonomous", json.dumps([]),
json.dumps({"portfolio_heat": "pass", "sector_exposure": "pass", "daily_loss": "pass"}),
BASE_TS,
)
# ── Broker Accounts ───────────────────────────────────────────
async def _seed_broker_accounts(conn: asyncpg.Connection) -> None:
await conn.execute(
"""INSERT INTO broker_accounts (id, provider, account_id, mode, config, active, created_at)
VALUES ($1, $2, $3, $4, $5::jsonb, $6, $7)
ON CONFLICT DO NOTHING""",
BROKER_ACCT_01, "alpaca", "PAPER-001", "paper",
json.dumps({"base_url": "https://paper-api.alpaca.markets"}),
True, BASE_TS,
)
# ── Orders (3: filled, pending, cancelled) ───────────────────
async def _seed_orders(conn: asyncpg.Connection) -> None:
orders = [
# Filled order
(ORDER_01, REC_01, BROKER_ACCT_01, "AAPL", "buy", "market", 10, None, None,
"filled", "inttest-order-001", "broker-ord-001",
json.dumps({"reason": "earnings_momentum"}),
BASE_TS, BASE_TS + timedelta(seconds=5), BASE_TS + timedelta(seconds=30),
None, None, None, 185.50, 10, BASE_TS, BASE_TS),
# Pending order
(ORDER_02, REC_02, BROKER_ACCT_01, "MSFT", "buy", "limit", 5, 410.00, None,
"pending", "inttest-order-002", None,
json.dumps({"reason": "cloud_growth"}),
BASE_TS, None, None,
None, None, None, None, None, BASE_TS, BASE_TS),
# Cancelled order
(ORDER_03, REC_05, BROKER_ACCT_01, "XOM", "sell", "market", 20, None, None,
"cancelled", "inttest-order-003", "broker-ord-003",
json.dumps({"reason": "oil_headwinds"}),
BASE_TS, BASE_TS + timedelta(seconds=3), None,
BASE_TS + timedelta(minutes=5), None, None, None, None, BASE_TS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO orders
(id, recommendation_id, broker_account_id, ticker, side, order_type, quantity,
limit_price, stop_price, status, idempotency_key, broker_order_id, decision_trace,
submitted_at, acknowledged_at, filled_at, cancelled_at, rejected_at, rejection_reason,
fill_price, fill_quantity, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13::jsonb,
$14, $15, $16, $17, $18, $19, $20, $21, $22, $23)
ON CONFLICT DO NOTHING""",
orders,
)
# ── Order Events ──────────────────────────────────────────────
async def _seed_order_events(conn: asyncpg.Connection) -> None:
events = [
(ORDER_EVT_01, ORDER_01, "submitted", json.dumps({"qty": 10}), BASE_TS, BASE_TS),
(ORDER_EVT_02, ORDER_01, "acknowledged", json.dumps({"broker_id": "broker-ord-001"}), BASE_TS + timedelta(seconds=5), BASE_TS + timedelta(seconds=5)),
(ORDER_EVT_03, ORDER_01, "filled", json.dumps({"fill_price": 185.50, "fill_qty": 10}), BASE_TS + timedelta(seconds=30), BASE_TS + timedelta(seconds=30)),
(ORDER_EVT_04, ORDER_02, "submitted", json.dumps({"qty": 5, "limit": 410.00}), BASE_TS, BASE_TS),
(ORDER_EVT_05, ORDER_03, "cancelled", json.dumps({"reason": "user_request"}), BASE_TS + timedelta(minutes=5), BASE_TS + timedelta(minutes=5)),
]
await conn.executemany(
"""INSERT INTO order_events (id, order_id, event_type, data, broker_timestamp, created_at)
VALUES ($1, $2, $3, $4::jsonb, $5, $6)
ON CONFLICT DO NOTHING""",
events,
)
# ── Positions (2) ─────────────────────────────────────────────
async def _seed_positions(conn: asyncpg.Connection) -> None:
positions = [
(POSITION_01, BROKER_ACCT_01, "AAPL", 10, 185.50, 192.30, 68.00, 0.0, BASE_TS),
(POSITION_02, BROKER_ACCT_01, "MSFT", 15, 405.00, 412.75, 116.25, 50.00, BASE_TS),
]
await conn.executemany(
"""INSERT INTO positions
(id, broker_account_id, ticker, quantity, avg_entry_price, current_price,
unrealized_pnl, realized_pnl, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT DO NOTHING""",
positions,
)
# ── Global Events (2) ────────────────────────────────────────
async def _seed_global_events(conn: asyncpg.Connection) -> None:
events = [
(GLOBAL_EVT_01, ["interest_rate_decision"], "medium",
["North America"], ["Financial Services", "Real Estate"],
[], "Federal Reserve holds rates steady, signals potential cuts in Q2 2025.",
json.dumps(["Fed holds rates", "Potential Q2 cuts", "Inflation moderating"]),
"weeks", 0.88, DOC_08, "ollama", "qwen3.5:9b", "event-classification-v1", "1.0.0", BASE_TS),
(GLOBAL_EVT_02, ["trade_war", "tariff"], "high",
["North America", "East Asia"], ["Technology", "Consumer Electronics"],
[], "US-China trade tensions escalate with new tariff proposals on tech imports.",
json.dumps(["New tariffs proposed", "Tech sector targeted", "Supply chain disruption"]),
"months", 0.82, DOC_09, "ollama", "qwen3.5:9b", "event-classification-v1", "1.0.0", BASE_TS),
]
await conn.executemany(
"""INSERT INTO global_events
(id, event_types, severity, affected_regions, affected_sectors, affected_commodities,
summary, key_facts, estimated_duration, confidence, source_document_id,
model_provider, model_name, prompt_version, schema_version, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9, $10, $11, $12, $13, $14, $15, $16)
ON CONFLICT DO NOTHING""",
events,
)
# ── Macro Impact Records (4) ─────────────────────────────────
async def _seed_macro_impact_records(conn: asyncpg.Connection) -> None:
records = [
# Fed rate decision impacts JPM and JNJ
(MACRO_IMPACT_01, GLOBAL_EVT_01, COMPANY_JPM, "JPM", 0.75, "positive",
json.dumps(["Rate-sensitive banking sector benefits"]), 0.80, BASE_TS),
(MACRO_IMPACT_02, GLOBAL_EVT_01, COMPANY_JNJ, "JNJ", 0.25, "neutral",
json.dumps(["Healthcare less rate-sensitive"]), 0.70, BASE_TS),
# Trade tensions impact AAPL and MSFT
(MACRO_IMPACT_03, GLOBAL_EVT_02, COMPANY_AAPL, "AAPL", -0.60, "negative",
json.dumps(["China manufacturing exposure", "Tariff on electronics"]), 0.85, BASE_TS),
(MACRO_IMPACT_04, GLOBAL_EVT_02, COMPANY_MSFT, "MSFT", -0.30, "negative",
json.dumps(["Cloud infrastructure less exposed"]), 0.75, BASE_TS),
]
await conn.executemany(
"""INSERT INTO macro_impact_records
(id, event_id, company_id, ticker, macro_impact_score, impact_direction,
contributing_factors, confidence, computed_at)
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9)
ON CONFLICT DO NOTHING""",
records,
)
# ── Exposure Profiles (2) ────────────────────────────────────
async def _seed_exposure_profiles(conn: asyncpg.Connection) -> None:
profiles = [
(EXPOSURE_01, COMPANY_AAPL,
json.dumps({"North America": 0.45, "Europe": 0.25, "China": 0.20, "Rest of Asia": 0.10}),
["China", "Taiwan", "India"], ["semiconductors", "rare_earth"],
["US", "EU", "China"], "global_leader", 0.55, "manual", 1.0, 1, True, BASE_TS, BASE_TS),
(EXPOSURE_02, COMPANY_JPM,
json.dumps({"North America": 0.70, "Europe": 0.20, "Asia": 0.10}),
["North America", "Europe"], [],
["US", "UK", "EU"], "global_leader", 0.30, "manual", 1.0, 1, True, BASE_TS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO exposure_profiles
(id, company_id, geographic_revenue_mix, supply_chain_regions, key_input_commodities,
regulatory_jurisdictions, market_position_tier, export_dependency_pct,
source, confidence, version, active, created_at, updated_at)
VALUES ($1, $2, $3::jsonb, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT DO NOTHING""",
profiles,
)
# ── Competitive Signal Records (2) ───────────────────────────
async def _seed_competitive_signals(conn: asyncpg.Connection) -> None:
signals = [
(COMP_SIGNAL_01, DOC_01, "AAPL", "MSFT", "earnings_beat", 0.75,
"positive", 0.6, 0.85, BASE_TS),
(COMP_SIGNAL_02, DOC_05, "XOM", "JPM", "production_change", 0.50,
"neutral", 0.3, 0.30, BASE_TS),
]
await conn.executemany(
"""INSERT INTO competitive_signal_records
(id, source_document_id, source_ticker, target_ticker, catalyst_type,
pattern_confidence, signal_direction, signal_strength, relationship_strength, computed_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT DO NOTHING""",
signals,
)
# ── Trading Engine Config (UPDATE existing row from migration 018) ─
async def _seed_trading_engine_config(conn: asyncpg.Connection) -> None:
# Migration 018 inserts a default row. Update it rather than insert.
await conn.execute(
"""UPDATE trading_engine_config
SET enabled = TRUE,
paused = FALSE,
risk_tier = 'moderate',
max_open_positions = 10,
updated_at = $1""",
BASE_TS,
)
# ── Trading Decisions ─────────────────────────────────────────
async def _seed_trading_decisions(conn: asyncpg.Connection) -> None:
await conn.execute(
"""INSERT INTO trading_decisions
(id, recommendation_id, decision, ticker, computed_position_size,
computed_share_quantity, risk_tier_at_decision, portfolio_heat_at_decision,
active_pool_at_decision, reserve_pool_at_decision, circuit_breaker_status,
correlation_check_result, sector_exposure_check_result,
earnings_proximity_flag, is_micro_trade, decision_trace, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11,
$12::jsonb, $13::jsonb, $14, $15, $16::jsonb, $17)
ON CONFLICT DO NOTHING""",
TRADING_DECISION_01, REC_01, "execute", "AAPL", 1855.00, 10,
"moderate", 0.15, 10000.00, 2500.00, "inactive",
json.dumps({"correlated_tickers": [], "max_correlation": 0.0}),
json.dumps({"Technology": 0.15}),
False, False,
json.dumps({"recommendation_id": str(REC_01), "action": "buy", "confidence": 0.80}),
BASE_TS,
)
# ── Portfolio Snapshots ───────────────────────────────────────
async def _seed_portfolio_snapshots(conn: asyncpg.Connection) -> None:
await conn.execute(
"""INSERT INTO portfolio_snapshots
(id, snapshot_date, portfolio_value, active_pool, reserve_pool,
daily_return, cumulative_return, unrealized_pnl, realized_pnl,
win_count, loss_count, win_rate, sharpe_ratio, max_drawdown,
current_drawdown_pct, portfolio_heat, risk_tier, positions, metrics, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17,
$18::jsonb, $19::jsonb, $20)
ON CONFLICT DO NOTHING""",
PORTFOLIO_SNAP_01, BASE_DATE, 12500.00, 10000.00, 2500.00,
0.012, 0.025, 184.25, 50.00,
3, 1, 0.75, 1.45, 0.03, 0.01, 0.15, "moderate",
json.dumps([
{"ticker": "AAPL", "quantity": 10, "unrealized_pnl": 68.00},
{"ticker": "MSFT", "quantity": 15, "unrealized_pnl": 116.25},
]),
json.dumps({"total_trades": 4, "avg_hold_days": 5}),
BASE_TS,
)
# ── AI Agents (3 — match migration 026 slugs, use ON CONFLICT) ─
async def _seed_ai_agents(conn: asyncpg.Connection) -> None:
# Migration 026 seeds these by slug. We insert with our deterministic IDs
# using ON CONFLICT on slug to capture the ID if already present.
# First, try to insert; if slug exists, just update the id isn't possible
# so we delete-and-reinsert with our IDs for test determinism.
# Safer approach: delete existing system agents and re-insert with our IDs.
await conn.execute(
"DELETE FROM agent_performance_log WHERE agent_id IN (SELECT id FROM ai_agents WHERE slug IN ('document-extractor', 'event-classifier', 'thesis-rewriter'))"
)
await conn.execute(
"DELETE FROM agent_variants WHERE agent_id IN (SELECT id FROM ai_agents WHERE slug IN ('document-extractor', 'event-classifier', 'thesis-rewriter'))"
)
await conn.execute(
"DELETE FROM ai_agents WHERE slug IN ('document-extractor', 'event-classifier', 'thesis-rewriter')"
)
agents = [
(AGENT_EXTRACTOR, "Document Intelligence Extractor", "document-extractor",
"Extracts structured intelligence from documents.",
"ollama", "qwen3.5:9b-fast", "You are a financial document analyst.", "document-intel-v2", "2.0.0",
0.0, 32768, 120, 2, True, "system", BASE_TS, BASE_TS),
(AGENT_CLASSIFIER, "Global Event Classifier", "event-classifier",
"Classifies global news into structured macro events.",
"ollama", "qwen3.5:9b-fast", "You classify MACRO-LEVEL global news.", "event-classification-v1", "1.0.0",
0.0, 32768, 120, 2, True, "system", BASE_TS, BASE_TS),
(AGENT_THESIS, "Thesis Rewriter", "thesis-rewriter",
"Rewrites trade thesis summaries into professional prose.",
"ollama", "qwen3.5:9b-fast", "You are a concise financial analyst.", "thesis-rewrite-v1", "1.0.0",
0.0, 32768, 120, 2, True, "system", BASE_TS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO ai_agents
(id, name, slug, purpose, model_provider, model_name, system_prompt,
prompt_version, schema_version, temperature, max_tokens, timeout_seconds,
max_retries, active, source, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)""",
agents,
)
# ── Agent Variants (1 per agent) ─────────────────────────────
async def _seed_agent_variants(conn: asyncpg.Connection) -> None:
variants = [
(VARIANT_EXTRACTOR, AGENT_EXTRACTOR, "Extractor GPT-4o Variant", "extractor-gpt4o",
"Testing GPT-4o for extraction quality comparison.",
"openai", "gpt-4o", "You are a financial document analyst.", "",
"document-intel-v2-gpt4o", 0.1, 16384, 0, 0, 0, 60, 3, False, BASE_TS, BASE_TS),
(VARIANT_CLASSIFIER, AGENT_CLASSIFIER, "Classifier Claude Variant", "classifier-claude",
"Testing Claude for event classification.",
"anthropic", "claude-sonnet-4-20250514", "You classify MACRO-LEVEL global news.", "",
"event-classification-v1-claude", 0.0, 16384, 0, 0, 0, 90, 2, False, BASE_TS, BASE_TS),
(VARIANT_THESIS, AGENT_THESIS, "Thesis GPT-4o-mini Variant", "thesis-gpt4o-mini",
"Testing GPT-4o-mini for thesis rewriting cost efficiency.",
"openai", "gpt-4o-mini", "You are a concise financial analyst.", "",
"thesis-rewrite-v1-mini", 0.2, 8192, 0, 0, 0, 30, 2, False, BASE_TS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO agent_variants
(id, agent_id, variant_name, variant_slug, description,
model_provider, model_name, system_prompt, user_prompt_template,
prompt_version, temperature, max_tokens, context_window,
input_token_limit, token_budget, timeout_seconds, max_retries,
is_active, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
ON CONFLICT DO NOTHING""",
variants,
)
# ── Agent Performance Log ─────────────────────────────────────
async def _seed_agent_performance_log(conn: asyncpg.Connection) -> None:
logs = [
(PERF_LOG_01, AGENT_EXTRACTOR, DOC_01, "AAPL", True, 2500, 0.85, 0, 1200, 800, None, VARIANT_EXTRACTOR, BASE_TS),
(PERF_LOG_02, AGENT_CLASSIFIER, DOC_08, None, True, 1800, 0.88, 0, 900, 600, None, VARIANT_CLASSIFIER, BASE_TS),
(PERF_LOG_03, AGENT_THESIS, None, "AAPL", True, 1200, 0.90, 0, 500, 300, None, VARIANT_THESIS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO agent_performance_log
(id, agent_id, document_id, ticker, success, duration_ms, confidence,
retry_count, input_tokens, output_tokens, error_message, variant_id, recorded_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
ON CONFLICT DO NOTHING""",
logs,
)
# ── Risk Configs ──────────────────────────────────────────────
async def _seed_risk_configs(conn: asyncpg.Connection) -> None:
config = json.dumps({
"max_portfolio_heat": 0.25,
"max_single_position_pct": 0.05,
"max_sector_concentration": 0.30,
"daily_loss_limit_pct": 0.03,
"macro_enabled": True,
"competitive_enabled": True,
})
await conn.execute(
"""INSERT INTO risk_configs (id, name, trading_mode, config, active, created_at, updated_at)
VALUES ($1, $2, $3, $4::jsonb, $5, $6, $7)
ON CONFLICT DO NOTHING""",
RISK_CONFIG_01, "inttest-default", "paper", config, True, BASE_TS, BASE_TS,
)
# ── Audit Events ─────────────────────────────────────────────
async def _seed_audit_events(conn: asyncpg.Connection) -> None:
events = [
(AUDIT_01, "order.submitted", "order", ORDER_01, "system",
json.dumps({"ticker": "AAPL", "side": "buy", "qty": 10}), BASE_TS),
(AUDIT_02, "order.filled", "order", ORDER_01, "system",
json.dumps({"ticker": "AAPL", "fill_price": 185.50, "fill_qty": 10}),
BASE_TS + timedelta(seconds=30)),
(AUDIT_03, "order.cancelled", "order", ORDER_03, "system",
json.dumps({"ticker": "XOM", "reason": "user_request"}),
BASE_TS + timedelta(minutes=5)),
]
await conn.executemany(
"""INSERT INTO audit_events (id, event_type, entity_type, entity_id, actor, data, created_at)
VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7)
ON CONFLICT DO NOTHING""",
events,
)
# ── Watchlists ────────────────────────────────────────────────
async def _seed_watchlists(conn: asyncpg.Connection) -> None:
watchlists = [
(WATCHLIST_01, "Tech Leaders", "Top technology companies", True, BASE_TS, BASE_TS),
(WATCHLIST_02, "Value Picks", "Undervalued large caps", True, BASE_TS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO watchlists (id, name, description, active, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT DO NOTHING""",
watchlists,
)
members = [
(WL_MEMBER_01, WATCHLIST_01, COMPANY_AAPL, BASE_TS),
(WL_MEMBER_02, WATCHLIST_01, COMPANY_MSFT, BASE_TS),
(WL_MEMBER_03, WATCHLIST_02, COMPANY_JPM, BASE_TS),
]
await conn.executemany(
"""INSERT INTO watchlist_members (id, watchlist_id, company_id, added_at)
VALUES ($1, $2, $3, $4)
ON CONFLICT DO NOTHING""",
members,
)
# ── Operator Approvals ────────────────────────────────────────
async def _seed_operator_approvals(conn: asyncpg.Connection) -> None:
approvals = [
(APPROVAL_PENDING, json.dumps({"ticker": "AAPL", "side": "buy", "qty": 5}),
REC_01, "AAPL", "buy", 5, 927.50, "pending", RISK_EVAL_01,
"system", None, None,
BASE_TS + timedelta(hours=24), # expires in 24h
BASE_TS, None, BASE_TS, BASE_TS),
(APPROVAL_APPROVED, json.dumps({"ticker": "MSFT", "side": "buy", "qty": 3}),
REC_02, "MSFT", "buy", 3, 1230.00, "approved", None,
"system", "test-operator", "Approved for paper trading",
BASE_TS + timedelta(hours=24),
BASE_TS - timedelta(hours=2), BASE_TS - timedelta(hours=1),
BASE_TS, BASE_TS),
]
await conn.executemany(
"""INSERT INTO operator_approvals
(id, order_job, recommendation_id, ticker, side, quantity, estimated_value,
status, risk_evaluation_id, requested_by, reviewed_by, review_note,
expires_at, requested_at, reviewed_at, created_at, updated_at)
VALUES ($1, $2::jsonb, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
ON CONFLICT DO NOTHING""",
approvals,
)
# ── Symbol Lockouts ───────────────────────────────────────────
async def _seed_symbol_lockouts(conn: asyncpg.Connection) -> None:
now = datetime.now(timezone.utc)
lockouts = [
(LOCKOUT_ACTIVE, "AAPL", "news_shock", "Earnings volatility cooldown",
now + timedelta(days=7), now - timedelta(hours=1)),
(LOCKOUT_EXPIRED, "XOM", "cooldown", "Post-trade cooldown period",
now - timedelta(days=1), now - timedelta(days=3)),
]
await conn.executemany(
"""INSERT INTO symbol_lockouts (id, ticker, lockout_type, reason, expires_at, created_at)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT DO NOTHING""",
lockouts,
)
# ── Notifications ─────────────────────────────────────────────
async def _seed_notifications(conn: asyncpg.Connection) -> None:
await conn.execute(
"""INSERT INTO notifications
(id, channel, event_type, message, delivery_status, retry_count, error_message, created_at, delivered_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT DO NOTHING""",
NOTIFICATION_01, "email", "order.filled",
"Order filled: AAPL buy 10 shares at $185.50",
"delivered", 0, None, BASE_TS, BASE_TS + timedelta(seconds=5),
)
# ── Ingestion Runs ────────────────────────────────────────────
async def _seed_ingestion_runs(conn: asyncpg.Connection) -> None:
runs = [
(INGESTION_RUN_01, SOURCE_AAPL, COMPANY_AAPL, "news", "completed",
BASE_TS - timedelta(hours=2), BASE_TS - timedelta(hours=1, minutes=55),
15, 8, None, 0, None),
(INGESTION_RUN_02, SOURCE_JPM, COMPANY_JPM, "filing", "failed",
BASE_TS - timedelta(hours=1), None,
0, 0, "Connection timeout to SEC EDGAR", 2,
BASE_TS + timedelta(hours=1)),
]
await conn.executemany(
"""INSERT INTO ingestion_runs
(id, source_id, company_id, source_type, status, started_at, completed_at,
items_fetched, items_new, error_message, retry_count, next_retry_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT DO NOTHING""",
runs,
)
# ── Saved Queries ─────────────────────────────────────────────
async def _seed_saved_queries(conn: asyncpg.Connection) -> None:
await conn.execute(
"""INSERT INTO saved_queries (id, name, description, sql_text, created_by, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT DO NOTHING""",
SAVED_QUERY_01, "Top Recommendations",
"Shows highest confidence recommendations",
"SELECT ticker, action, confidence FROM recommendations ORDER BY confidence DESC LIMIT 10",
"operator", BASE_TS, BASE_TS,
)
# ── Daily Risk Snapshots ──────────────────────────────────────
async def _seed_daily_risk_snapshots(conn: asyncpg.Connection) -> None:
await conn.execute(
"""INSERT INTO daily_risk_snapshots
(id, account_id, snapshot_date, portfolio_value, daily_pnl,
daily_trade_count, positions_by_sector, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9)
ON CONFLICT DO NOTHING""",
RISK_SNAPSHOT_01, "PAPER-001", BASE_DATE, 12500.00, 150.25,
4, json.dumps({"Technology": 0.45, "Financial Services": 0.30, "Energy": 0.25}),
BASE_TS, BASE_TS,
)
# ── Entry point ───────────────────────────────────────────────
if __name__ == "__main__":
asyncio.run(seed())