diff --git a/docker-compose.yml b/docker-compose.yml index 7288d68..149f648 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,6 +50,11 @@ services: networks: - nginx_proxy_manager_default + # DB backend is one switch for the whole stack and it defaults to sqlite on purpose. + # the postgres copy is a stale snapshot (predictions stop around 2026-08-17) and the + # migrate script only appends, it never replays UPDATEs, so a `compose up` must not + # quietly flip us over. set DURIIN_DB_BACKEND=postgres + DURIIN_USE_POSTGRES=true in + # .env only after a fresh intelligence migration has been run and verifyed. api: build: context: . @@ -61,8 +66,8 @@ services: environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite - DURIIN_DB_BACKEND: postgres - DURIIN_USE_POSTGRES: "true" + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" DURIIN_RUN_SCHEDULER: "false" AUTONOMY_EXECUTION_MODE: "${AUTONOMY_EXECUTION_MODE:-shadow}" @@ -73,7 +78,60 @@ services: networks: - nginx_proxy_manager_default + # same image as api, but this one actually runs the cron scheduler + # (rss / gdelt / edgar / alphavantage / finnhub). it also boots fastify on + # 3001 but nothing proxies to it, so its effectivly ingestion only. + ingest: + build: + context: . + provenance: false + env_file: .env + volumes: + - ./config.json:/app/config.json:ro + - ./data:/data + environment: + NODE_ENV: production + INTELLIGENCE_DB: /data/intelligence.sqlite + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" + DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" + DURIIN_RUN_SCHEDULER: "true" + AUTONOMY_EXECUTION_MODE: "${AUTONOMY_EXECUTION_MODE:-shadow}" + depends_on: + postgres: + condition: service_healthy + restart: unless-stopped + networks: + - nginx_proxy_manager_default + + # enrichment chain: queue feeder -> augor -> consolidation -> graph -> signal -> outcome. + # this is what fills event_id / content / has_embedding, which the autonomy + # reconcilers require before they enqueue anything. + enrichment: + build: + context: . + provenance: false + command: node workers/index.js + env_file: .env + volumes: + - ./config.json:/app/config.json:ro + - ./data:/data + environment: + NODE_ENV: production + DURIIN_DB: /data/archive.sqlite + INTELLIGENCE_DB: /data/intelligence.sqlite + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" + DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" + depends_on: + postgres: + condition: service_healthy + restart: unless-stopped + networks: + - nginx_proxy_manager_default + intelligence: + # superseded by the "enrichment" service above, kept behind a profile profiles: [legacy] build: context: . @@ -104,8 +162,8 @@ services: NODE_ENV: production DURIIN_DB: /data/archive.sqlite INTELLIGENCE_DB: /data/intelligence.sqlite - DURIIN_DB_BACKEND: postgres - DURIIN_USE_POSTGRES: "true" + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_POLL_MS: "${AUTONOMY_POLL_MS:-5000}" depends_on: @@ -130,8 +188,8 @@ services: NODE_ENV: production DURIIN_DB: /data/archive.sqlite INTELLIGENCE_DB: /data/intelligence.sqlite - DURIIN_DB_BACKEND: postgres - DURIIN_USE_POSTGRES: "true" + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_POLL_MS: "${AUTONOMY_COORDINATOR_POLL_MS:-5000}" depends_on: @@ -154,8 +212,8 @@ services: environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite - DURIIN_DB_BACKEND: postgres - DURIIN_USE_POSTGRES: "true" + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_OUTCOME_POLL_MS: "${AUTONOMY_OUTCOME_POLL_MS:-60000}" depends_on: @@ -180,8 +238,8 @@ services: NODE_ENV: production DURIIN_DB: /data/archive.sqlite INTELLIGENCE_DB: /data/intelligence.sqlite - DURIIN_DB_BACKEND: postgres - DURIIN_USE_POSTGRES: "true" + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_REPLAY_POLL_MS: "${AUTONOMY_REPLAY_POLL_MS:-15000}" AUTONOMY_REPLAY_DAILY_BUDGET: "${AUTONOMY_REPLAY_DAILY_BUDGET:-100}" @@ -206,8 +264,8 @@ services: environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite - DURIIN_DB_BACKEND: postgres - DURIIN_USE_POSTGRES: "true" + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_CALIBRATION_POLL_MS: "${AUTONOMY_CALIBRATION_POLL_MS:-60000}" depends_on: @@ -230,8 +288,8 @@ services: environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite - DURIIN_DB_BACKEND: postgres - DURIIN_USE_POSTGRES: "true" + DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" + DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_EXECUTION_MODE: "${AUTONOMY_EXECUTION_MODE:-shadow}" AUTONOMY_DEFAULT_NOTIONAL: "${AUTONOMY_DEFAULT_NOTIONAL:-100}" diff --git a/public/admin/assets/css/autonomy.css b/public/admin/assets/css/autonomy.css index 4cda4aa..33d7c61 100644 --- a/public/admin/assets/css/autonomy.css +++ b/public/admin/assets/css/autonomy.css @@ -53,6 +53,7 @@ .decision-chip { justify-self: end; padding: 5px 8px; color: var(--warning); background: rgba(243,201,105,.06); border: 1px solid rgba(243,201,105,.16); border-radius: 3px; font-family: var(--mono); font-size: 9px; font-weight: 750; } .decision-chip.buy { color: var(--positive); border-color: rgba(142,230,168,.18); background: rgba(142,230,168,.06); } .decision-chip.sell { color: var(--negative); border-color: rgba(255,141,125,.18); background: rgba(255,141,125,.06); } +.decision-chip.qualifies { color: var(--positive); border-color: rgba(142,230,168,.18); background: rgba(142,230,168,.06); } .performance-panel { padding-bottom: 18px; } .accuracy-orbit { --accuracy: 0deg; width: 166px; height: 166px; margin: 28px auto 24px; padding: 1px; display: grid; place-items: center; border-radius: 50%; background: conic-gradient(var(--accent) var(--accuracy), #252b24 0); } diff --git a/public/admin/assets/js/autonomy.js b/public/admin/assets/js/autonomy.js index fc0c204..733b166 100644 --- a/public/admin/assets/js/autonomy.js +++ b/public/admin/assets/js/autonomy.js @@ -35,11 +35,80 @@ `).join(""); } + const ORIGIN_LABELS = { + live: "Live", + historical: "Historical backfill", + replay: "Walk-forward replay", + }; + + // live, historical and replay are deliberately never blended. only the live + // row is an edge claim, the other two are how the model was taught. + function renderOriginSplit(byOrigin, livePredictions) { + const host = byId("origin-split"); + if (!host) return; + + const rows = (byOrigin || []).filter(row => row.origin !== "live"); + if (!rows.length) { + host.innerHTML = ""; + return; + } + + host.innerHTML = rows.map(row => { + const total = Number(row.total || 0); + const label = ORIGIN_LABELS[row.origin] || row.origin; + const accuracy = total ? formatPercent(Number(row.correct || 0) / total) : "—"; + return `
Duriin will only claim an edge after predictions mature and are measured out of sample.
+ +Historical backfill and walk-forward replay are shown separately. Neither is evidence of live edge.
@@ -138,10 +140,20 @@| Cohort | Source | Samples | Distinct tickers | Top ticker share | Status |
|---|---|---|---|---|---|
| No calibration snapshots yet. | |||||