c85c0068a2
- Replace all datetime.utcnow() with datetime.now(tz=timezone.utc) across 8 files - Fix 12 failing tests to match current implementation behavior - Fix pytest_plugins in non-top-level conftest (moved to root conftest.py) - Auto-fix 189 lint issues (import sorting, unused imports) - Add CI/CD pipeline infrastructure (ARC, ArgoCD, Kargo manifests) - Add values-beta.yaml and values-paper.yaml for staged deployments - Update GitHub Actions workflow to use self-hosted-gremlin runners - Add integration-test job to CI pipeline Result: 1596 passed, 0 failed, 0 warnings
997 lines
49 KiB
Python
997 lines
49 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")
|
|
|
|
# ── 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 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)
|
|
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,
|
|
)
|
|
|
|
|
|
# ── Entry point ───────────────────────────────────────────────
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(seed())
|