@hedge-layer/cli 1.8.0 → 2.0.0
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 +18 -5
- package/dist/index.mjs +148 -299
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command4 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/auth.ts
|
|
7
7
|
import readline from "readline/promises";
|
|
@@ -1073,10 +1073,9 @@ function registerFeedCommand(program2) {
|
|
|
1073
1073
|
});
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
1076
|
-
// src/commands/
|
|
1077
|
-
import { InvalidArgumentError } from "commander";
|
|
1078
|
-
import {
|
|
1079
|
-
import chalk6 from "chalk";
|
|
1076
|
+
// src/commands/lp.ts
|
|
1077
|
+
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
1078
|
+
import { writeFile } from "fs/promises";
|
|
1080
1079
|
|
|
1081
1080
|
// src/allocator-display.ts
|
|
1082
1081
|
import chalk5 from "chalk";
|
|
@@ -1100,38 +1099,6 @@ function num(value, fallback = 0) {
|
|
|
1100
1099
|
const n = Number(value);
|
|
1101
1100
|
return Number.isFinite(n) ? n : fallback;
|
|
1102
1101
|
}
|
|
1103
|
-
function feedMarketsToAllocatorMarkets(markets) {
|
|
1104
|
-
return markets.slice(0, 25).map((m) => {
|
|
1105
|
-
const probability = num(m.probability ?? m.yesPrice, 0.5);
|
|
1106
|
-
const daysToEnd = m.daysToEnd == null || m.daysToEnd === void 0 ? void 0 : num(m.daysToEnd);
|
|
1107
|
-
return {
|
|
1108
|
-
slug: String(m.slug ?? ""),
|
|
1109
|
-
question: String(m.question ?? ""),
|
|
1110
|
-
yesTokenId: m.yesTokenId ? String(m.yesTokenId) : void 0,
|
|
1111
|
-
noTokenId: m.noTokenId ? String(m.noTokenId) : void 0,
|
|
1112
|
-
yesPrice: num(m.yesPrice, probability),
|
|
1113
|
-
noPrice: num(m.noPrice, 1 - probability),
|
|
1114
|
-
liquidity: num(m.liquidity),
|
|
1115
|
-
volume24h: num(m.volume24h),
|
|
1116
|
-
spread: num(m.spread),
|
|
1117
|
-
rewardsDailyRate: num(m.rewardsDailyRate),
|
|
1118
|
-
oneDayPriceChange: num(m.oneDayPriceChange),
|
|
1119
|
-
...daysToEnd !== void 0 && { daysToEnd },
|
|
1120
|
-
active: m.active == null ? true : Boolean(m.active)
|
|
1121
|
-
};
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
function allocationsFromDecisions(decisions) {
|
|
1125
|
-
return decisions.filter((d) => String(d.market_slug ?? "")).map((d) => ({
|
|
1126
|
-
market_slug: String(d.market_slug),
|
|
1127
|
-
status: String(d.action ?? "planned").toLowerCase(),
|
|
1128
|
-
allocated_capital: num(d.target_capital),
|
|
1129
|
-
locked_capital: 0,
|
|
1130
|
-
inventory_yes: 0,
|
|
1131
|
-
inventory_no: 0,
|
|
1132
|
-
open_order_notional: Array.isArray(d.order_plan) ? d.order_plan.reduce((sum, order) => sum + num(order.notional), 0) : 0
|
|
1133
|
-
}));
|
|
1134
|
-
}
|
|
1135
1102
|
function displayAllocatorCycleResult(result, globalOpts) {
|
|
1136
1103
|
if (globalOpts.json) {
|
|
1137
1104
|
json(result);
|
|
@@ -1145,7 +1112,7 @@ function displayAllocatorCycleResult(result, globalOpts) {
|
|
|
1145
1112
|
chalk5.dim(
|
|
1146
1113
|
` ${num(result.total_markets, decisions.length)} markets \xB7 ${currency(
|
|
1147
1114
|
num(summary.target_capital)
|
|
1148
|
-
)} target capital
|
|
1115
|
+
)} target capital
|
|
1149
1116
|
|
|
1150
1117
|
`
|
|
1151
1118
|
)
|
|
@@ -1158,7 +1125,6 @@ function displayAllocatorCycleResult(result, globalOpts) {
|
|
|
1158
1125
|
const action = String(d.action ?? "UNKNOWN");
|
|
1159
1126
|
const score = num(d.score?.score);
|
|
1160
1127
|
const expected = num(d.score?.expected_return_daily_pct);
|
|
1161
|
-
const orders = Array.isArray(d.order_plan) ? d.order_plan.length : 0;
|
|
1162
1128
|
const failedChecks = Array.isArray(d.safety_checks) ? d.safety_checks.filter((check) => check.passed === false).length : 0;
|
|
1163
1129
|
return [
|
|
1164
1130
|
truncate(String(d.question ?? d.market_slug ?? "\u2014"), 42),
|
|
@@ -1166,12 +1132,21 @@ function displayAllocatorCycleResult(result, globalOpts) {
|
|
|
1166
1132
|
currency(num(d.target_capital)),
|
|
1167
1133
|
signedCurrency(num(d.capital_delta)),
|
|
1168
1134
|
`${expected.toFixed(3)}%`,
|
|
1135
|
+
regimeLabel(String(d.quote_regime ?? "\u2014")),
|
|
1169
1136
|
String(Math.round(score)),
|
|
1170
|
-
String(orders),
|
|
1171
1137
|
failedChecks === 0 ? chalk5.green("0") : chalk5.yellow(String(failedChecks))
|
|
1172
1138
|
];
|
|
1173
1139
|
});
|
|
1174
|
-
table(rows, [
|
|
1140
|
+
table(rows, [
|
|
1141
|
+
"Market",
|
|
1142
|
+
"Action",
|
|
1143
|
+
"Target",
|
|
1144
|
+
"Delta",
|
|
1145
|
+
"Exp/day",
|
|
1146
|
+
"Regime",
|
|
1147
|
+
"Score",
|
|
1148
|
+
"Fails"
|
|
1149
|
+
]);
|
|
1175
1150
|
process.stdout.write("\n");
|
|
1176
1151
|
for (const decision of decisions.slice(0, 5)) {
|
|
1177
1152
|
const action = String(decision.action ?? "UNKNOWN");
|
|
@@ -1181,17 +1156,13 @@ function displayAllocatorCycleResult(result, globalOpts) {
|
|
|
1181
1156
|
process.stdout.write(" " + chalk5.dim("Rationale: ") + truncate(decision.rationale, 120));
|
|
1182
1157
|
}
|
|
1183
1158
|
process.stdout.write("\n");
|
|
1184
|
-
const
|
|
1185
|
-
|
|
1159
|
+
const economics = decision.economics ?? {};
|
|
1160
|
+
if (economics.realized_spread_pnl !== void 0 || economics.reward_income !== void 0 || economics.net_realized_pnl !== void 0) {
|
|
1186
1161
|
process.stdout.write(
|
|
1187
|
-
" " + chalk5.dim("
|
|
1162
|
+
" " + chalk5.dim("Economics: ") + `spread ${signedCurrency(num(economics.realized_spread_pnl))}, rewards ${signedCurrency(num(economics.reward_income))}, net ${signedCurrency(num(economics.net_realized_pnl))}
|
|
1188
1163
|
`
|
|
1189
1164
|
);
|
|
1190
1165
|
}
|
|
1191
|
-
if (orders.length > 2) {
|
|
1192
|
-
process.stdout.write(chalk5.dim(` \u2026 ${orders.length - 2} more orders
|
|
1193
|
-
`));
|
|
1194
|
-
}
|
|
1195
1166
|
}
|
|
1196
1167
|
if (decisions.length > 5) {
|
|
1197
1168
|
process.stdout.write(chalk5.dim(`
|
|
@@ -1199,6 +1170,18 @@ function displayAllocatorCycleResult(result, globalOpts) {
|
|
|
1199
1170
|
`));
|
|
1200
1171
|
}
|
|
1201
1172
|
}
|
|
1173
|
+
function regimeLabel(value) {
|
|
1174
|
+
switch (value) {
|
|
1175
|
+
case "reward_optimized":
|
|
1176
|
+
return chalk5.green("reward");
|
|
1177
|
+
case "defensive":
|
|
1178
|
+
return chalk5.yellow("defense");
|
|
1179
|
+
case "no_quote":
|
|
1180
|
+
return chalk5.red("no quote");
|
|
1181
|
+
default:
|
|
1182
|
+
return chalk5.dim(value);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1202
1185
|
function signedCurrency(value) {
|
|
1203
1186
|
const formatted = currency(Math.abs(value));
|
|
1204
1187
|
if (value > 0) return chalk5.green(`+${formatted}`);
|
|
@@ -1207,23 +1190,8 @@ function signedCurrency(value) {
|
|
|
1207
1190
|
}
|
|
1208
1191
|
|
|
1209
1192
|
// src/commands/allocator.ts
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
if (!client.isAuthenticated) {
|
|
1213
|
-
error("Not authenticated. Run `hl auth login` first.");
|
|
1214
|
-
process.exit(1);
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
function isProfile2(s) {
|
|
1218
|
-
return s !== void 0 && PROFILE_CHOICES2.includes(s);
|
|
1219
|
-
}
|
|
1220
|
-
function parseNonNegative(value) {
|
|
1221
|
-
const n = Number(value);
|
|
1222
|
-
if (!Number.isFinite(n) || n < 0) {
|
|
1223
|
-
throw new InvalidArgumentError("Expected a non-negative number");
|
|
1224
|
-
}
|
|
1225
|
-
return n;
|
|
1226
|
-
}
|
|
1193
|
+
import { InvalidArgumentError } from "commander";
|
|
1194
|
+
import { readFile } from "fs/promises";
|
|
1227
1195
|
function parsePositiveNumber(value) {
|
|
1228
1196
|
const n = Number(value);
|
|
1229
1197
|
if (!Number.isFinite(n) || n <= 0) {
|
|
@@ -1231,61 +1199,6 @@ function parsePositiveNumber(value) {
|
|
|
1231
1199
|
}
|
|
1232
1200
|
return n;
|
|
1233
1201
|
}
|
|
1234
|
-
function parsePositiveInt(value) {
|
|
1235
|
-
const n = Number(value);
|
|
1236
|
-
if (!Number.isInteger(n) || n < 1) {
|
|
1237
|
-
throw new InvalidArgumentError("Expected a positive integer");
|
|
1238
|
-
}
|
|
1239
|
-
return n;
|
|
1240
|
-
}
|
|
1241
|
-
function maybeAdd(entries, key, value) {
|
|
1242
|
-
if (value !== void 0 && value !== "") entries.push([key, value]);
|
|
1243
|
-
}
|
|
1244
|
-
function feedQueryParams2(profile, opts) {
|
|
1245
|
-
const entries = [];
|
|
1246
|
-
maybeAdd(entries, "profile", profile);
|
|
1247
|
-
maybeAdd(entries, "sortBy", opts.sortBy);
|
|
1248
|
-
maybeAdd(entries, "tag", opts.tag);
|
|
1249
|
-
maybeAdd(entries, "minVolume", opts.minVolume);
|
|
1250
|
-
maybeAdd(entries, "minLiquidity", opts.minLiquidity);
|
|
1251
|
-
maybeAdd(entries, "maxLiquidity", opts.maxLiquidity);
|
|
1252
|
-
maybeAdd(entries, "minRewardsDailyRate", opts.minRewardsDailyRate);
|
|
1253
|
-
maybeAdd(entries, "minDaysToEnd", opts.minDaysToEnd);
|
|
1254
|
-
maybeAdd(entries, "maxDaysToEnd", opts.maxDaysToEnd);
|
|
1255
|
-
maybeAdd(entries, "maxMarketAgeHours", opts.maxMarketAgeHours);
|
|
1256
|
-
maybeAdd(entries, "liquidProfile", opts.liquidProfile);
|
|
1257
|
-
maybeAdd(entries, "limit", opts.limit);
|
|
1258
|
-
return Object.fromEntries(entries);
|
|
1259
|
-
}
|
|
1260
|
-
function strategyFromOptions(opts) {
|
|
1261
|
-
return {
|
|
1262
|
-
id: "cli-dry-run",
|
|
1263
|
-
name: "CLI dry-run LP strategy",
|
|
1264
|
-
status: opts.paused ? "paused" : "dry_run",
|
|
1265
|
-
...opts.totalHoldings !== void 0 && { total_holdings: opts.totalHoldings },
|
|
1266
|
-
...opts.capitalLimitPct !== void 0 && { capital_limit_pct: opts.capitalLimitPct },
|
|
1267
|
-
...opts.perMarketLimitPct !== void 0 && { per_market_limit_pct: opts.perMarketLimitPct },
|
|
1268
|
-
capital_limit: opts.capitalLimit,
|
|
1269
|
-
per_market_limit: opts.perMarketLimit,
|
|
1270
|
-
min_expected_return_daily_pct: opts.minExpectedReturnDailyPct,
|
|
1271
|
-
max_inventory_imbalance: opts.maxInventoryImbalance,
|
|
1272
|
-
max_order_notional: opts.maxOrderNotional,
|
|
1273
|
-
quote_edge_bps: opts.quoteEdgeBps,
|
|
1274
|
-
min_liquidity: opts.allocatorMinLiquidity,
|
|
1275
|
-
max_spread: opts.maxSpread,
|
|
1276
|
-
min_days_to_end: opts.allocatorMinDaysToEnd,
|
|
1277
|
-
max_markets: opts.maxMarkets
|
|
1278
|
-
};
|
|
1279
|
-
}
|
|
1280
|
-
function validatePercentageSizing(opts) {
|
|
1281
|
-
const usesPercentageSizing = opts.capitalLimitPct !== void 0 || opts.perMarketLimitPct !== void 0;
|
|
1282
|
-
if (usesPercentageSizing && opts.totalHoldings === void 0) {
|
|
1283
|
-
error(
|
|
1284
|
-
"Percentage sizing requires --total-holdings so the allocator can convert percentages into dollar caps."
|
|
1285
|
-
);
|
|
1286
|
-
process.exit(1);
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
1202
|
async function readAllocations(path) {
|
|
1290
1203
|
if (!path) return [];
|
|
1291
1204
|
const raw = path === "-" ? await readStdin() : await readFile(path, "utf8");
|
|
@@ -1295,6 +1208,18 @@ async function readAllocations(path) {
|
|
|
1295
1208
|
}
|
|
1296
1209
|
return parsed;
|
|
1297
1210
|
}
|
|
1211
|
+
async function readMarketPayload(path) {
|
|
1212
|
+
if (!path) return void 0;
|
|
1213
|
+
const raw = path === "-" ? await readStdin() : await readFile(path, "utf8");
|
|
1214
|
+
const parsed = JSON.parse(raw);
|
|
1215
|
+
if (Array.isArray(parsed)) {
|
|
1216
|
+
return parsed;
|
|
1217
|
+
}
|
|
1218
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.markets)) {
|
|
1219
|
+
return parsed.markets;
|
|
1220
|
+
}
|
|
1221
|
+
throw new Error("Markets JSON must be an array or { markets: [...] }");
|
|
1222
|
+
}
|
|
1298
1223
|
async function readStdin() {
|
|
1299
1224
|
const chunks = [];
|
|
1300
1225
|
for await (const chunk of process.stdin) {
|
|
@@ -1302,92 +1227,18 @@ async function readStdin() {
|
|
|
1302
1227
|
}
|
|
1303
1228
|
return Buffer.concat(chunks).toString("utf8");
|
|
1304
1229
|
}
|
|
1305
|
-
async function runAllocatorCycle(client, payload) {
|
|
1306
|
-
return client.post("/api/allocator/cycle", payload);
|
|
1307
|
-
}
|
|
1308
|
-
function registerAllocatorCommands(program2) {
|
|
1309
|
-
const allocator = program2.command("allocator").description("Run dry-run liquidity allocation cycles");
|
|
1310
|
-
allocator.command("cycle").description("Find LP candidates from /api/feed and run a dry-run allocator cycle").argument(
|
|
1311
|
-
"[screening]",
|
|
1312
|
-
`Optional screening preset: ${PROFILE_CHOICES2.join(" | ")} (same as --profile)`,
|
|
1313
|
-
"lp-opportunity"
|
|
1314
|
-
).option("--profile <name>", `Screening defaults: ${PROFILE_CHOICES2.join(", ")}`).option("--sort-by <key>", "score | volume | liquidity | movement | spread | rewards | rewardYield | lpExpectedReturn | horizon").option("--tag <slug>", "Polymarket category tag, e.g. crypto, politics").option("--min-volume <usd>", "Minimum 24h volume (USD)").option("--min-liquidity <usd>", "Minimum displayed liquidity (USD)").option("--max-liquidity <usd>", "Maximum displayed liquidity (USD)").option("--min-rewards-daily-rate <usd>", "Minimum LP rewards USD/day").option("--min-days-to-end <n>", "Feed filter: minimum days until resolution").option("--max-days-to-end <n>", "Feed filter: maximum days until resolution").option("--max-market-age-hours <n>", "With liquid-new-or-long: max age for the new branch").option("--liquid-profile <mode>", "new-or-long (used by liquid-new-or-long screen)").option("--limit <n>", "Max feed markets to fetch (1-100, default 15)", "15").option("--total-holdings <usd>", "Total user 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 cycle", 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("--max-order-notional <usd>", "Maximum notional per planned passive order", parseNonNegative, 25).option("--quote-edge-bps <bps>", "Passive quote edge in basis points", parseNonNegative, 100).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("--allocations <file>", "Existing allocations JSON array; use '-' to read stdin").option("--repeat", "Run a second cycle using targets returned by the first cycle").option("--paused", "Send strategy status paused instead of dry_run").action(async (screening, o) => {
|
|
1315
|
-
const globalOpts = program2.opts();
|
|
1316
|
-
const client = new ApiClient(globalOpts);
|
|
1317
|
-
requireAuth5(client);
|
|
1318
|
-
validatePercentageSizing(o);
|
|
1319
|
-
let profile = o.profile ?? screening ?? "lp-opportunity";
|
|
1320
|
-
if (!isProfile2(profile)) {
|
|
1321
|
-
error(`Unknown screening "${profile}". Use: ${PROFILE_CHOICES2.join(" or ")}`);
|
|
1322
|
-
process.exit(1);
|
|
1323
|
-
}
|
|
1324
|
-
if (o.profile && screening && screening !== "lp-opportunity" && o.profile !== screening) {
|
|
1325
|
-
warn(`Both positional and --profile set; using --profile (${o.profile}).`);
|
|
1326
|
-
profile = o.profile;
|
|
1327
|
-
}
|
|
1328
|
-
const feed = await client.get("/api/feed", feedQueryParams2(profile, o));
|
|
1329
|
-
if (feed.error) {
|
|
1330
|
-
error(feed.error);
|
|
1331
|
-
process.exit(1);
|
|
1332
|
-
}
|
|
1333
|
-
if (feed.markets.length === 0) {
|
|
1334
|
-
warn("No feed markets matched the allocator criteria.");
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
const allocations = await readAllocations(o.allocations);
|
|
1338
|
-
const payload = {
|
|
1339
|
-
strategy: strategyFromOptions(o),
|
|
1340
|
-
markets: feedMarketsToAllocatorMarkets(feed.markets),
|
|
1341
|
-
allocations
|
|
1342
|
-
};
|
|
1343
|
-
if (!globalOpts.json) {
|
|
1344
|
-
process.stderr.write(
|
|
1345
|
-
chalk6.dim(
|
|
1346
|
-
` Running allocator dry-run on ${feed.markets.length} ${profile} candidates...
|
|
1347
|
-
`
|
|
1348
|
-
)
|
|
1349
|
-
);
|
|
1350
|
-
}
|
|
1351
|
-
const first = await runAllocatorCycle(client, payload);
|
|
1352
|
-
const firstResult = first.result ?? {};
|
|
1353
|
-
if (o.repeat) {
|
|
1354
|
-
const repeatPayload = {
|
|
1355
|
-
...payload,
|
|
1356
|
-
allocations: allocationsFromDecisions(firstResult.decisions ?? [])
|
|
1357
|
-
};
|
|
1358
|
-
const second = await runAllocatorCycle(client, repeatPayload);
|
|
1359
|
-
if (globalOpts.json) {
|
|
1360
|
-
json({ initial: first, repeat: second });
|
|
1361
|
-
} else {
|
|
1362
|
-
displayAllocatorCycleResult(firstResult, globalOpts);
|
|
1363
|
-
process.stdout.write("\n" + chalk6.bold("Repeated with target allocations") + "\n");
|
|
1364
|
-
displayAllocatorCycleResult(second.result ?? {}, globalOpts);
|
|
1365
|
-
}
|
|
1366
|
-
return;
|
|
1367
|
-
}
|
|
1368
|
-
if (globalOpts.json) {
|
|
1369
|
-
json(first);
|
|
1370
|
-
} else {
|
|
1371
|
-
displayAllocatorCycleResult(firstResult, globalOpts);
|
|
1372
|
-
}
|
|
1373
|
-
});
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
// src/commands/lp.ts
|
|
1377
|
-
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
1378
|
-
import { writeFile } from "fs/promises";
|
|
1379
1230
|
|
|
1380
1231
|
// src/lp-display.ts
|
|
1381
|
-
import
|
|
1232
|
+
import chalk6 from "chalk";
|
|
1382
1233
|
function num2(value, fallback = 0) {
|
|
1383
1234
|
const n = Number(value);
|
|
1384
1235
|
return Number.isFinite(n) ? n : fallback;
|
|
1385
1236
|
}
|
|
1386
1237
|
function signedCurrency2(value) {
|
|
1387
1238
|
const formatted = currency(Math.abs(value));
|
|
1388
|
-
if (value > 0) return
|
|
1389
|
-
if (value < 0) return
|
|
1390
|
-
return
|
|
1239
|
+
if (value > 0) return chalk6.green(`+${formatted}`);
|
|
1240
|
+
if (value < 0) return chalk6.yellow(`-${formatted}`);
|
|
1241
|
+
return chalk6.dim("$0.00");
|
|
1391
1242
|
}
|
|
1392
1243
|
function actionSummary(actions) {
|
|
1393
1244
|
if (!actions) return "none";
|
|
@@ -1400,13 +1251,13 @@ function displayLpScanResult(result, globalOpts) {
|
|
|
1400
1251
|
}
|
|
1401
1252
|
heading("LP Scan");
|
|
1402
1253
|
process.stdout.write(
|
|
1403
|
-
|
|
1254
|
+
chalk6.dim(
|
|
1404
1255
|
` scan ${result.scanId} \xB7 strategy ${result.strategyId} \xB7 ${result.evidenceSaved} evidence rows saved
|
|
1405
1256
|
`
|
|
1406
1257
|
)
|
|
1407
1258
|
);
|
|
1408
1259
|
process.stdout.write(
|
|
1409
|
-
|
|
1260
|
+
chalk6.dim(
|
|
1410
1261
|
` ${result.totalScanned.toLocaleString()} scanned \xB7 ${result.totalAfterFilter.toLocaleString()} after filters \xB7 profile ${result.profile}
|
|
1411
1262
|
|
|
1412
1263
|
`
|
|
@@ -1435,13 +1286,13 @@ function displayLpRecommendResult(result, globalOpts) {
|
|
|
1435
1286
|
}
|
|
1436
1287
|
heading("LP Recommendations");
|
|
1437
1288
|
process.stdout.write(
|
|
1438
|
-
|
|
1289
|
+
chalk6.dim(
|
|
1439
1290
|
` cycle ${result.cycleId} \xB7 strategy ${result.strategyId} \xB7 ${result.candidatesSubmitted} markets \xB7 ${result.allocationsSubmitted} current allocations
|
|
1440
1291
|
`
|
|
1441
1292
|
)
|
|
1442
1293
|
);
|
|
1443
1294
|
process.stdout.write(
|
|
1444
|
-
|
|
1295
|
+
chalk6.dim(
|
|
1445
1296
|
` PnL context ${result.pnlContextCount} rows${result.pnlSynced ? " \xB7 synced" : ""} \xB7 approvals required
|
|
1446
1297
|
|
|
1447
1298
|
`
|
|
@@ -1456,11 +1307,11 @@ function displayLpEvaluateResult(result, globalOpts) {
|
|
|
1456
1307
|
}
|
|
1457
1308
|
heading("LP Evaluation");
|
|
1458
1309
|
process.stdout.write(
|
|
1459
|
-
|
|
1310
|
+
chalk6.dim(
|
|
1460
1311
|
` strategy ${result.strategyId} \xB7 ${result.summary.snapshots} snapshots \xB7 ${result.summary.markets} markets`
|
|
1461
1312
|
)
|
|
1462
1313
|
);
|
|
1463
|
-
if (result.pnlSynced) process.stdout.write(
|
|
1314
|
+
if (result.pnlSynced) process.stdout.write(chalk6.dim(" \xB7 synced"));
|
|
1464
1315
|
process.stdout.write("\n\n");
|
|
1465
1316
|
if (result.syncError) warn(result.syncError);
|
|
1466
1317
|
table(
|
|
@@ -1488,58 +1339,58 @@ function displayLpEvaluateResult(result, globalOpts) {
|
|
|
1488
1339
|
["Market", "Outcome", "Net", "Lesson"]
|
|
1489
1340
|
);
|
|
1490
1341
|
}
|
|
1491
|
-
function displayLpRunResult(result, globalOpts) {
|
|
1492
|
-
if (globalOpts.json) {
|
|
1493
|
-
json(result);
|
|
1494
|
-
return;
|
|
1495
|
-
}
|
|
1496
|
-
heading("LP Run");
|
|
1497
|
-
process.stdout.write(
|
|
1498
|
-
chalk7.dim(
|
|
1499
|
-
` cycle ${result.run.cycleId} \xB7 scan ${result.run.scanId ?? "n/a"} \xB7 strategy ${result.run.strategyId}
|
|
1500
|
-
`
|
|
1501
|
-
)
|
|
1502
|
-
);
|
|
1503
|
-
process.stdout.write(
|
|
1504
|
-
chalk7.dim(
|
|
1505
|
-
` ${result.run.opportunitiesFound} opportunities \xB7 PnL ${result.run.pnlSynced ? "synced" : "not synced"} \xB7 approvals required
|
|
1506
|
-
|
|
1507
|
-
`
|
|
1508
|
-
)
|
|
1509
|
-
);
|
|
1510
|
-
displayAllocatorCycleResult(result.run.result, globalOpts);
|
|
1511
|
-
if (result.evaluation) {
|
|
1512
|
-
process.stdout.write("\n");
|
|
1513
|
-
process.stdout.write(
|
|
1514
|
-
chalk7.dim(
|
|
1515
|
-
` Evaluation: ${signedCurrency2(result.evaluation.summary.netPnl)} net PnL across ${result.evaluation.summary.markets} markets
|
|
1516
|
-
`
|
|
1517
|
-
)
|
|
1518
|
-
);
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
1342
|
|
|
1522
1343
|
// src/commands/lp.ts
|
|
1523
|
-
function
|
|
1344
|
+
function requireAuth5(client) {
|
|
1524
1345
|
if (!client.isAuthenticated) {
|
|
1525
1346
|
error("Not authenticated. Run `hl auth login` first.");
|
|
1526
1347
|
process.exit(1);
|
|
1527
1348
|
}
|
|
1528
1349
|
}
|
|
1529
|
-
function
|
|
1350
|
+
function parseNonNegative(value) {
|
|
1530
1351
|
const n = Number(value);
|
|
1531
1352
|
if (!Number.isFinite(n) || n < 0) {
|
|
1532
1353
|
throw new InvalidArgumentError2("Expected a non-negative number");
|
|
1533
1354
|
}
|
|
1534
1355
|
return n;
|
|
1535
1356
|
}
|
|
1536
|
-
function
|
|
1357
|
+
function parsePositiveInt(value) {
|
|
1537
1358
|
const n = Number(value);
|
|
1538
1359
|
if (!Number.isInteger(n) || n < 1) {
|
|
1539
1360
|
throw new InvalidArgumentError2("Expected a positive integer");
|
|
1540
1361
|
}
|
|
1541
1362
|
return n;
|
|
1542
1363
|
}
|
|
1364
|
+
function strategyFromAllocatorOptions(opts) {
|
|
1365
|
+
return {
|
|
1366
|
+
id: "cli-lp-allocator",
|
|
1367
|
+
name: "CLI LP allocator dry run",
|
|
1368
|
+
status: opts.paused ? "paused" : "dry_run",
|
|
1369
|
+
...opts.totalHoldings !== void 0 && { total_holdings: opts.totalHoldings },
|
|
1370
|
+
...opts.capitalLimitPct !== void 0 && { capital_limit_pct: opts.capitalLimitPct },
|
|
1371
|
+
...opts.perMarketLimitPct !== void 0 && { per_market_limit_pct: opts.perMarketLimitPct },
|
|
1372
|
+
capital_limit: opts.capitalLimit,
|
|
1373
|
+
per_market_limit: opts.perMarketLimit,
|
|
1374
|
+
min_expected_return_daily_pct: opts.minExpectedReturnDailyPct,
|
|
1375
|
+
max_inventory_imbalance: opts.maxInventoryImbalance,
|
|
1376
|
+
volatility_fill_spike_threshold: opts.volatilityFillSpikeThreshold,
|
|
1377
|
+
event_no_quote_minutes_before: opts.eventNoQuoteMinutesBefore,
|
|
1378
|
+
event_no_quote_minutes_after: opts.eventNoQuoteMinutesAfter,
|
|
1379
|
+
min_liquidity: opts.allocatorMinLiquidity,
|
|
1380
|
+
max_spread: opts.maxSpread,
|
|
1381
|
+
min_days_to_end: opts.allocatorMinDaysToEnd,
|
|
1382
|
+
max_markets: opts.maxMarkets
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
function validateAllocatorPercentageSizing(opts) {
|
|
1386
|
+
const usesPercentageSizing = opts.capitalLimitPct !== void 0 || opts.perMarketLimitPct !== void 0;
|
|
1387
|
+
if (usesPercentageSizing && opts.totalHoldings === void 0) {
|
|
1388
|
+
error(
|
|
1389
|
+
"Percentage sizing requires --total-holdings so the allocator can convert percentages into dollar caps."
|
|
1390
|
+
);
|
|
1391
|
+
process.exit(1);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1543
1394
|
function compact(payload) {
|
|
1544
1395
|
return Object.fromEntries(
|
|
1545
1396
|
Object.entries(payload).filter(([, value]) => value !== void 0 && value !== "")
|
|
@@ -1587,19 +1438,43 @@ function buildLpEvaluatePayload(opts) {
|
|
|
1587
1438
|
limit: opts.limit
|
|
1588
1439
|
});
|
|
1589
1440
|
}
|
|
1590
|
-
function
|
|
1591
|
-
return
|
|
1592
|
-
strategyId: opts.strategyId,
|
|
1593
|
-
limit: opts.limit,
|
|
1594
|
-
syncPnl: opts.syncPnl
|
|
1595
|
-
});
|
|
1441
|
+
async function runAllocatorCycle(client, payload) {
|
|
1442
|
+
return client.post("/api/lp/allocator", payload);
|
|
1596
1443
|
}
|
|
1597
1444
|
function registerLpCommands(program2) {
|
|
1598
1445
|
const lp = program2.command("lp").description("Run persisted liquidity-provider scan, recommendation, and evaluation workflows");
|
|
1599
|
-
lp.command("
|
|
1446
|
+
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; 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) => {
|
|
1600
1447
|
const globalOpts = program2.opts();
|
|
1601
1448
|
const client = new ApiClient(globalOpts);
|
|
1602
|
-
|
|
1449
|
+
requireAuth5(client);
|
|
1450
|
+
validateAllocatorPercentageSizing(opts);
|
|
1451
|
+
const markets = await readMarketPayload(opts.markets);
|
|
1452
|
+
if (!markets || markets.length === 0) {
|
|
1453
|
+
error("Markets JSON must include at least one market.");
|
|
1454
|
+
process.exit(1);
|
|
1455
|
+
}
|
|
1456
|
+
const payload = {
|
|
1457
|
+
strategy: strategyFromAllocatorOptions(opts),
|
|
1458
|
+
markets,
|
|
1459
|
+
allocations: await readAllocations(opts.allocations)
|
|
1460
|
+
};
|
|
1461
|
+
if (!globalOpts.json) {
|
|
1462
|
+
process.stderr.write(
|
|
1463
|
+
dim(` Running allocator dry-run on ${markets.length} provided candidates...
|
|
1464
|
+
`)
|
|
1465
|
+
);
|
|
1466
|
+
}
|
|
1467
|
+
const response = await runAllocatorCycle(client, payload);
|
|
1468
|
+
if (globalOpts.json) {
|
|
1469
|
+
json(response);
|
|
1470
|
+
} else {
|
|
1471
|
+
displayAllocatorCycleResult(response.result ?? {}, globalOpts);
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
lp.command("scan").description("Scan LP candidates and persist them as evidence").argument("[topic]", "Human label for this scan, e.g. liquidity opportunities").option("--profile <name>", "lp-opportunity | liquidity-provider | liquid-new-or-long", "liquidity-provider").option("--sort-by <key>", "score | volume | liquidity | movement | spread | rewards | rewardYield | lpExpectedReturn | horizon").option("--tag <slug>", "Polymarket category tag, e.g. crypto, politics").option("--min-volume <usd>", "Minimum 24h volume (USD)", parseNonNegative).option("--min-liquidity <usd>", "Minimum displayed liquidity (USD)", parseNonNegative).option("--max-liquidity <usd>", "Maximum displayed liquidity (USD)", parseNonNegative).option("--min-rewards-daily-rate <usd>", "Minimum LP rewards USD/day", parseNonNegative).option("--min-days-to-end <n>", "Minimum days until resolution", parseNonNegative).option("--max-days-to-end <n>", "Maximum days until resolution", parseNonNegative).option("--max-market-age-hours <n>", "With liquid-new-or-long: max age for the new branch", parseNonNegative).option("--liquid-profile <mode>", "new-or-long").option("--limit <n>", "Max markets to scan/save (1-100, default 15)", parsePositiveInt, 15).option("--strategy-id <uuid>", "LP strategy id").option("--output <file>", "Write the evidence JSON response to a local file").action(async (topic, opts) => {
|
|
1475
|
+
const globalOpts = program2.opts();
|
|
1476
|
+
const client = new ApiClient(globalOpts);
|
|
1477
|
+
requireAuth5(client);
|
|
1603
1478
|
const result = await client.post(
|
|
1604
1479
|
"/api/lp/scan",
|
|
1605
1480
|
buildLpScanPayload(topic, opts)
|
|
@@ -1607,10 +1482,10 @@ function registerLpCommands(program2) {
|
|
|
1607
1482
|
await writeArtifact(opts.output, result, Boolean(globalOpts.json));
|
|
1608
1483
|
displayLpScanResult(result, globalOpts);
|
|
1609
1484
|
});
|
|
1610
|
-
lp.command("recommend").description("Run allocator recommendations from saved LP evidence and current allocations").option("--strategy-id <uuid>", "LP strategy id").option("--scan-id <uuid>", "Use candidates from a specific saved scan").option("--limit <n>", "Max saved candidates to submit (1-25, default 15)",
|
|
1485
|
+
lp.command("recommend").description("Run allocator recommendations from saved LP evidence and current allocations").option("--strategy-id <uuid>", "LP strategy id").option("--scan-id <uuid>", "Use candidates from a specific saved scan").option("--limit <n>", "Max saved candidates to submit (1-25, default 15)", parsePositiveInt, 15).option("--sync-pnl", "Refresh wallet PnL before recommending").option("--output <file>", "Write the recommendation JSON response to a local file").action(async (opts) => {
|
|
1611
1486
|
const globalOpts = program2.opts();
|
|
1612
1487
|
const client = new ApiClient(globalOpts);
|
|
1613
|
-
|
|
1488
|
+
requireAuth5(client);
|
|
1614
1489
|
const result = await client.post(
|
|
1615
1490
|
"/api/lp/recommend",
|
|
1616
1491
|
buildLpRecommendPayload(opts)
|
|
@@ -1618,10 +1493,10 @@ function registerLpCommands(program2) {
|
|
|
1618
1493
|
await writeArtifact(opts.output, result, Boolean(globalOpts.json));
|
|
1619
1494
|
displayLpRecommendResult(result, globalOpts);
|
|
1620
1495
|
});
|
|
1621
|
-
lp.command("evaluate").description("Evaluate LP performance and return compact lessons").option("--strategy-id <uuid>", "LP strategy id").option("--wallet-address <address>", "Wallet address to sync PnL from").option("--no-sync-pnl", "Use existing PnL snapshots without refreshing").option("--limit <n>", "Max PnL snapshots to evaluate (1-100, default 50)",
|
|
1496
|
+
lp.command("evaluate").description("Evaluate LP performance and return compact lessons").option("--strategy-id <uuid>", "LP strategy id").option("--wallet-address <address>", "Wallet address to sync PnL from").option("--no-sync-pnl", "Use existing PnL snapshots without refreshing").option("--limit <n>", "Max PnL snapshots to evaluate (1-100, default 50)", parsePositiveInt, 50).option("--output <file>", "Write the evaluation JSON response to a local file").action(async (opts) => {
|
|
1622
1497
|
const globalOpts = program2.opts();
|
|
1623
1498
|
const client = new ApiClient(globalOpts);
|
|
1624
|
-
|
|
1499
|
+
requireAuth5(client);
|
|
1625
1500
|
const result = await client.post(
|
|
1626
1501
|
"/api/lp/evaluate",
|
|
1627
1502
|
buildLpEvaluatePayload(opts)
|
|
@@ -1629,31 +1504,6 @@ function registerLpCommands(program2) {
|
|
|
1629
1504
|
await writeArtifact(opts.output, result, Boolean(globalOpts.json));
|
|
1630
1505
|
displayLpEvaluateResult(result, globalOpts);
|
|
1631
1506
|
});
|
|
1632
|
-
lp.command("run").description("Run scan, recommendation, and evaluation as one dry-run chain").option("--strategy-id <uuid>", "LP strategy id").option("--limit <n>", "Max candidates to scan/recommend (1-25, default 15)", parsePositiveInt2, 15).option("--no-sync-pnl", "Use existing PnL snapshots without refreshing").option("--output <file>", "Write the chained run JSON response to a local file").action(async (opts) => {
|
|
1633
|
-
const globalOpts = program2.opts();
|
|
1634
|
-
const client = new ApiClient(globalOpts);
|
|
1635
|
-
requireAuth6(client);
|
|
1636
|
-
const run = await client.post(
|
|
1637
|
-
"/api/lp/run",
|
|
1638
|
-
buildLpRunPayload(opts)
|
|
1639
|
-
);
|
|
1640
|
-
let evaluation = null;
|
|
1641
|
-
try {
|
|
1642
|
-
evaluation = await client.post("/api/lp/evaluate", {
|
|
1643
|
-
strategyId: run.strategyId,
|
|
1644
|
-
syncPnl: false
|
|
1645
|
-
});
|
|
1646
|
-
} catch (error2) {
|
|
1647
|
-
if (!globalOpts.json) {
|
|
1648
|
-
warn(
|
|
1649
|
-
`Could not load evaluation summary: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
1650
|
-
);
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
const combined = { run, evaluation };
|
|
1654
|
-
await writeArtifact(opts.output, combined, Boolean(globalOpts.json));
|
|
1655
|
-
displayLpRunResult(combined, globalOpts);
|
|
1656
|
-
});
|
|
1657
1507
|
}
|
|
1658
1508
|
|
|
1659
1509
|
// src/commands/wallet.ts
|
|
@@ -1664,7 +1514,7 @@ var POLYGON_NATIVE_USDC = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
|
|
|
1664
1514
|
var SUPPORTED_ASSETS = ["pUsd", "usdcE", "matic"];
|
|
1665
1515
|
var POLYGON_NATIVE_ASSET_LABEL = "POL";
|
|
1666
1516
|
var TERMINAL_WITHDRAWAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "expired"]);
|
|
1667
|
-
function
|
|
1517
|
+
function requireAuth6(client) {
|
|
1668
1518
|
if (!client.isAuthenticated) {
|
|
1669
1519
|
error("Not authenticated. Run `hl auth login` first.");
|
|
1670
1520
|
process.exit(1);
|
|
@@ -1849,7 +1699,7 @@ function registerWalletCommands(program2) {
|
|
|
1849
1699
|
wallet.command("status").description("Show linked owner and Polymarket deposit wallet status").action(async () => {
|
|
1850
1700
|
const globalOpts = program2.opts();
|
|
1851
1701
|
const client = new ApiClient(globalOpts);
|
|
1852
|
-
|
|
1702
|
+
requireAuth6(client);
|
|
1853
1703
|
const status = await client.get("/api/wallet/status");
|
|
1854
1704
|
if (globalOpts.json) {
|
|
1855
1705
|
json(status);
|
|
@@ -1860,7 +1710,7 @@ function registerWalletCommands(program2) {
|
|
|
1860
1710
|
wallet.command("balances").alias("funds").description("Show available wallet funds").action(async () => {
|
|
1861
1711
|
const globalOpts = program2.opts();
|
|
1862
1712
|
const client = new ApiClient(globalOpts);
|
|
1863
|
-
|
|
1713
|
+
requireAuth6(client);
|
|
1864
1714
|
const balances = await client.get("/api/wallet/balances");
|
|
1865
1715
|
if (globalOpts.json) {
|
|
1866
1716
|
json(balances);
|
|
@@ -1871,7 +1721,7 @@ function registerWalletCommands(program2) {
|
|
|
1871
1721
|
wallet.command("deposit").description("Show deposit address and supported Polygon assets").option("--asset <asset>", "pUSD | USDC.e | POL").option("--bridge", "Also request Polymarket Bridge deposit addresses").action(async (opts) => {
|
|
1872
1722
|
const globalOpts = program2.opts();
|
|
1873
1723
|
const client = new ApiClient(globalOpts);
|
|
1874
|
-
|
|
1724
|
+
requireAuth6(client);
|
|
1875
1725
|
const asset = normalizeWalletAsset(opts.asset);
|
|
1876
1726
|
const result = await client.post("/api/wallet/deposit", {
|
|
1877
1727
|
bridge: Boolean(opts.bridge)
|
|
@@ -1894,7 +1744,7 @@ function registerWalletCommands(program2) {
|
|
|
1894
1744
|
wallet.command("withdraw").description("Create a browser-signed withdrawal intent and poll for completion").requiredOption("--to <address>", "Recipient EVM wallet address", parseWalletAddress).requiredOption("--amount <amount>", "Amount to withdraw", parsePositiveNumber2).option("--asset <asset>", "Source asset; currently only pUSD is supported", "pUSD").option("--to-chain-id <id>", "Destination chain id", "137").option("--to-token-address <address>", "Destination token address", parseWalletAddress, POLYGON_NATIVE_USDC).option("--no-open", "Do not open the browser signing URL").option("--no-wait", "Do not poll for completion after creating the intent").option("--poll-interval <seconds>", "Polling interval in seconds", parsePositiveNumber2, 5).option("--timeout <seconds>", "Maximum seconds to wait for completion", parsePositiveNumber2, 600).action(async (opts) => {
|
|
1895
1745
|
const globalOpts = program2.opts();
|
|
1896
1746
|
const client = new ApiClient(globalOpts);
|
|
1897
|
-
|
|
1747
|
+
requireAuth6(client);
|
|
1898
1748
|
const created = await client.post(
|
|
1899
1749
|
"/api/wallet/withdraw/intents",
|
|
1900
1750
|
buildWithdrawRequestPayload(opts)
|
|
@@ -1924,7 +1774,7 @@ import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
|
|
|
1924
1774
|
import { readFile as readFile2 } from "fs/promises";
|
|
1925
1775
|
|
|
1926
1776
|
// src/signal-display.ts
|
|
1927
|
-
import
|
|
1777
|
+
import chalk7 from "chalk";
|
|
1928
1778
|
function pctFromSignalValue(value) {
|
|
1929
1779
|
if (value === null || value === void 0 || Number.isNaN(value)) return null;
|
|
1930
1780
|
return Math.abs(value) <= 1 ? value * 100 : value;
|
|
@@ -1937,14 +1787,14 @@ function formatGap(value) {
|
|
|
1937
1787
|
if (value === null || value === void 0 || Number.isNaN(value)) return "n/a";
|
|
1938
1788
|
const points = Math.abs(value) <= 1 ? value * 100 : value;
|
|
1939
1789
|
const formatted = `${points >= 0 ? "+" : ""}${points.toFixed(1)}pp`;
|
|
1940
|
-
if (points > 0) return
|
|
1941
|
-
if (points < 0) return
|
|
1942
|
-
return
|
|
1790
|
+
if (points > 0) return chalk7.green(formatted);
|
|
1791
|
+
if (points < 0) return chalk7.red(formatted);
|
|
1792
|
+
return chalk7.dim(formatted);
|
|
1943
1793
|
}
|
|
1944
1794
|
function formatStrength(value) {
|
|
1945
|
-
if (value === "strong") return
|
|
1946
|
-
if (value === "weak") return
|
|
1947
|
-
return value ?
|
|
1795
|
+
if (value === "strong") return chalk7.green("strong");
|
|
1796
|
+
if (value === "weak") return chalk7.yellow("weak");
|
|
1797
|
+
return value ? chalk7.dim(value) : "n/a";
|
|
1948
1798
|
}
|
|
1949
1799
|
function analysisItems(result) {
|
|
1950
1800
|
if (!result) return [];
|
|
@@ -1991,27 +1841,27 @@ function displaySignalAnalysis(response, globalOpts) {
|
|
|
1991
1841
|
for (const item of items.slice(0, 3)) {
|
|
1992
1842
|
const analysis = item.analysis;
|
|
1993
1843
|
if (!analysis) continue;
|
|
1994
|
-
process.stdout.write("\n " +
|
|
1844
|
+
process.stdout.write("\n " + chalk7.bold(truncate(titleFor(analysis), 76)) + "\n");
|
|
1995
1845
|
if (analysis.market_link) {
|
|
1996
|
-
process.stdout.write(" " +
|
|
1846
|
+
process.stdout.write(" " + chalk7.dim("Polymarket: ") + analysis.market_link + "\n");
|
|
1997
1847
|
}
|
|
1998
1848
|
if (analysis.key_factors && analysis.key_factors.length > 0) {
|
|
1999
|
-
process.stdout.write(" " +
|
|
1849
|
+
process.stdout.write(" " + chalk7.dim("Key factors: ") + analysis.key_factors.slice(0, 4).join("; ") + "\n");
|
|
2000
1850
|
}
|
|
2001
1851
|
if (analysis.research_findings) {
|
|
2002
1852
|
process.stdout.write(
|
|
2003
|
-
" " +
|
|
1853
|
+
" " + chalk7.dim("Research: ") + truncate(analysis.research_findings, 180) + "\n"
|
|
2004
1854
|
);
|
|
2005
1855
|
}
|
|
2006
1856
|
}
|
|
2007
1857
|
if (items.length > 3) {
|
|
2008
|
-
process.stdout.write(
|
|
1858
|
+
process.stdout.write(chalk7.dim(`
|
|
2009
1859
|
... and ${items.length - 3} more
|
|
2010
1860
|
`));
|
|
2011
1861
|
}
|
|
2012
1862
|
if (result?.strong_signal_count !== void 0) {
|
|
2013
1863
|
process.stdout.write(
|
|
2014
|
-
|
|
1864
|
+
chalk7.dim(`
|
|
2015
1865
|
Strong signals: ${result.strong_signal_count}
|
|
2016
1866
|
`)
|
|
2017
1867
|
);
|
|
@@ -2019,7 +1869,7 @@ function displaySignalAnalysis(response, globalOpts) {
|
|
|
2019
1869
|
}
|
|
2020
1870
|
|
|
2021
1871
|
// src/commands/signal.ts
|
|
2022
|
-
function
|
|
1872
|
+
function requireAuth7(client) {
|
|
2023
1873
|
if (!client.isAuthenticated) {
|
|
2024
1874
|
error("Not authenticated. Run `hl auth login` first.");
|
|
2025
1875
|
process.exit(1);
|
|
@@ -2042,7 +1892,7 @@ async function readStdin2() {
|
|
|
2042
1892
|
}
|
|
2043
1893
|
return Buffer.concat(chunks).toString("utf8");
|
|
2044
1894
|
}
|
|
2045
|
-
async function
|
|
1895
|
+
async function readMarketPayload2(path) {
|
|
2046
1896
|
if (!path) return {};
|
|
2047
1897
|
const raw = path === "-" ? await readStdin2() : await readFile2(path, "utf8");
|
|
2048
1898
|
const parsed = JSON.parse(raw);
|
|
@@ -2074,7 +1924,7 @@ async function buildSignalPayload(positionalUrl, opts) {
|
|
|
2074
1924
|
const urls = [positionalUrl, ...opts.url ?? []].filter(
|
|
2075
1925
|
(value) => Boolean(value)
|
|
2076
1926
|
);
|
|
2077
|
-
const filePayload = await
|
|
1927
|
+
const filePayload = await readMarketPayload2(opts.market);
|
|
2078
1928
|
const inlineMarket = inlineMarketFromOptions(opts);
|
|
2079
1929
|
const hasMarketInput = Boolean(filePayload.market || filePayload.markets || inlineMarket);
|
|
2080
1930
|
if (urls.length > 0 && hasMarketInput) {
|
|
@@ -2103,7 +1953,7 @@ function registerSignalCommands(program2) {
|
|
|
2103
1953
|
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) => {
|
|
2104
1954
|
const globalOpts = program2.opts();
|
|
2105
1955
|
const client = new ApiClient(globalOpts);
|
|
2106
|
-
|
|
1956
|
+
requireAuth7(client);
|
|
2107
1957
|
let payload;
|
|
2108
1958
|
try {
|
|
2109
1959
|
payload = await buildSignalPayload(url, o);
|
|
@@ -2120,8 +1970,8 @@ function registerSignalCommands(program2) {
|
|
|
2120
1970
|
}
|
|
2121
1971
|
|
|
2122
1972
|
// src/index.ts
|
|
2123
|
-
var program = new
|
|
2124
|
-
program.name("hl").description("Hedge Layer CLI \u2014 prediction market intelligence from the terminal").version("
|
|
1973
|
+
var program = new Command4();
|
|
1974
|
+
program.name("hl").description("Hedge Layer CLI \u2014 prediction market intelligence from the terminal").version("2.0.0").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");
|
|
2125
1975
|
registerAuthCommands(program);
|
|
2126
1976
|
registerBriefCommands(program);
|
|
2127
1977
|
registerProfileCommand(program);
|
|
@@ -2129,7 +1979,6 @@ registerResearchCommands(program);
|
|
|
2129
1979
|
registerFeedCommand(program);
|
|
2130
1980
|
registerWalletCommands(program);
|
|
2131
1981
|
registerLpCommands(program);
|
|
2132
|
-
registerAllocatorCommands(program);
|
|
2133
1982
|
registerSignalCommands(program);
|
|
2134
1983
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
2135
1984
|
const opts = program.opts();
|