[object Object]

← back to Ken

Ken: seed 99 paper-trading portfolios + add portfolio stat columns (local boot)

2fa8032ec6302cbb2cc3e43b01bfe02d8cf24ef1 · 2026-07-10 11:11:08 -0700 · Steve

Files touched

Diff

commit 2fa8032ec6302cbb2cc3e43b01bfe02d8cf24ef1
Author: Steve <steve@designerwallcoverings.com>
Date:   Fri Jul 10 11:11:08 2026 -0700

    Ken: seed 99 paper-trading portfolios + add portfolio stat columns (local boot)
---
 kalshi-dash/schema.sql          | 17 ++++++++++++---
 kalshi-dash/seed-portfolios.mjs | 47 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/kalshi-dash/schema.sql b/kalshi-dash/schema.sql
index bb98e54..70f855d 100644
--- a/kalshi-dash/schema.sql
+++ b/kalshi-dash/schema.sql
@@ -98,9 +98,20 @@ CREATE TABLE IF NOT EXISTS ken_portfolios (
   id BIGINT PRIMARY KEY,
   name TEXT, strategy TEXT,
   balance_cents BIGINT, daily_budget_cents BIGINT,
-  total_invested_cents BIGINT DEFAULT 0, starting_balance_cents BIGINT,
-  color TEXT, last_trade_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW()
-);
+  total_invested_cents BIGINT DEFAULT 0, total_returned_cents BIGINT DEFAULT 0,
+  starting_balance_cents BIGINT, color TEXT,
+  trades_won BIGINT DEFAULT 0, trades_lost BIGINT DEFAULT 0, trades_expired BIGINT DEFAULT 0,
+  streak BIGINT DEFAULT 0, best_trade_cents BIGINT DEFAULT 0, worst_trade_cents BIGINT DEFAULT 0,
+  last_trade_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW()
+);
+-- backfill columns on an already-created ken_portfolios (idempotent)
+ALTER TABLE ken_portfolios ADD COLUMN IF NOT EXISTS total_returned_cents BIGINT DEFAULT 0;
+ALTER TABLE ken_portfolios ADD COLUMN IF NOT EXISTS trades_won BIGINT DEFAULT 0;
+ALTER TABLE ken_portfolios ADD COLUMN IF NOT EXISTS trades_lost BIGINT DEFAULT 0;
+ALTER TABLE ken_portfolios ADD COLUMN IF NOT EXISTS trades_expired BIGINT DEFAULT 0;
+ALTER TABLE ken_portfolios ADD COLUMN IF NOT EXISTS streak BIGINT DEFAULT 0;
+ALTER TABLE ken_portfolios ADD COLUMN IF NOT EXISTS best_trade_cents BIGINT DEFAULT 0;
+ALTER TABLE ken_portfolios ADD COLUMN IF NOT EXISTS worst_trade_cents BIGINT DEFAULT 0;
 
 CREATE TABLE IF NOT EXISTS ken_portfolio_trades (
   id BIGSERIAL PRIMARY KEY,
diff --git a/kalshi-dash/seed-portfolios.mjs b/kalshi-dash/seed-portfolios.mjs
new file mode 100644
index 0000000..bb43d42
--- /dev/null
+++ b/kalshi-dash/seed-portfolios.mjs
@@ -0,0 +1,47 @@
+// Seed ken_portfolios — one paper-trading portfolio per PORTFOLIO_STRATEGIES key.
+// Keys + names + colors are read straight out of server.js so the seed stays in sync.
+// Model: $100 starting balance, $20/day budget (engine tops up +$100/week).
+// Idempotent: ON CONFLICT (id) DO NOTHING — re-running never disturbs live balances.
+import fs from 'fs';
+import pg from 'pg';
+
+const src = fs.readFileSync(new URL('./server.js', import.meta.url), 'utf8');
+
+const stratBlock = src.match(/const PORTFOLIO_STRATEGIES = \{([\s\S]*?)\n\};/)[1];
+const keys = [...stratBlock.matchAll(/^\s*([a-z_]+):\s*\(sig\)/gm)].map((m) => m[1]);
+
+const grab = (name) => {
+  const m = src.match(new RegExp('const ' + name + ' = (\\{[\\s\\S]*?\\});'));
+  // eslint-disable-next-line no-eval
+  return m ? eval('(' + m[1] + ')') : {};
+};
+const stratNames = grab('stratNames');
+const stratColors = grab('stratColors');
+const titleCase = (k) => k.split('_').map((w) => w[0].toUpperCase() + w.slice(1)).join(' ');
+
+const START_BALANCE = 10000; // $100
+const DAILY_BUDGET = 2000;   // $20/day
+
+const url = process.env.KEN_DATABASE_URL || 'postgresql://localhost:5432/ken';
+const pool = new pg.Pool({ connectionString: url });
+
+let seeded = 0;
+for (let i = 0; i < keys.length; i++) {
+  const strat = keys[i];
+  const id = i + 1;
+  const name = stratNames[strat] || titleCase(strat);
+  const color = stratColors[strat] || '#64748b';
+  const r = await pool.query(
+    `INSERT INTO ken_portfolios
+       (id, name, strategy, balance_cents, daily_budget_cents, total_invested_cents,
+        total_returned_cents, starting_balance_cents, color)
+     VALUES ($1,$2,$3,$4,$5,0,0,$4,$6)
+     ON CONFLICT (id) DO NOTHING`,
+    [id, name, strat, START_BALANCE, DAILY_BUDGET, color]
+  );
+  seeded += r.rowCount;
+}
+
+const { rows } = await pool.query('SELECT COUNT(*) c FROM ken_portfolios');
+console.log(`Seeded ${seeded} new portfolios; ken_portfolios now has ${rows[0].c} rows (${keys.length} strategies).`);
+await pool.end();

← a3dd171 Ken: switch AI analyst from paid Gemini to local Ollama (her  ·  back to Ken  ·  Ken: add captured_at to ken_market_snapshots (momentum query a060e29 →