898f89926d
Extends integration test coverage from 108 to 193 tests for the beta gate. New test modules: - test_query_api_extended.py (33 tests): documents, evidence, macro/competitive, ops/admin, agents, analytics - test_registry_write_paths.py (16 tests): write paths, validation, duplicates, competitor/exposure CRUD - test_risk_approval_lifecycle.py (8 tests): evaluation edge cases, full approval lifecycle - test_trading_extended.py (12 tests): config round-trips, decision filtering, override validation - test_cross_service_roundtrip.py (4 tests): cross-service data consistency - test_error_handling.py (12 tests): 404s, 422s, empty states, health checks Seed script extended with watchlists, approvals, lockouts, notifications, ingestion runs, saved queries, and daily risk snapshots.
1196 lines
57 KiB
Python
1196 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:
|
|
lockouts = [
|
|
(LOCKOUT_ACTIVE, "AAPL", "news_shock", "Earnings volatility cooldown",
|
|
BASE_TS + timedelta(days=7), BASE_TS),
|
|
(LOCKOUT_EXPIRED, "XOM", "cooldown", "Post-trade cooldown period",
|
|
BASE_TS - timedelta(days=1), BASE_TS - 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())
|