Back to Blog
technical-referencepostgresqldatabaserunbooktradingaccess

Trading Database — Access Runbook

Runbook for accessing the DGX trading PostgreSQL database: connection methods, read-only credentials, query examples, and maintenance procedures.

December 8, 2025·11 min read

Trading Database — Access Runbook

Database: trading (PostgreSQL 15)
Host container: trading-postgres
Last updated: 2026-05-05


Connection Details

Parameter Value
Host (LAN) [DB-HOST-LAN-IP]
Host (Tailscale) [DB-HOST-TAILSCALE-IP]
Port 5432
Database trading
Auth method scram-sha-256 (password)
SSL not required (plain TCP within trusted network)

The port is bound to 0.0.0.0:5432 on the DGX host — reachable from any system on the same LAN or Tailscale mesh.


Credentials

Two read-only accounts have been provisioned. Both are members of the trading_reader role which grants SELECT on all current and future tables in the public schema.

System Username Default password
miraiclaw [db-user] [YOUR-DB-PASSWORD]
miraistudio [db-user-2] [YOUR-DB-PASSWORD]

Change these passwords immediately after first connection:

ALTER USER [db-user] PASSWORD 'your-strong-password';
ALTER USER [db-user-2] PASSWORD 'your-strong-password';

Permissions: SELECT only. No INSERT, UPDATE, DELETE, CREATE, or DROP.


Connecting

psql (CLI)

psql -h [DB-HOST-LAN-IP] -p 5432 -U [db-user] -d trading
# or via Tailscale
psql -h [DB-HOST-TAILSCALE-IP] -p 5432 -U [db-user] -d trading

Python — psycopg2

import psycopg2

conn = psycopg2.connect(
    host="[DB-HOST-LAN-IP]",
    port=5432,
    dbname="trading",
    user="[db-user]",
    password="[YOUR-DB-PASSWORD]",
)
cur = conn.cursor()
cur.execute("SELECT ticker, date, close FROM market_data_daily ORDER BY date DESC LIMIT 5")
rows = cur.fetchall()
conn.close()

Python — SQLAlchemy

from sqlalchemy import create_engine

engine = create_engine(
    "postgresql+psycopg2://[db-user]:[YOUR-DB-PASSWORD]@[DB-HOST-LAN-IP]:5432/trading"
)
with engine.connect() as conn:
    result = conn.execute("SELECT * FROM tqqq_signals ORDER BY signal_date DESC LIMIT 1")
    print(result.fetchall())

Python — pandas

import pandas as pd
import psycopg2

conn = psycopg2.connect(
    host="[DB-HOST-LAN-IP]", port=5432, dbname="trading",
    user="[db-user]", password="[YOUR-DB-PASSWORD]"
)
df = pd.read_sql(
    "SELECT * FROM market_data_daily WHERE ticker = 'NVDA' ORDER BY date DESC LIMIT 30",
    conn
)
conn.close()

Node.js — pg

const { Pool } = require('pg');

const pool = new Pool({
  host: '[DB-HOST-LAN-IP]',
  port: 5432,
  database: 'trading',
  user: '[db-user-2]',
  password: '[YOUR-DB-PASSWORD]',
});

const res = await pool.query(
  "SELECT * FROM tqqq_signals ORDER BY signal_date DESC LIMIT 1"
);
console.log(res.rows);
await pool.end();

Store credentials in .env and load them at runtime rather than hardcoding:

DB_HOST=[DB-HOST-LAN-IP]
DB_PORT=5432
DB_NAME=trading
DB_USER=miraiclaw
DB_PASSWORD=[YOUR-DB-PASSWORD]
import os, psycopg2

conn = psycopg2.connect(
    host=os.environ["DB_HOST"],
    port=int(os.environ.get("DB_PORT", 5432)),
    dbname=os.environ["DB_NAME"],
    user=os.environ["DB_USER"],
    password=os.environ["DB_PASSWORD"],
)

Data Freshness

Ingestion runs on the DGX via cron (weekdays, all times DGX local / US Central):

Time What refreshes
18:00 market_data_daily, options_metrics_daily
18:15 ldtm_daily_snapshots, ldtm_run_log, model_versions
18:30 news_headlines, news_articles
19:00 Azure Blob export (read replica not applicable)
Sat 02:00 model_versions canary retrain
Sun 01:00 model_versions full retrain

tqqq_signals and tqqq_daily_summary are populated on demand (not on a fixed cron).


Table Reference

Market Data

market_data_daily

OHLCV daily bars for the full NDX-100 universe + TQQQ, SQQQ, SPY, QQQ, IWM, DIA, plus ETFs (GLD, SLV, TLT, IEF, SHY, HYG, LQD, USO, UNG, GLD, VXX, UUP, FXE, FXY, FXA, FXB).

Column Type Notes
ticker text PK
date date PK
open float8
high float8
low float8
close float8
volume int8

Primary key: (ticker, date). Upsert-safe — re-running ingestion is idempotent.

-- Latest close for all tickers
SELECT ticker, date, close
FROM market_data_daily
WHERE date = (SELECT MAX(date) FROM market_data_daily)
ORDER BY ticker;

-- 90-day history for NVDA
SELECT date, open, high, low, close, volume
FROM market_data_daily
WHERE ticker = 'NVDA'
ORDER BY date DESC
LIMIT 90;

market_data

NVDA 1-minute intraday bars (legacy table, NVDA only).

Column Type Notes
ticker text
timestamp timestamptz timezone-aware
open float8
high float8
low float8
close float8
volume int8

No primary key constraint — rows may duplicate on re-run. Filter by timestamp range for recent bars.

-- Last 390 minutes (one trading day) of NVDA 1-min bars
SELECT timestamp, open, high, low, close, volume
FROM market_data
WHERE ticker = 'NVDA'
  AND timestamp >= NOW() - INTERVAL '1 day'
ORDER BY timestamp;

options_metrics_daily

Daily implied volatility (IV) and historical volatility (HV) metrics per ticker, sourced from Interactive Brokers.

Column Type Notes
ticker text PK
date date PK
iv_open float8 IV at open
iv_high float8 IV daily high
iv_low float8 IV daily low
iv_close float8 IV at close
hv_open float8 HV at open
hv_high float8 HV daily high
hv_low float8 HV daily low
hv_close float8 HV at close
source text Always 'interactive_brokers'
updated_at timestamptz Last upsert time

Primary key: (ticker, date).

-- IV/HV for TQQQ over the last 30 days
SELECT date, iv_close, hv_close, iv_close - hv_close AS iv_premium
FROM options_metrics_daily
WHERE ticker = 'TQQQ'
ORDER BY date DESC
LIMIT 30;

News

news_headlines

Article metadata from IB news feeds. One row per (ticker, article_id) combination — the same article may appear under multiple tickers.

Column Type Notes
id int8 PK (serial)
ticker text
provider text IB news provider code
article_id text IB's article identifier
headline text
published_at timestamptz
fetched_at timestamptz When this row was inserted
-- Latest 20 headlines for NVDA
SELECT ticker, provider, headline, published_at
FROM news_headlines
WHERE ticker = 'NVDA'
ORDER BY published_at DESC
LIMIT 20;

-- Join headline to body
SELECT h.ticker, h.headline, h.published_at, a.body
FROM news_headlines h
JOIN news_articles a ON a.article_id = h.article_id
WHERE h.ticker = 'AAPL'
ORDER BY h.published_at DESC
LIMIT 5;

news_articles

Full article body text. Not all headlines have bodies — some providers are headline-only or paywalled (IB returns error 10172 for those).

Column Type Notes
article_id text PK — matches news_headlines.article_id
article_type text IB content type
body text Full article text
fetched_at timestamptz
-- How many headlines have bodies vs. not
SELECT
  COUNT(*) FILTER (WHERE a.article_id IS NOT NULL) AS with_body,
  COUNT(*) FILTER (WHERE a.article_id IS NULL)     AS headline_only
FROM news_headlines h
LEFT JOIN news_articles a ON a.article_id = h.article_id;

LDTM Model (LSTM Multi-Horizon Predictions)

ldtm_daily_snapshots

One row per (run_date, ticker) written each evening after LDTM inference. Actuals are filled back by snapshot_fillback.py once the future date's close is available.

Column Type Notes
id int8 PK
run_date date Date the model ran
ticker text UNIQUE with run_date
generated_at timestamptz
next_day_close_pred float8 Predicted next-day close
next_monday_close_pred float8 Predicted next Monday close
one_month_close_pred float8 Predicted 1-month close
run_date_close float8 Actual close on run_date
next_day_actual float8 Filled back: actual next-day close
next_day_actual_date date
next_day_pct_error float8 (pred - actual) / actual * 100
next_day_direction_pred text 'UP' / 'DOWN'
next_day_direction_actual text Filled back
next_day_direction_correct bool Filled back
next_monday_actual float8 Filled back
next_monday_actual_date date
next_monday_pct_error float8
one_month_actual float8 Filled back
one_month_actual_date date
one_month_pct_error float8
source_run_log_id int8 FK → ldtm_run_log.id

Unique constraint: (run_date, ticker).

-- Today's predictions for all tickers
SELECT ticker, next_day_close_pred, next_monday_close_pred, one_month_close_pred, run_date_close
FROM ldtm_daily_snapshots
WHERE run_date = CURRENT_DATE
ORDER BY ticker;

-- Most recent prediction per ticker
SELECT DISTINCT ON (ticker)
  ticker, run_date, next_day_close_pred, next_day_direction_pred
FROM ldtm_daily_snapshots
ORDER BY ticker, run_date DESC;

ldtm_accuracy_30d (VIEW)

Rolling 30-day accuracy summary per ticker, computed live from ldtm_daily_snapshots.

Column Type Notes
ticker text
evaluated_days int8 Days with filled-back actuals
avg_abs_pct_error numeric Mean absolute % error (next-day)
avg_abs_pct_error_weekly numeric Mean absolute % error (next Monday)
direction_accuracy_pct numeric % of correct UP/DOWN calls
latest_run_date date
latest_next_day_pred float8 Most recent next-day prediction
-- Top 10 tickers by direction accuracy
SELECT ticker, direction_accuracy_pct, avg_abs_pct_error, evaluated_days
FROM ldtm_accuracy_30d
ORDER BY direction_accuracy_pct DESC
LIMIT 10;

ldtm_run_log

One row per model run (train or infer) — written by model/ldtm/ containers.

Column Type Notes
id int8 PK
run_at timestamptz
ticker text
mode text 'train' or 'infer'
status text 'ok' or 'FAIL'
duration_sec float8
epochs_run int4
best_val_loss float8
next_day_close float8 Prediction from this run
next_monday_close float8
one_month_close float8
error_msg text Populated on failure
-- Last 10 inference runs and their status
SELECT run_at, ticker, mode, status, duration_sec, next_day_close
FROM ldtm_run_log
WHERE mode = 'infer'
ORDER BY run_at DESC
LIMIT 10;

ldtm_backtest_results

Walk-forward backtest results — one row per (ticker, train_cutoff, test_date, horizon).

Column Type Notes
id int8 PK
ticker text
train_cutoff date Last date of training window
test_date date Date being predicted
horizon text 'next_day', 'next_monday', 'one_month'
pred_close float8
actual_close float8
pct_error float8
direction_pred text 'UP' / 'DOWN'
direction_actual text
direction_correct bool
created_at timestamptz
-- Direction accuracy by ticker and horizon
SELECT ticker, horizon,
  COUNT(*) AS n,
  ROUND(100.0 * SUM(CASE WHEN direction_correct THEN 1 ELSE 0 END) / COUNT(*), 1) AS dir_acc_pct,
  ROUND(AVG(ABS(pct_error))::numeric, 2) AS avg_abs_err
FROM ldtm_backtest_results
GROUP BY ticker, horizon
ORDER BY dir_acc_pct DESC;

ldtm_backtest_summary (VIEW)

Aggregated from ldtm_backtest_results — one row per (ticker, horizon).

Column Type
ticker text
horizon text
n_predictions int8
n_folds int8
avg_abs_pct_error numeric
direction_accuracy_pct numeric
earliest_cutoff date
latest_cutoff date
latest_test_date date

model_versions

LDTM model registry — tracks candidate → active → retired lifecycle.

Column Type Notes
id int8 PK
ticker text
version text Semantic version string
status text 'candidate', 'active', 'retired'
checkpoint_path text Path inside container
window_size int4 LSTM lookback window
hidden_size int4 LSTM hidden units
num_layers int4
trained_on_date date
val_loss float8
bt_direction_acc_nextday float8 Backtest direction accuracy
bt_avg_pct_error_nextday float8
promoted_at timestamptz When moved to active
retired_at timestamptz When retired
created_at timestamptz
-- Currently active models
SELECT ticker, version, val_loss, bt_direction_acc_nextday, promoted_at
FROM model_versions
WHERE status = 'active'
ORDER BY ticker;

TQQQ RF Classifier

tqqq_signals

Daily directional signal for TQQQ from the Random Forest classifier.

Column Type Notes
id int4 PK
signal_date date UNIQUE with horizon_days
generated_at timestamptz
direction text 'BUY', 'SHORT', 'HOLD'
prob_buy float8 Class probability
prob_short float8
prob_hold float8
validation_precision float8 Precision on held-out validation set
horizon_days int4 Signal horizon in trading days
use_iv bool Whether IV features were included
actual_date date Filled back: settlement date
actual_return float8 Filled back: TQQQ return over horizon
actual_direction text Filled back
was_correct bool Filled back
latest_features jsonb Feature snapshot at signal time
model_file text Path to the RF pickle

Unique constraint: (signal_date, horizon_days).

-- Latest signal
SELECT signal_date, direction, prob_buy, prob_short, prob_hold, horizon_days
FROM tqqq_signals
ORDER BY signal_date DESC, horizon_days
LIMIT 5;

-- Historical accuracy
SELECT
  direction,
  COUNT(*) AS n,
  ROUND(100.0 * SUM(CASE WHEN was_correct THEN 1 ELSE 0 END) / NULLIF(COUNT(*) FILTER (WHERE was_correct IS NOT NULL), 0), 1) AS accuracy_pct
FROM tqqq_signals
GROUP BY direction;

tqqq_backtest_runs

Walk-forward backtest results for the TQQQ RF strategy.

Column Type Notes
id int4 PK
run_date date
generated_at timestamptz
backtest_start / backtest_end date Window tested
horizon_days int4
retrain_every int4 Days between retrains
use_iv bool
initial_capital float8
final_capital float8
return_pct float8
cagr float8
max_drawdown float8
sharpe float8
total_trades int4
win_rate float8
bh_return_pct float8 Buy-and-hold benchmark return
bh_final_capital float8
alpha float8 Strategy return − B&H return
trade_log jsonb Per-trade detail
equity_curve jsonb Daily equity values
model_file text
-- Latest backtest summary
SELECT run_date, return_pct, cagr, sharpe, max_drawdown, alpha, total_trades, win_rate
FROM tqqq_backtest_runs
ORDER BY run_date DESC
LIMIT 1;

tqqq_montecarlo_runs

Bootstrap Monte Carlo simulation results (1000 sims) based on a backtest's trade distribution.

Column Type Notes
id int4 PK
run_date date
generated_at timestamptz
source_backtest_id int4 FK → tqqq_backtest_runs.id
n_simulations int4
n_trades int4
initial_capital float8
p05_final / p25_final / p50_final / p75_final / p95_final float8 Percentile final capital
p05_return_pct / p50_return_pct / p95_return_pct float8 Percentile returns
p05_max_drawdown / p50_max_drawdown / p95_max_drawdown float8
prob_profit float8 P(final > initial)
prob_double float8 P(final > 2× initial)
raw_results jsonb Full simulation distribution

tqqq_daily_summary

LLM-ready JSONB summary blob, generated by model-tqqq-summarize. One row per trading day.

Column Type Notes
id int4 PK
summary_date date UNIQUE
generated_at timestamptz
summary_json jsonb Structured summary for LLM context
-- Today's summary
SELECT summary_date, summary_json
FROM tqqq_daily_summary
WHERE summary_date = CURRENT_DATE;

TFT Model

tft_run_log

One row per TFT training or inference run.

Column Type Notes
id int8 PK
run_at timestamptz
target text Primary target ticker
mode text 'train' or 'infer'
status text 'ok' or 'FAIL'
duration_sec float8
epochs_run int4
best_val_loss float8
sequence_length int4 Input sequence length
context_count int4 Number of context tickers
last_close float8 Target ticker's last close
next_day_return float8 Predicted return (1d)
next_week_return float8 Predicted return (1wk)
one_month_return float8 Predicted return (1mo)
next_day_close float8 Predicted absolute close
next_week_close float8
one_month_close float8
context_tickers text[] Array of context tickers used
checkpoint_path text
error_msg text

Dashboard / Custom Jobs

custom_run_jobs

Jobs submitted through the Streamlit dashboard for on-demand LDTM runs.

Column Type Notes
id int8 PK
ticker text
model_type text Default 'ldtm'
status text 'queued', 'running', 'done', 'failed'
window_size int4
epochs int4
started_at timestamptz
completed_at timestamptz
duration_sec int4
error_msg text
last_close float8
next_day_pred float8
next_monday_pred float8
one_month_pred float8
checkpoint_path text

Useful Cross-Table Queries

-- Latest LDTM prediction + 30d accuracy for every ticker
SELECT
  s.ticker,
  s.run_date,
  s.next_day_close_pred,
  s.next_day_direction_pred,
  s.run_date_close,
  a.direction_accuracy_pct,
  a.avg_abs_pct_error
FROM (
  SELECT DISTINCT ON (ticker) *
  FROM ldtm_daily_snapshots
  ORDER BY ticker, run_date DESC
) s
LEFT JOIN ldtm_accuracy_30d a ON a.ticker = s.ticker
ORDER BY a.direction_accuracy_pct DESC NULLS LAST;

-- TQQQ composite view: today's signal + latest backtest + latest MC
SELECT
  ts.signal_date,
  ts.direction,
  ts.prob_buy,
  ts.prob_short,
  tb.cagr,
  tb.sharpe,
  tb.alpha,
  tm.prob_profit,
  tm.p50_return_pct AS mc_median_return
FROM tqqq_signals ts
LEFT JOIN tqqq_backtest_runs tb ON tb.run_date = ts.signal_date
LEFT JOIN tqqq_montecarlo_runs tm ON tm.run_date = ts.signal_date
WHERE ts.signal_date = (SELECT MAX(signal_date) FROM tqqq_signals);

-- Recent news with bodies for a ticker
SELECT h.ticker, h.headline, h.published_at, a.body
FROM news_headlines h
JOIN news_articles a ON a.article_id = h.article_id
WHERE h.ticker = 'NVDA'
  AND h.published_at >= NOW() - INTERVAL '7 days'
ORDER BY h.published_at DESC;

Access Control Notes

  • Both miraiclaw and miraistudio are read-only via the trading_reader role.
  • trading_reader has SELECT on all present and future tables (ALTER DEFAULT PRIVILEGES is set).
  • Neither user can modify data, create objects, or access system catalogs beyond standard pg_catalog reads.
  • To revoke access: REVOKE trading_reader FROM miraiclaw;
  • To add another system: CREATE USER newsystem WITH PASSWORD '...'; GRANT trading_reader TO newsystem;

Troubleshooting

Symptom Likely cause Fix
Connection refused on port 5432 trading-postgres container not running docker start trading-postgres
password authentication failed Wrong password or user Verify user exists: SELECT rolname FROM pg_roles;
FATAL: no pg_hba.conf entry Client IP not covered Check pg_hba.conf — current rule allows all IPs with password
Stale data Cron job failed Check schedule/logs/cron.log on DGX
permission denied for table X New table added before DEFAULT PRIVILEGES took effect GRANT SELECT ON TABLE X TO trading_reader;