@hedge-layer/cli 3.0.1 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,12 +63,7 @@ hl quote "example-market" --action buy --outcome yes --cash 25
63
63
  hl quote "example-market" --action buy --outcome yes --cash 25 \
64
64
  --signal-id <forecast-id> --capital 1000 --save
65
65
 
66
- # 7. Run the advanced dry-run liquidity-provider planner
67
- hl --json feed liquidity-provider --limit 15 | jq '{ markets: .markets }' > markets.json
68
- hl lp allocator --markets markets.json
69
- your execution workflow
70
-
71
- # 8. Analyze a market probability edge
66
+ # 7. Analyze a market probability edge
72
67
  hl signal analyze "https://polymarket.com/event/example-market"
73
68
  ```
74
69
 
@@ -91,35 +86,17 @@ estimated fill, slippage, fees, cost or proceeds, payout risk, and optional
91
86
  Signal edge. It never signs or submits an order. `--cash` is BUY-only; SELL
92
87
  quotes require `--shares`.
93
88
 
94
- Advanced liquidity-provider planning remains available under `hl lp` as a
95
- dry-run-only API client:
96
-
97
- ```bash
98
- hl lp allocator --markets markets.json
99
- ```
89
+ Liquidity allocation, wallet management, and trade execution are outside the
90
+ official `hl` CLI.
100
91
 
101
- The lightweight LP planning loop is:
92
+ For a broader discovery screen, `hl feed ensemble` runs several feed lenses
93
+ (liquid core, active volume, movers, new markets, uncertainty, and LP quality),
94
+ de-duplicates by slug, and writes a ranked candidate file:
102
95
 
103
96
  ```bash
104
97
  hl feed ensemble --limit 25 --output candidates.json
105
- # or: hl --json feed lp-opportunity --limit 15 > markets.json
106
- external-pnl-export --json > pnl.json # optional: wallet PnL + live inventory
107
- hl lp allocator --markets candidates.json --pnl pnl.json --allocations pnl.json
108
- your execution workflow ...
109
98
  ```
110
99
 
111
- `hl feed ensemble` runs several feed lenses (liquid core, active volume, movers, new markets, uncertainty, and LP quality), de-duplicates by slug, and writes a daily candidate file.
112
-
113
- `hl lp allocator` submits the candidate market list through the web API to the
114
- allocator agent. Allocator output shows target capital, quote regime, failed
115
- safety checks, and split spread/reward economics. Trade execution stays outside
116
- the `hl` CLI.
117
-
118
- `--allocations` tells the allocator what you already hold (enabling HOLD,
119
- REDUCE, and EXIT decisions), and `--pnl` feeds per-market PnL into its caution
120
- overlay so borderline allocations on losing markets are downgraded to WATCH or
121
- HOLD. Both flags accept the same external wallet/inventory export shape.
122
-
123
100
  Signal-agent analysis is available under `hl signal`:
124
101
 
125
102
  ```bash
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command4 } from "commander";
4
+ import { Command as Command3 } from "commander";
5
5
 
6
6
  // src/commands/auth.ts
7
7
  import readline from "readline/promises";
8
+ import { Writable } from "stream";
8
9
 
9
10
  // src/config.ts
10
11
  import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
@@ -292,42 +293,59 @@ function relativeTime(date) {
292
293
  }
293
294
 
294
295
  // src/commands/auth.ts
296
+ async function promptHidden(prompt, options = {}) {
297
+ const input = options.input ?? process.stdin;
298
+ const output = options.output ?? process.stderr;
299
+ const createInterface = options.createInterface ?? readline.createInterface;
300
+ const mutedOutput = new Writable({
301
+ write(_chunk, _encoding, callback) {
302
+ callback();
303
+ }
304
+ });
305
+ const rl = createInterface({
306
+ input,
307
+ output: mutedOutput,
308
+ terminal: Boolean(input.isTTY)
309
+ });
310
+ output.write(prompt);
311
+ try {
312
+ return (await rl.question("")).trim();
313
+ } finally {
314
+ output.write("\n");
315
+ rl.close();
316
+ }
317
+ }
295
318
  function registerAuthCommands(program2) {
296
319
  const auth = program2.command("auth").description("Manage API authentication");
297
320
  auth.command("login").description("Authenticate with a Hedge Layer API token").option("--api-url <url>", "API base URL", DEFAULT_API_URL).action(async (cmdOpts) => {
298
321
  const globalOpts = program2.opts();
299
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
300
- try {
301
- heading("Hedge Layer CLI \u2014 Login");
302
- process.stderr.write(
303
- `Create an API token at ${bold("https://hedgelayer.ai/account/settings")} \u2192 API Tokens
322
+ heading("Hedge Layer CLI \u2014 Login");
323
+ process.stderr.write(
324
+ `Create an API token at ${bold("https://hedgelayer.ai/account/settings")} \u2192 API Tokens
304
325
 
305
326
  `
306
- );
307
- const token = (await rl.question("Paste your API token: ")).trim();
308
- if (!token.startsWith("hl_") || token.length !== 43) {
309
- error('Invalid token format. Tokens start with "hl_" and are 43 characters.');
310
- process.exit(1);
311
- }
312
- const apiUrl = globalOpts.apiUrl ?? cmdOpts.apiUrl ?? DEFAULT_API_URL;
313
- const client = new ApiClient({ token, apiUrl });
314
- process.stderr.write("\nValidating token...");
315
- let profile;
316
- try {
317
- profile = await client.get("/api/profile");
318
- } catch {
319
- process.stderr.write("\n");
320
- error("Token validation failed. Check your token and try again.");
321
- process.exit(1);
322
- }
323
- process.stderr.write(" done\n\n");
324
- saveConfig({ api_url: apiUrl, token });
325
- success(`Logged in as ${bold(profile.handle || profile.user_id)}`);
326
- process.stderr.write(` Config saved to ${dim(configPath())}
327
- `);
328
- } finally {
329
- rl.close();
327
+ );
328
+ const token = await promptHidden("Paste your API token: ");
329
+ if (!token.startsWith("hl_") || token.length !== 43) {
330
+ error('Invalid token format. Tokens start with "hl_" and are 43 characters.');
331
+ process.exit(1);
330
332
  }
333
+ const apiUrl = globalOpts.apiUrl ?? cmdOpts.apiUrl ?? DEFAULT_API_URL;
334
+ const client = new ApiClient({ token, apiUrl });
335
+ process.stderr.write("\nValidating token...");
336
+ let profile;
337
+ try {
338
+ profile = await client.get("/api/profile");
339
+ } catch {
340
+ process.stderr.write("\n");
341
+ error("Token validation failed. Check your token and try again.");
342
+ process.exit(1);
343
+ }
344
+ process.stderr.write(" done\n\n");
345
+ saveConfig({ api_url: apiUrl, token });
346
+ success(`Logged in as ${bold(profile.handle || profile.user_id)}`);
347
+ process.stderr.write(` Config saved to ${dim(configPath())}
348
+ `);
331
349
  });
332
350
  auth.command("status").description("Show current authentication status").action(async () => {
333
351
  const globalOpts = program2.opts();
@@ -1245,281 +1263,12 @@ function registerFeedCommand(program2) {
1245
1263
  });
1246
1264
  }
1247
1265
 
1248
- // src/commands/lp.ts
1249
- import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
1250
-
1251
- // src/allocator-display.ts
1252
- import chalk5 from "chalk";
1253
- function actionColor(action) {
1254
- switch (action) {
1255
- case "ALLOCATE":
1256
- case "INCREASE":
1257
- return chalk5.green(action);
1258
- case "REDUCE":
1259
- case "EXIT":
1260
- return chalk5.yellow(action);
1261
- case "SKIP":
1262
- return chalk5.dim(action);
1263
- case "WATCH":
1264
- return chalk5.cyan(action);
1265
- default:
1266
- return action;
1267
- }
1268
- }
1269
- function num2(value, fallback = 0) {
1270
- const n = Number(value);
1271
- return Number.isFinite(n) ? n : fallback;
1272
- }
1273
- function displayAllocatorCycleResult(result, globalOpts) {
1274
- if (globalOpts.json) {
1275
- json(result);
1276
- return;
1277
- }
1278
- const decisions = Array.isArray(result.decisions) ? result.decisions : [];
1279
- const summary = result.summary ?? {};
1280
- const dryRun = result.dry_run === false ? "live" : "dry run";
1281
- heading(`Allocator Cycle \u2014 ${dryRun}`);
1282
- process.stdout.write(
1283
- chalk5.dim(
1284
- ` ${num2(result.total_markets, decisions.length)} markets \xB7 ${currency(
1285
- num2(summary.target_capital)
1286
- )} target capital
1287
-
1288
- `
1289
- )
1290
- );
1291
- if (decisions.length === 0) {
1292
- warn("No allocator decisions returned.");
1293
- return;
1294
- }
1295
- const rows = decisions.map((d) => {
1296
- const action = String(d.action ?? "UNKNOWN");
1297
- const score = num2(d.score?.score);
1298
- const expected = num2(d.score?.expected_return_daily_pct);
1299
- const failedChecks = Array.isArray(d.safety_checks) ? d.safety_checks.filter((check) => check.passed === false).length : 0;
1300
- return [
1301
- truncate(String(d.question ?? d.market_slug ?? "\u2014"), 42),
1302
- actionColor(action),
1303
- currency(num2(d.target_capital)),
1304
- signedCurrency(num2(d.capital_delta)),
1305
- `${expected.toFixed(3)}%`,
1306
- regimeLabel(String(d.quote_regime ?? "\u2014")),
1307
- String(Math.round(score)),
1308
- failedChecks === 0 ? chalk5.green("0") : chalk5.yellow(String(failedChecks))
1309
- ];
1310
- });
1311
- table(rows, [
1312
- "Market",
1313
- "Action",
1314
- "Target",
1315
- "Delta",
1316
- "Exp/day",
1317
- "Regime",
1318
- "Score",
1319
- "Fails"
1320
- ]);
1321
- process.stdout.write("\n");
1322
- for (const decision of decisions.slice(0, 5)) {
1323
- const action = String(decision.action ?? "UNKNOWN");
1324
- process.stdout.write(" " + chalk5.dim("\u25B8 ") + truncate(String(decision.question ?? decision.market_slug ?? "\u2014"), 65) + "\n");
1325
- process.stdout.write(" " + chalk5.dim("Action: ") + actionColor(action));
1326
- if (decision.rationale) {
1327
- process.stdout.write(" " + chalk5.dim("Rationale: ") + truncate(decision.rationale, 120));
1328
- }
1329
- process.stdout.write("\n");
1330
- const economics = decision.economics ?? {};
1331
- if (economics.realized_spread_pnl !== void 0 || economics.reward_income !== void 0 || economics.net_realized_pnl !== void 0) {
1332
- process.stdout.write(
1333
- " " + chalk5.dim("Economics: ") + `spread ${signedCurrency(num2(economics.realized_spread_pnl))}, rewards ${signedCurrency(num2(economics.reward_income))}, net ${signedCurrency(num2(economics.net_realized_pnl))}
1334
- `
1335
- );
1336
- }
1337
- }
1338
- if (decisions.length > 5) {
1339
- process.stdout.write(chalk5.dim(`
1340
- \u2026 and ${decisions.length - 5} more decisions
1341
- `));
1342
- }
1343
- }
1344
- function regimeLabel(value) {
1345
- switch (value) {
1346
- case "reward_optimized":
1347
- return chalk5.green("reward");
1348
- case "defensive":
1349
- return chalk5.yellow("defense");
1350
- case "no_quote":
1351
- return chalk5.red("no quote");
1352
- default:
1353
- return chalk5.dim(value);
1354
- }
1355
- }
1356
- function signedCurrency(value) {
1357
- const formatted = currency(Math.abs(value));
1358
- if (value > 0) return chalk5.green(`+${formatted}`);
1359
- if (value < 0) return chalk5.yellow(`-${formatted}`);
1360
- return chalk5.dim("$0.00");
1361
- }
1362
-
1363
- // src/commands/allocator.ts
1266
+ // src/commands/signal.ts
1364
1267
  import { InvalidArgumentError } from "commander";
1365
1268
  import { readFile } from "fs/promises";
1366
- function parsePositiveNumber(value) {
1367
- const n = Number(value);
1368
- if (!Number.isFinite(n) || n <= 0) {
1369
- throw new InvalidArgumentError("Expected a positive number");
1370
- }
1371
- return n;
1372
- }
1373
- function parseAllocationsInput(parsed) {
1374
- const rows = extractArray(parsed, "allocations");
1375
- if (!rows) {
1376
- throw new Error("Allocations JSON must be an array or { allocations: [...] }");
1377
- }
1378
- return rows;
1379
- }
1380
- function parsePnlContextInput(parsed) {
1381
- const rows = extractArray(parsed, "pnl_context");
1382
- if (!rows) {
1383
- throw new Error("PnL JSON must be an array or { pnl_context: [...] }");
1384
- }
1385
- return rows;
1386
- }
1387
- async function readAllocations(path) {
1388
- if (!path) return [];
1389
- return parseAllocationsInput(await readJsonInput(path));
1390
- }
1391
- async function readPnlContext(path) {
1392
- if (!path) return [];
1393
- return parsePnlContextInput(await readJsonInput(path));
1394
- }
1395
- async function readMarketPayload(path) {
1396
- if (!path) return void 0;
1397
- const parsed = await readJsonInput(path);
1398
- const rows = extractArray(parsed, "markets");
1399
- if (!rows) {
1400
- throw new Error("Markets JSON must be an array or { markets: [...] }");
1401
- }
1402
- return rows;
1403
- }
1404
- function extractArray(parsed, key) {
1405
- if (Array.isArray(parsed)) {
1406
- return parsed;
1407
- }
1408
- if (parsed && typeof parsed === "object" && Array.isArray(parsed[key])) {
1409
- return parsed[key];
1410
- }
1411
- return void 0;
1412
- }
1413
- async function readJsonInput(path) {
1414
- const raw = path === "-" ? await readStdin() : await readFile(path, "utf8");
1415
- return JSON.parse(raw);
1416
- }
1417
- async function readStdin() {
1418
- const chunks = [];
1419
- for await (const chunk of process.stdin) {
1420
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1421
- }
1422
- return Buffer.concat(chunks).toString("utf8");
1423
- }
1424
-
1425
- // src/commands/lp.ts
1426
- function requireAuth5(client) {
1427
- if (!client.isAuthenticated) {
1428
- error("Not authenticated. Run `hl auth login` first.");
1429
- process.exit(1);
1430
- }
1431
- }
1432
- function parseNonNegative(value) {
1433
- const n = Number(value);
1434
- if (!Number.isFinite(n) || n < 0) {
1435
- throw new InvalidArgumentError2("Expected a non-negative number");
1436
- }
1437
- return n;
1438
- }
1439
- function parsePositiveInt(value) {
1440
- const n = Number(value);
1441
- if (!Number.isInteger(n) || n < 1) {
1442
- throw new InvalidArgumentError2("Expected a positive integer");
1443
- }
1444
- return n;
1445
- }
1446
- function strategyFromAllocatorOptions(opts) {
1447
- return {
1448
- id: "cli-lp-allocator",
1449
- name: "CLI LP allocator dry run",
1450
- status: opts.paused ? "paused" : "dry_run",
1451
- ...opts.totalHoldings !== void 0 && { total_holdings: opts.totalHoldings },
1452
- ...opts.capitalLimitPct !== void 0 && { capital_limit_pct: opts.capitalLimitPct },
1453
- ...opts.perMarketLimitPct !== void 0 && { per_market_limit_pct: opts.perMarketLimitPct },
1454
- capital_limit: opts.capitalLimit,
1455
- per_market_limit: opts.perMarketLimit,
1456
- min_expected_return_daily_pct: opts.minExpectedReturnDailyPct,
1457
- max_inventory_imbalance: opts.maxInventoryImbalance,
1458
- volatility_fill_spike_threshold: opts.volatilityFillSpikeThreshold,
1459
- event_no_quote_minutes_before: opts.eventNoQuoteMinutesBefore,
1460
- event_no_quote_minutes_after: opts.eventNoQuoteMinutesAfter,
1461
- min_liquidity: opts.allocatorMinLiquidity,
1462
- max_spread: opts.maxSpread,
1463
- min_days_to_end: opts.allocatorMinDaysToEnd,
1464
- max_markets: opts.maxMarkets
1465
- };
1466
- }
1467
- function validateAllocatorPercentageSizing(opts) {
1468
- const usesPercentageSizing = opts.capitalLimitPct !== void 0 || opts.perMarketLimitPct !== void 0;
1469
- if (usesPercentageSizing && opts.totalHoldings === void 0) {
1470
- error(
1471
- "Percentage sizing requires --total-holdings so the allocator can convert percentages into dollar caps."
1472
- );
1473
- process.exit(1);
1474
- }
1475
- }
1476
- async function runAllocatorCycle(client, payload) {
1477
- return client.post("/api/lp/allocator", payload);
1478
- }
1479
- function registerLpCommands(program2) {
1480
- const lp = program2.command("lp").description("Generate dry-run liquidity-provider allocation plans");
1481
- lp.command("allocator").description("Run the allocator agent on an explicit market list").requiredOption("--markets <file>", "Candidate market JSON array or { markets }; use '-' to read stdin").option("--allocations <file>", "Existing allocations JSON array or { allocations }; use '-' to read stdin").option("--pnl <file>", "Per-market PnL context JSON array or { pnl_context } (external wallet/inventory export); use '-' to read stdin").option("--total-holdings <usd>", "Total holdings / portfolio value used for percentage sizing", parsePositiveNumber).option("--capital-limit-pct <pct>", "Portfolio-level allocation cap as a percent of total holdings", parsePositiveNumber).option("--per-market-limit-pct <pct>", "Per-market target cap as a percent of total holdings", parsePositiveNumber).option("--capital-limit <usd>", "Portfolio capital limit for this allocator request", parseNonNegative, 500).option("--per-market-limit <usd>", "Per-market target cap", parseNonNegative, 100).option("--min-expected-return-daily-pct <pct>", "Minimum expected daily return percent", parseNonNegative, 0.02).option("--max-inventory-imbalance <ratio>", "Maximum inventory imbalance", parseNonNegative, 0.25).option("--volatility-fill-spike-threshold <ratio>", "Fill-rate imbalance that switches quotes to defensive mode", parseNonNegative, 0.35).option("--event-no-quote-minutes-before <n>", "No-quote window before scheduled events", parseNonNegative, 60).option("--event-no-quote-minutes-after <n>", "No-quote window after scheduled events", parseNonNegative, 30).option("--allocator-min-liquidity <usd>", "Allocator safety gate: minimum market liquidity", parseNonNegative, 500).option("--max-spread <ratio>", "Allocator safety gate: maximum spread", parseNonNegative, 0.12).option("--allocator-min-days-to-end <n>", "Allocator safety gate: minimum days to resolution", parseNonNegative, 3).option("--max-markets <n>", "Maximum markets allocator may target", parsePositiveInt, 5).option("--paused", "Send strategy status paused instead of dry_run").action(async (opts) => {
1482
- const globalOpts = program2.opts();
1483
- const client = new ApiClient(globalOpts);
1484
- requireAuth5(client);
1485
- validateAllocatorPercentageSizing(opts);
1486
- const markets = await readMarketPayload(opts.markets);
1487
- if (!markets || markets.length === 0) {
1488
- error("Markets JSON must include at least one market.");
1489
- process.exit(1);
1490
- }
1491
- if (opts.allocations === "-" && opts.pnl === "-") {
1492
- error("Only one of --allocations and --pnl can read from stdin.");
1493
- process.exit(1);
1494
- }
1495
- const pnlContext = await readPnlContext(opts.pnl);
1496
- const payload = {
1497
- strategy: strategyFromAllocatorOptions(opts),
1498
- markets,
1499
- allocations: await readAllocations(opts.allocations),
1500
- ...pnlContext.length > 0 && { pnl_context: pnlContext }
1501
- };
1502
- if (!globalOpts.json) {
1503
- process.stderr.write(
1504
- dim(` Running allocator dry-run on ${markets.length} provided candidates...
1505
- `)
1506
- );
1507
- }
1508
- const response = await runAllocatorCycle(client, payload);
1509
- if (globalOpts.json) {
1510
- json(response);
1511
- } else {
1512
- displayAllocatorCycleResult(response.result ?? {}, globalOpts);
1513
- }
1514
- });
1515
- }
1516
-
1517
- // src/commands/signal.ts
1518
- import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
1519
- import { readFile as readFile2 } from "fs/promises";
1520
1269
 
1521
1270
  // src/signal-display.ts
1522
- import chalk6 from "chalk";
1271
+ import chalk5 from "chalk";
1523
1272
  function pctFromSignalValue(value) {
1524
1273
  if (value === null || value === void 0 || Number.isNaN(value)) return null;
1525
1274
  return Math.abs(value) <= 1 ? value * 100 : value;
@@ -1532,14 +1281,14 @@ function formatGap(value) {
1532
1281
  if (value === null || value === void 0 || Number.isNaN(value)) return "n/a";
1533
1282
  const points = Math.abs(value) <= 1 ? value * 100 : value;
1534
1283
  const formatted = `${points >= 0 ? "+" : ""}${points.toFixed(1)}pp`;
1535
- if (points > 0) return chalk6.green(formatted);
1536
- if (points < 0) return chalk6.red(formatted);
1537
- return chalk6.dim(formatted);
1284
+ if (points > 0) return chalk5.green(formatted);
1285
+ if (points < 0) return chalk5.red(formatted);
1286
+ return chalk5.dim(formatted);
1538
1287
  }
1539
1288
  function formatStrength(value) {
1540
- if (value === "strong") return chalk6.green("strong");
1541
- if (value === "weak") return chalk6.yellow("weak");
1542
- return value ? chalk6.dim(value) : "n/a";
1289
+ if (value === "strong") return chalk5.green("strong");
1290
+ if (value === "weak") return chalk5.yellow("weak");
1291
+ return value ? chalk5.dim(value) : "n/a";
1543
1292
  }
1544
1293
  function analysisItems(result) {
1545
1294
  if (!result) return [];
@@ -1586,27 +1335,27 @@ function displaySignalAnalysis(response, globalOpts) {
1586
1335
  for (const item of items.slice(0, 3)) {
1587
1336
  const analysis = item.analysis;
1588
1337
  if (!analysis) continue;
1589
- process.stdout.write("\n " + chalk6.bold(truncate(titleFor(analysis), 76)) + "\n");
1338
+ process.stdout.write("\n " + chalk5.bold(truncate(titleFor(analysis), 76)) + "\n");
1590
1339
  if (analysis.market_link) {
1591
- process.stdout.write(" " + chalk6.dim("Polymarket: ") + analysis.market_link + "\n");
1340
+ process.stdout.write(" " + chalk5.dim("Polymarket: ") + analysis.market_link + "\n");
1592
1341
  }
1593
1342
  if (analysis.key_factors && analysis.key_factors.length > 0) {
1594
- process.stdout.write(" " + chalk6.dim("Key factors: ") + analysis.key_factors.slice(0, 4).join("; ") + "\n");
1343
+ process.stdout.write(" " + chalk5.dim("Key factors: ") + analysis.key_factors.slice(0, 4).join("; ") + "\n");
1595
1344
  }
1596
1345
  if (analysis.research_findings) {
1597
1346
  process.stdout.write(
1598
- " " + chalk6.dim("Research: ") + truncate(analysis.research_findings, 180) + "\n"
1347
+ " " + chalk5.dim("Research: ") + truncate(analysis.research_findings, 180) + "\n"
1599
1348
  );
1600
1349
  }
1601
1350
  }
1602
1351
  if (items.length > 3) {
1603
- process.stdout.write(chalk6.dim(`
1352
+ process.stdout.write(chalk5.dim(`
1604
1353
  ... and ${items.length - 3} more
1605
1354
  `));
1606
1355
  }
1607
1356
  if (result?.strong_signal_count !== void 0) {
1608
1357
  process.stdout.write(
1609
- chalk6.dim(`
1358
+ chalk5.dim(`
1610
1359
  Strong signals: ${result.strong_signal_count}
1611
1360
  `)
1612
1361
  );
@@ -1614,7 +1363,7 @@ function displaySignalAnalysis(response, globalOpts) {
1614
1363
  }
1615
1364
 
1616
1365
  // src/commands/signal.ts
1617
- function requireAuth6(client) {
1366
+ function requireAuth5(client) {
1618
1367
  if (!client.isAuthenticated) {
1619
1368
  error("Not authenticated. Run `hl auth login` first.");
1620
1369
  process.exit(1);
@@ -1626,20 +1375,20 @@ function collect(value, previous = []) {
1626
1375
  function parseProbability(value) {
1627
1376
  const n = Number(value);
1628
1377
  if (!Number.isFinite(n) || n < 0 || n > 100) {
1629
- throw new InvalidArgumentError3("Expected a probability between 0 and 100");
1378
+ throw new InvalidArgumentError("Expected a probability between 0 and 100");
1630
1379
  }
1631
1380
  return n;
1632
1381
  }
1633
- async function readStdin2() {
1382
+ async function readStdin() {
1634
1383
  const chunks = [];
1635
1384
  for await (const chunk of process.stdin) {
1636
1385
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1637
1386
  }
1638
1387
  return Buffer.concat(chunks).toString("utf8");
1639
1388
  }
1640
- async function readMarketPayload2(path) {
1389
+ async function readMarketPayload(path) {
1641
1390
  if (!path) return {};
1642
- const raw = path === "-" ? await readStdin2() : await readFile2(path, "utf8");
1391
+ const raw = path === "-" ? await readStdin() : await readFile(path, "utf8");
1643
1392
  const parsed = JSON.parse(raw);
1644
1393
  if (Array.isArray(parsed)) {
1645
1394
  return { markets: parsed };
@@ -1669,7 +1418,7 @@ async function buildSignalPayload(positionalUrl, opts) {
1669
1418
  const urls = [positionalUrl, ...opts.url ?? []].filter(
1670
1419
  (value) => Boolean(value)
1671
1420
  );
1672
- const filePayload = await readMarketPayload2(opts.market);
1421
+ const filePayload = await readMarketPayload(opts.market);
1673
1422
  const inlineMarket = inlineMarketFromOptions(opts);
1674
1423
  const hasMarketInput = Boolean(filePayload.market || filePayload.markets || inlineMarket);
1675
1424
  if (urls.length > 0 && hasMarketInput) {
@@ -1698,7 +1447,7 @@ function registerSignalCommands(program2) {
1698
1447
  signal.command("analyze").description("Estimate true YES probability and compare it with market pricing").argument("[url]", "Polymarket market/event URL to analyze").option("--url <url>", "Additional Polymarket URL; repeat for multiple markets", collect, []).option("--market <file>", "Inline market JSON object/array; use '-' to read stdin").option("--context <text>", "Prior search notes or analysis context for the agent").option("--question <text>", "Inline market question when not using a URL").option("--description <text>", "Inline market description or resolution criteria").option("--yes-prob <prob>", "Current YES probability or price, e.g. 0.52 or 52", parseProbability).option("--no-prob <prob>", "Current NO probability or price, e.g. 0.48 or 48", parseProbability).option("--slug <slug>", "Inline market slug").option("--link <url>", "Inline market link").action(async (url, o) => {
1699
1448
  const globalOpts = program2.opts();
1700
1449
  const client = new ApiClient(globalOpts);
1701
- requireAuth6(client);
1450
+ requireAuth5(client);
1702
1451
  let payload;
1703
1452
  try {
1704
1453
  payload = await buildSignalPayload(url, o);
@@ -1715,38 +1464,38 @@ function registerSignalCommands(program2) {
1715
1464
  }
1716
1465
 
1717
1466
  // src/commands/quote.ts
1718
- import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
1719
- function requireAuth7(client) {
1467
+ import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
1468
+ function requireAuth6(client) {
1720
1469
  if (!client.isAuthenticated) {
1721
1470
  error("Not authenticated. Run `hl auth login` first.");
1722
1471
  process.exit(1);
1723
1472
  }
1724
1473
  }
1725
- function parsePositiveNumber2(value) {
1474
+ function parsePositiveNumber(value) {
1726
1475
  const parsed = Number(value);
1727
1476
  if (!Number.isFinite(parsed) || parsed <= 0) {
1728
- throw new InvalidArgumentError4("Expected a positive number");
1477
+ throw new InvalidArgumentError2("Expected a positive number");
1729
1478
  }
1730
1479
  return parsed;
1731
1480
  }
1732
1481
  function parseQuoteAction(value) {
1733
1482
  const normalized = value.trim().toUpperCase();
1734
1483
  if (normalized !== "BUY" && normalized !== "SELL") {
1735
- throw new InvalidArgumentError4("Expected buy or sell");
1484
+ throw new InvalidArgumentError2("Expected buy or sell");
1736
1485
  }
1737
1486
  return normalized;
1738
1487
  }
1739
1488
  function parseQuoteOutcome(value) {
1740
1489
  const normalized = value.trim().toUpperCase();
1741
1490
  if (normalized !== "YES" && normalized !== "NO") {
1742
- throw new InvalidArgumentError4("Expected yes or no");
1491
+ throw new InvalidArgumentError2("Expected yes or no");
1743
1492
  }
1744
1493
  return normalized;
1745
1494
  }
1746
1495
  function parseQuoteRoute(value) {
1747
1496
  const normalized = value.trim().toLowerCase();
1748
1497
  if (normalized !== "auto" && normalized !== "aggressive" && normalized !== "passive") {
1749
- throw new InvalidArgumentError4("Expected auto, aggressive, or passive");
1498
+ throw new InvalidArgumentError2("Expected auto, aggressive, or passive");
1750
1499
  }
1751
1500
  return normalized;
1752
1501
  }
@@ -1874,10 +1623,10 @@ function displayQuotePreview(preview, globalOpts) {
1874
1623
  `));
1875
1624
  }
1876
1625
  function registerQuoteCommand(program2) {
1877
- program2.command("quote").description("Preview the cost, liquidity, and risk of a Polymarket trade").argument("<slug-or-url>", "Polymarket market slug or URL").requiredOption("--action <action>", "buy | sell", parseQuoteAction).requiredOption("--outcome <outcome>", "yes | no", parseQuoteOutcome).option("--cash <usd>", "Maximum cash to spend (BUY only)", parsePositiveNumber2).option("--shares <shares>", "Number of outcome shares", parsePositiveNumber2).option("--signal-id <uuid>", "Saved Signal forecast to include in edge calculations").option("--capital <usd>", "Manual portfolio capital for non-binding BUY sizing", parsePositiveNumber2).option("--route <route>", "auto | aggressive | passive", parseQuoteRoute, "auto").option("--save", "Save a freshly generated preview to quote history").action(async (instrument, opts) => {
1626
+ program2.command("quote").description("Preview the cost, liquidity, and risk of a Polymarket trade").argument("<slug-or-url>", "Polymarket market slug or URL").requiredOption("--action <action>", "buy | sell", parseQuoteAction).requiredOption("--outcome <outcome>", "yes | no", parseQuoteOutcome).option("--cash <usd>", "Maximum cash to spend (BUY only)", parsePositiveNumber).option("--shares <shares>", "Number of outcome shares", parsePositiveNumber).option("--signal-id <uuid>", "Saved Signal forecast to include in edge calculations").option("--capital <usd>", "Manual portfolio capital for non-binding BUY sizing", parsePositiveNumber).option("--route <route>", "auto | aggressive | passive", parseQuoteRoute, "auto").option("--save", "Save a freshly generated preview to quote history").action(async (instrument, opts) => {
1878
1627
  const globalOpts = program2.opts();
1879
1628
  const client = new ApiClient(globalOpts);
1880
- requireAuth7(client);
1629
+ requireAuth6(client);
1881
1630
  let payload;
1882
1631
  try {
1883
1632
  payload = buildQuotePayload(instrument, opts);
@@ -1894,14 +1643,13 @@ function registerQuoteCommand(program2) {
1894
1643
  }
1895
1644
 
1896
1645
  // src/index.ts
1897
- var program = new Command4();
1898
- program.name("hl").description("Hedge Layer CLI \u2014 prediction market intelligence from the terminal").version("3.0.1").option("--json", "Output as JSON (machine-readable)").option("--api-url <url>", "Override API base URL").option("--token <token>", "Override stored API token").option("--verbose", "Show HTTP request details").option("--no-color", "Disable colored output");
1646
+ var program = new Command3();
1647
+ program.name("hl").description("Hedge Layer CLI \u2014 prediction market intelligence from the terminal").version("4.0.1").option("--json", "Output as JSON (machine-readable)").option("--api-url <url>", "Override API base URL").option("--token <token>", "Override stored API token").option("--verbose", "Show HTTP request details").option("--no-color", "Disable colored output");
1899
1648
  registerAuthCommands(program);
1900
1649
  registerBriefCommands(program);
1901
1650
  registerProfileCommand(program);
1902
1651
  registerResearchCommands(program);
1903
1652
  registerFeedCommand(program);
1904
- registerLpCommands(program);
1905
1653
  registerSignalCommands(program);
1906
1654
  registerQuoteCommand(program);
1907
1655
  program.hook("preAction", (_thisCommand, actionCommand) => {