Risk Dimensions · Analyst Seat

rd-sandbox — Starter Notebook

A readable preview of the day-one notebook. It walks the whole loop on one database credential: connect → pull → compute → rank & comment → push your picks → read the feedback.
The file: lib/rd_sandbox/rd_sandbox_starter.ipynb (with rd_metrics.py beside it). Open it in Jupyter or VS Code and run top to bottom. This page is just a read-only view of what's inside. Calculations reference: rd_metrics.
Step 0 · setup

Install + load the house calc library

Dependencies, then rd_metrics (sits beside the notebook; downloads in Colab). Never re-derive a formula — call the function.

%pip install -q pandas numpy requests sqlalchemy psycopg2-binary

import rd_metrics as m
print(m.__version__)
Step 1 · credentials

Your seat login (never hard-coded)

getpass prompts and keeps secrets out of the saved file. Your read-only DB credential was sent privately; your Contributor grant rides the same role.

from getpass import getpass
DB_USER = input('DB role (e.g. seat_philipp): ').strip()
DB_PASS = getpass('DB password: ')
DB_HOST = input('DB host [db.riskdimensions.io]: ').strip() or 'db.riskdimensions.io'
DB_PORT = input('DB port [5432]: ').strip() or '5432'
DB_NAME = input('DB name [barbarian_db]: ').strip() or 'barbarian_db'
AS_OF   = input('As-of date YYYY-MM-DD: ').strip()
AUTHOR  = DB_USER
Step 2 · connect readwrite

One connection, both directions

Laptop Jupyter: open the tunnel first (cloudflared access tcp --hostname db.riskdimensions.io --url localhost:5432) and set DB_HOST=localhost. The same handle reads the hub and writes your picks.

from sqlalchemy import create_engine, text
import pandas as pd
engine = create_engine(f'postgresql+psycopg2://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}')
def q(sql, **p):
    with engine.connect() as c:
        return pd.read_sql(text(sql), c, params=p)
print(q('select current_user, current_database()').iloc[0].to_dict())
Step 3 · pull candidates read

Your universe to choose from

The catalog lists every fiscal/macro series; cross-country values live in sovereign.macro_series. Adapt to whatever the ask defines (the ~200 you narrow to 9).

catalog = q("""
    select series_code, label, category, unit, native_freq
    from sovereign.series_catalog where active
    order by category, sort_order
""")

us = q("""
    select distinct series_code, unit, freq
    from sovereign.macro_series where country_iso3='USA' order by series_code
""")
SERIES = us.iloc[0]['series_code']
obs = q("""
    select record_date as obs_date, value from sovereign.macro_series
    where series_code = :sid and country_iso3='USA' order by record_date
""", sid=SERIES)
Step 4 · compute

House calculations (rd_metrics)

Everyone computes the same metric the same way, so picks are comparable and auditable.

levels  = obs['value'].tolist()
returns = m.to_returns(levels)
stats = {
    'cagr':         m.cagr(levels, ppy=1),
    'total_return': m.total_return(returns),
    'ann_vol':      m.ann_vol(returns, ppy=1),
    'max_drawdown': m.max_drawdown(returns),
}
Step 5 · rank & comment

Your judgment / your Claude

Your context files drive the selection: narrow to 9 picks, 3 per region, each with commentary. Build it by hand, or hand the computed stats to your own Claude CLI and paste the ranked output here. Encoding that judgment is the job.

picks = pd.DataFrame([
    {'region':'US','indicator_code':'GDP_US','rank':1,'commentary':'Growth above trend; ...'},
    {'region':'US','indicator_code':'M2_US','rank':2,'commentary':'...'},
    # ... 9 total (3 US, 3 EU, 3 ASIA)
])
Step 6 · push write — contributor

Append your picks to the shared table

Appends your 9 to contrib.featured_indicators. Append-only — add, never edit or delete; corrections are a new row. Needs the rd_contributor grant; Readers get a permission error here (expected).

ins = text("""
  insert into contrib.featured_indicators
    (as_of_date, region, indicator_code, rank, commentary, author)
  values (:as_of,:region,:code,:rank,:commentary,:author)
""")
with engine.begin() as c:
    for _, r in picks.iterrows():
        c.execute(ins, {'as_of':AS_OF,'region':r['region'],'code':r['indicator_code'],
                        'rank':int(r['rank']),'commentary':r['commentary'],'author':AUTHOR})
Step 7 · read feedback read

Coach insight + your weekly grade

After you submit, the house adds its lens (Coach insight) and a weekly job grades how the call held up. Both are readable here — your directed-learning loop.

feedback = q("""
  select f.as_of_date, f.region, f.indicator_code, f.rank,
         i.insight, i.lens, e.verdict, e.score, e.eval_week
  from contrib.featured_indicators f
  left join contrib.featured_indicator_insight i on i.featured_id = f.id
  left join contrib.featured_indicator_eval    e on e.featured_id = f.id
  where f.author = :a
  order by f.as_of_date desc, f.region, f.rank
""", a=AUTHOR)
Risk Dimensions · rd-sandbox starter. Canonical data is read-only · your workspace is yours · contributions are append-only. Source: lib/rd_sandbox/.