← back to Ken

kalshi-dash/seed-portfolios.mjs

48 lines

// 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();