Deine Datenbankstruktur sollte genau auf die IBKR-Web-API abgestimmt sein – vor allem auf den zentralen conid (Contract ID).
Ich habe mir die komplette Web-API-Dokumentation zum Tag „Trading-Contracts“ (und die damit verknüpften Endpoints /iserver/secdef/search, /trsrv/stocks, /trsrv/secdef etc.) angeschaut. Die wichtigsten Erkenntnisse daraus:

Symbol allein reicht nie (AAPL gibt es z. B. auf NASDAQ, MEXI, LSE etc.).
Der conid ist der einzige stabile, eindeutige Schlüssel in IBKR (z. B. AAPL US Stock = conid: 265598). Er ändert sich nie und wird für alle Market-Data-, Order- und Positions-Calls verwendet.
IBKR liefert über die Search-Endpoints automatisch companyName, sector, primaryExchange, validExchanges etc. mit – das kannst du direkt in die DB schreiben.
Historische Daten (und alle Trades) referenzieren immer nur den conid.

Genau das nutzen wir für die Datenbank. Ich schlage eine saubere, skalierbare und zukunftssichere Struktur vor (PostgreSQL + TimescaleDB für die Zeitreihen ist ideal, funktioniert aber auch mit normalem Postgres).
1. Kern-Tabellen (relational)
SQL-- 1. Instruments = IBKR-Contract (der zentrale „Single Source of Truth“)
CREATE TABLE instruments (
    id                  BIGSERIAL PRIMARY KEY,
    ibkr_conid          BIGINT NOT NULL UNIQUE,           -- wichtigster Key!
    symbol              VARCHAR(20) NOT NULL,
    sec_type            VARCHAR(10) NOT NULL,              -- STK, OPT, FUT, BOND ...
    exchange            VARCHAR(20) NOT NULL,              -- NASDAQ, SMART, NYSE ...
    primary_exchange    VARCHAR(20),
    currency            VARCHAR(3) NOT NULL,
    company_name        TEXT,
    local_symbol        VARCHAR(20),
    trading_class       VARCHAR(20),
    isin                VARCHAR(12),                       -- oft nützlich
    sector              VARCHAR(100),
    industry            VARCHAR(100),
    description         TEXT,
    has_options         BOOLEAN,
    asset_class         VARCHAR(10),
    listing_exchange    VARCHAR(20),
    last_fetched        TIMESTAMPTZ DEFAULT NOW(),
    active              BOOLEAN DEFAULT true,
    custom_metadata     JSONB,                            -- flexibler Platz für alles, was du später brauchst
    created_at          TIMESTAMPTZ DEFAULT NOW(),
    updated_at          TIMESTAMPTZ DEFAULT NOW()
);

-- 2. External Identifiers = Verknüpfung zu allen anderen Quellen
CREATE TABLE external_identifiers (
    id                BIGSERIAL PRIMARY KEY,
    instrument_id     BIGINT REFERENCES instruments(id) ON DELETE CASCADE,
    source            VARCHAR(50) NOT NULL,          -- 'yahoo', 'alpha_vantage', 'bloomberg', 'web_scrape', 'manual', 'tradingview' ...
    ticker            VARCHAR(50) NOT NULL,
    additional_params JSONB,                          -- z. B. {'exchange': 'NYSE', 'region': 'US'}
    priority          SMALLINT DEFAULT 1,             -- falls mehrere Ticker für dieselbe Quelle existieren
    last_verified     TIMESTAMPTZ,
    UNIQUE (instrument_id, source, ticker)
);

-- Index für schnelle Lookups
CREATE INDEX idx_external_source_ticker ON external_identifiers(source, ticker);
CREATE INDEX idx_instrument_conid ON instruments(ibkr_conid);
Warum diese Trennung?
Du bekommst z. B. von einer Web-Analyse oder einem Scraping-Tool den Ticker „AAPL“. Du schaust in external_identifiers nach → findest sofort das instrument_id → hast den ibkr_conid und alle IBKR-spezifischen Daten.
2. Eigene Informationen & Analysen (sehr flexibel)
Du kannst zwei Wege gehen – ich empfehle beide gleichzeitig:
Variante A (einfach & schnell): Die Spalte custom_metadata JSONB in der instruments-Tabelle (siehe oben). Dort kannst du beliebig strukturierte eigene Daten speichern:
JSON{
  "ai_sentiment_score": 0.87,
  "my_strategy_rating": "strong_buy",
  "notes": "Apple AI-Investitionen 2025 sehr interessant",
  "last_ai_analysis": "2026-05-10",
  "custom_pe_target": 32.5,
  "watchlist_tags": ["growth", "tech", "ai"]
}
Variante B (noch flexibler, für sehr viele eigene Felder): Eine separate EAV-Tabelle:
SQLCREATE TABLE instrument_custom_data (
    id                BIGSERIAL PRIMARY KEY,
    instrument_id     BIGINT REFERENCES instruments(id) ON DELETE CASCADE,
    key               VARCHAR(100) NOT NULL,      -- z. B. 'ai_sentiment', 'my_risk_score'
    value             JSONB,
    source            VARCHAR(20),                -- 'user', 'gpt-4o', 'claude' ...
    created_at        TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE (instrument_id, key)
);
3. Historische Marktdaten (Time-Series)
SQL-- Mit TimescaleDB (empfohlen) oder normaler Postgres-Tabelle
CREATE TABLE market_data (
    instrument_id   BIGINT REFERENCES instruments(id) ON DELETE CASCADE,
    data_source     VARCHAR(20) DEFAULT 'ibkr',     -- 'ibkr', 'yahoo', 'polygon' ...
    bar_size        VARCHAR(10) NOT NULL,           -- '1min', '5min', '15min', '1hour', 'daily', 'weekly' ...
    timestamp       TIMESTAMPTZ NOT NULL,
    open            NUMERIC(12,6),
    high            NUMERIC(12,6),
    low             NUMERIC(12,6),
    close           NUMERIC(12,6),
    volume          BIGINT,
    wap             NUMERIC(12,6),                  -- weighted average price (IBKR)
    count           INTEGER,                        -- Anzahl Trades in der Bar
    additional_data JSONB,                          -- falls andere Quellen mehr Felder liefern
    PRIMARY KEY (instrument_id, data_source, bar_size, timestamp)
);

-- Timescale-Hypertable (sehr performant bei Millionen von Zeilen)
SELECT create_hypertable('market_data', 'timestamp', chunk_time_interval => INTERVAL '7 days');
Zukunftssicherheit:
Durch data_source kannst du später problemlos Polygon, Yahoo, Alpha Vantage etc. hinzufügen, ohne die Struktur zu ändern.
4. Zusätzliche nützliche Tabellen (optional, aber sehr empfehlenswert)

data_imports → Log-Tabelle (wann welcher Ticker von welcher Quelle importiert wurde, Status, Fehler)
fundamental_data → Bilanz-/Gewinn- und Verlustdaten (kann auch als JSONB in instruments oder eigene Tabelle)
orders / trades → Deine automatisch platzierten IBKR-Orders (mit ibkr_conid, Order-ID, Status etc.)
watchlists / portfolios → Deine eigenen Watchlists mit eigenen Tags

Praktische Umsetzungstipps

Mapping-Prozess (wichtigster Teil deines Tools):
Neue externe Ticker kommen herein → erst /iserver/secdef/search?symbol=... oder /trsrv/stocks?symbols=... aufrufen.
Ergebnis in instruments speichern (falls neu) + Mapping in external_identifiers.
Danach immer nur noch über ibkr_conid arbeiten.

Performance & Skalierung:
ibkr_conid + symbol + exchange stark indizieren.
Für sehr viele Instrumente (>> 10.000) lohnt sich eine separate instrument_aliases oder Cache-Layer (Redis).

Flexibilität für weitere Datenquellen:
Immer data_source und external_identifiers benutzen → du bist komplett unabhängig von IBKR als einziger Datenquelle.

Eigene Infos für Apple & Co.:
Einfach in custom_metadata oder instrument_custom_data schreiben. Die AI kann dort direkt Ergebnisse ablegen.


Diese Struktur ist praxisbewährt (ähnliche Schemata laufen bei vielen algo-Tradern mit IBKR + externen Daten). Sie ist gleichzeitig schlank und extrem erweiterbar.
Wenn du möchtest, kann ich dir:

das komplette SQL-Skript (mit allen Indizes, Triggers, Timescale-Setup),
ein Python-Beispiel mit SQLAlchemy + IBKR-WebAPI-Client zum automatischen Mapping,
oder ein ER-Diagramm (Mermaid) liefern.

Sag einfach Bescheid, wo du als Nächstes tiefer einsteigen willst!