pandora-cli-skills 1.1.29 → 1.1.31
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/SKILL.md +27 -1
- package/cli/lib/arbitrage_service.cjs +1 -12
- package/cli/lib/autopilot_service.cjs +1 -11
- package/cli/lib/cli_output_service.cjs +6 -0
- package/cli/lib/command_router.cjs +7 -1
- package/cli/lib/history_service.cjs +14 -26
- package/cli/lib/leaderboard_service.cjs +1 -6
- package/cli/lib/market_admin_service.cjs +45 -5
- package/cli/lib/mirror_command_service.cjs +149 -0
- package/cli/lib/mirror_econ_service.cjs +25 -41
- package/cli/lib/mirror_handlers/browse.cjs +46 -0
- package/cli/lib/mirror_handlers/close.cjs +31 -0
- package/cli/lib/mirror_handlers/deploy.cjs +53 -0
- package/cli/lib/mirror_handlers/go.cjs +276 -0
- package/cli/lib/mirror_handlers/hedge_calc.cjs +116 -0
- package/cli/lib/mirror_handlers/lp_explain.cjs +37 -0
- package/cli/lib/mirror_handlers/plan.cjs +46 -0
- package/cli/lib/mirror_handlers/simulate.cjs +37 -0
- package/cli/lib/mirror_handlers/status.cjs +165 -0
- package/cli/lib/mirror_handlers/sync.cjs +299 -0
- package/cli/lib/mirror_handlers/verify.cjs +73 -0
- package/cli/lib/mirror_service.cjs +1 -7
- package/cli/lib/mirror_sizing_service.cjs +1 -16
- package/cli/lib/mirror_sync_service.cjs +716 -386
- package/cli/lib/mirror_verify_service.cjs +11 -23
- package/cli/lib/pandora_deploy_service.cjs +6 -8
- package/cli/lib/parsers/autopilot_flags.cjs +257 -0
- package/cli/lib/parsers/mirror_deploy_flags.cjs +240 -0
- package/cli/lib/parsers/mirror_go_flags.cjs +342 -0
- package/cli/lib/parsers/mirror_hedge_calc_flags.cjs +187 -0
- package/cli/lib/parsers/mirror_plan_flags.cjs +151 -0
- package/cli/lib/parsers/mirror_sync_flags.cjs +445 -0
- package/cli/lib/parsers/trade_flags.cjs +180 -0
- package/cli/lib/parsers/watch_flags.cjs +212 -0
- package/cli/lib/polymarket_adapter.cjs +1 -6
- package/cli/lib/polymarket_trade_adapter.cjs +120 -35
- package/cli/lib/scan_command_service.cjs +11 -0
- package/cli/lib/shared/constants.cjs +23 -0
- package/cli/lib/shared/utils.cjs +63 -0
- package/cli/lib/similarity_service.cjs +1 -12
- package/cli/pandora.cjs +4341 -6823
- package/package.json +5 -2
- package/tests/smoke/consumer-json-smoke.cjs +217 -0
- package/tests/smoke/pack-install-smoke.cjs +302 -0
package/SKILL.md
CHANGED
|
@@ -110,7 +110,7 @@ lp-explain --liquidity-usdc <n> [--source-yes-pct <0-100>] [--distribution-yes <
|
|
|
110
110
|
hedge-calc [--reserve-yes-usdc <n> --reserve-no-usdc <n>] [--excess-yes-usdc <n>] [--excess-no-usdc <n>] [--polymarket-yes-pct <0-100>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--volume-scenarios <csv>] [--pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug>] [--trust-deploy] [--manifest-file <path>]
|
|
111
111
|
simulate --liquidity-usdc <n> [--source-yes-pct <0-100>] [--target-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>] [--fee-tier 500|3000|10000] [--volume-scenarios <csv>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--polymarket-yes-pct <0-100>]
|
|
112
112
|
go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--manifest-file <path>] [--trust-deploy] [--skip-gate] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>]
|
|
113
|
-
sync run|once|start
|
|
113
|
+
sync run|once|start --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--trust-deploy] [--manifest-file <path>] [--skip-gate] [--daemon] [--stream|--no-stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--depth-slippage-bps <n>] [--min-time-to-close-sec <n>] [--iterations <n>] [--state-file <path>] [--kill-switch-file <path>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--webhook-url <url>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>]
|
|
114
114
|
status --state-file <path>|--strategy-hash <hash> [--with-live] [--pandora-market-address <address>|--market-address <address>] [--polymarket-market-id <id>|--polymarket-slug <slug>]
|
|
115
115
|
close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute
|
|
116
116
|
```
|
|
@@ -257,6 +257,7 @@ pandora --output json suggest --wallet <0x...> --risk medium --budget 50 --inclu
|
|
|
257
257
|
- Polymarket trading collateral is Polygon USDC.e on the proxy wallet.
|
|
258
258
|
- rebalance sizing is pool-aware and bounded by `--max-rebalance-usdc`.
|
|
259
259
|
- endpoint resilience: Polymarket snapshots are cached under `~/.pandora/polymarket`; paper/read flows can reuse cache during outages, while live sync blocks cached sources.
|
|
260
|
+
- daemon verification note: `mirror sync start` launches a child process that executes `cli/pandora.cjs` and enters the same CLI `main()` handlers, so crash/error handling remains covered by the normal command dispatcher + structured error envelopes.
|
|
260
261
|
- `mirror status`: local mirror state inspection with optional live market diagnostics (`--with-live`).
|
|
261
262
|
- `--with-live` uses the same `POLYMARKET_*` env keys for optional position visibility (YES/NO balances, open orders count, estimated value) and adds `netDeltaApprox` / `pnlApprox`.
|
|
262
263
|
- missing credentials or unavailable position endpoints do not hard-fail status; diagnostics are returned with null position fields.
|
|
@@ -346,6 +347,25 @@ Error envelope:
|
|
|
346
347
|
## Additional JSON response shapes
|
|
347
348
|
- `doctor`:
|
|
348
349
|
- `{ ok: true, command: "doctor", data: { schemaVersion, generatedAt, env, rpc, codeChecks, polymarket, summary } }`
|
|
350
|
+
- `history`:
|
|
351
|
+
- `{ ok: true, command: "history", data: { schemaVersion, generatedAt, indexerUrl, wallet, chainId, filters, pagination, pageInfo, summary, count, items[] } }`
|
|
352
|
+
- each `items[]` row includes `entryPriceUsdcPerToken`, `markPriceUsdcPerToken`, `currentValueUsdc`, `pnlUnrealizedApproxUsdc`, `pnlRealizedApproxUsdc`, `status`, and `diagnostics[]`.
|
|
353
|
+
- `export`:
|
|
354
|
+
- `{ ok: true, command: "export", data: { schemaVersion, generatedAt, format: "csv"|"json", wallet, chainId, count, filters, columns[], outPath, rows[], content } }`
|
|
355
|
+
- `content` is the deterministic serialized payload written to `outPath` when `--out` is provided.
|
|
356
|
+
- `arbitrage`:
|
|
357
|
+
- `{ ok: true, command: "arbitrage", data: { schemaVersion, generatedAt, indexerUrl, venues, filters, count, opportunities[], diagnostics[] } }`
|
|
358
|
+
- `autopilot`:
|
|
359
|
+
- `{ ok: true, command: "autopilot", data: { schemaVersion, generatedAt, strategyHash, mode, executeLive, stateFile, killSwitchFile, iterationsRequested, iterationsCompleted, stoppedReason?, parameters: { marketAddress, side, amountUsdc, triggerYesBelow?, triggerYesAbove?, intervalMs, cooldownMs, maxAmountUsdc?, maxOpenExposureUsdc, dailySpendCapUsdc, maxTradesPerDay }, state, actionCount, actions[], snapshots[], webhookReports[] } }`
|
|
360
|
+
- `mirror browse`:
|
|
361
|
+
- `{ ok: true, command: "mirror.browse", data: { schemaVersion, generatedAt, source, gammaApiError, filters, count, items[], diagnostics[] } }`
|
|
362
|
+
- each candidate row can include `existingMirror: { marketAddress, similarity } | null`.
|
|
363
|
+
- `mirror go`:
|
|
364
|
+
- `{ ok: true, command: "mirror.go", data: { schemaVersion, generatedAt, mode, plan, deploy, verify, sync, polymarketPreflight, suggestedSyncCommand, trustManifest, diagnostics[] } }`
|
|
365
|
+
- `plan` is the same payload shape as `mirror.plan`; `deploy` is the same payload shape as `mirror.deploy`; `sync` is null unless `--auto-sync` is used.
|
|
366
|
+
- close peers:
|
|
367
|
+
- `mirror close`: `{ ok: true, command: "mirror.close", data: { schemaVersion, generatedAt, mode, pandoraMarketAddress, polymarketMarketId?, polymarketSlug?, steps[], diagnostics[] } }`
|
|
368
|
+
- `mirror sync start|status|stop`: `{ ok: true, command: "mirror.sync.start|mirror.sync.status|mirror.sync.stop", data: { strategyHash, pid?, pidFile, logFile?, alive, status, metadata? } }`
|
|
349
369
|
- `resolve`:
|
|
350
370
|
- dry-run: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "dry-run", txPlan } }`
|
|
351
371
|
- execute: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "execute", tx } }`
|
|
@@ -354,6 +374,12 @@ Error envelope:
|
|
|
354
374
|
- `lp positions`: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "positions", mode: "read", wallet, count, items[] } }`
|
|
355
375
|
- `polymarket`:
|
|
356
376
|
- `check|preflight|approve|trade` all return `{ ok: true, command, data }` with `schemaVersion` and `generatedAt`; execute paths include `result`/`tx` blocks.
|
|
377
|
+
- `leaderboard`:
|
|
378
|
+
- `{ ok: true, command: "leaderboard", data: { schemaVersion, generatedAt, indexerUrl, metric, limit, minTrades, count, items[], diagnostics[] } }`
|
|
379
|
+
- `analyze`:
|
|
380
|
+
- `{ ok: true, command: "analyze", data: { schemaVersion, generatedAt, indexerUrl, marketAddress, provider, model, market, quote, result } }`
|
|
381
|
+
- `suggest`:
|
|
382
|
+
- `{ ok: true, command: "suggest", data: { schemaVersion, generatedAt, wallet, risk, budget, count, items[], indexerUrl, includeVenues, historySummary, arbitrageCount } }`
|
|
357
383
|
|
|
358
384
|
## Pandora mainnet deployment reference
|
|
359
385
|
- PredictionOracle (Factory): `0x259308E7d8557e4Ba192De1aB8Cf7e0E21896442`
|
|
@@ -5,21 +5,10 @@ const {
|
|
|
5
5
|
questionSimilarityBreakdown,
|
|
6
6
|
questionSimilarity,
|
|
7
7
|
} = require('./similarity_service.cjs');
|
|
8
|
+
const { toNumber, round } = require('./shared/utils.cjs');
|
|
8
9
|
|
|
9
10
|
const ARBITRAGE_SCHEMA_VERSION = '1.1.0';
|
|
10
11
|
|
|
11
|
-
function toNumber(value) {
|
|
12
|
-
const numeric = Number(value);
|
|
13
|
-
if (!Number.isFinite(numeric)) return null;
|
|
14
|
-
return numeric;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function round(value, decimals = 6) {
|
|
18
|
-
if (!Number.isFinite(value)) return null;
|
|
19
|
-
const factor = 10 ** decimals;
|
|
20
|
-
return Math.round(value * factor) / factor;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
12
|
function toUsdc(raw) {
|
|
24
13
|
const numeric = toNumber(raw);
|
|
25
14
|
if (numeric === null) return null;
|
|
@@ -9,17 +9,7 @@ const {
|
|
|
9
9
|
pruneIdempotencyKeys,
|
|
10
10
|
resetDailyCountersIfNeeded,
|
|
11
11
|
} = require('./autopilot_state_store.cjs');
|
|
12
|
-
|
|
13
|
-
function toNumber(value) {
|
|
14
|
-
const numeric = Number(value);
|
|
15
|
-
if (!Number.isFinite(numeric)) return null;
|
|
16
|
-
return numeric;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function sleepMs(ms) {
|
|
20
|
-
if (!Number.isFinite(ms) || ms <= 0) return Promise.resolve();
|
|
21
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
|
-
}
|
|
12
|
+
const { toNumber, sleepMs } = require('./shared/utils.cjs');
|
|
23
13
|
|
|
24
14
|
function buildIdempotencyKey(options, quote, nowMs) {
|
|
25
15
|
const yesPct = toNumber(quote && quote.odds && quote.odds.yesPct);
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create CLI output helpers for success/error envelopes and table output.
|
|
3
|
+
* @param {{defaultSchemaVersion?: string, CliError: Function}} [options]
|
|
4
|
+
* @returns {{emitFailure: (outputMode: 'table'|'json', error: any) => void, emitSuccess: (outputMode: 'table'|'json', command: string, data: any, tableRenderer?: Function) => void}}
|
|
5
|
+
*/
|
|
1
6
|
function createCliOutputService(options = {}) {
|
|
2
7
|
const defaultSchemaVersion =
|
|
3
8
|
typeof options.defaultSchemaVersion === 'string' && options.defaultSchemaVersion.trim()
|
|
@@ -120,6 +125,7 @@ function createCliOutputService(options = {}) {
|
|
|
120
125
|
};
|
|
121
126
|
}
|
|
122
127
|
|
|
128
|
+
/** Public CLI output service factory export. */
|
|
123
129
|
module.exports = {
|
|
124
130
|
createCliOutputService,
|
|
125
131
|
};
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the top-level command dispatcher for `pandora`.
|
|
3
|
+
* The returned function routes parsed command/args/context to subcommand handlers.
|
|
4
|
+
* @param {object} deps
|
|
5
|
+
* @returns {(command: string|undefined, args: string[], context: {outputMode: 'table'|'json'}) => Promise<void>}
|
|
6
|
+
*/
|
|
1
7
|
function createCommandRouter(deps = {}) {
|
|
2
8
|
const {
|
|
3
9
|
CliError,
|
|
@@ -156,7 +162,7 @@ function createCommandRouter(deps = {}) {
|
|
|
156
162
|
};
|
|
157
163
|
}
|
|
158
164
|
|
|
165
|
+
/** Public command-router factory export. */
|
|
159
166
|
module.exports = {
|
|
160
167
|
createCommandRouter,
|
|
161
168
|
};
|
|
162
|
-
|
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
const { createIndexerClient } = require('./indexer_client.cjs');
|
|
2
|
+
const { round, toOptionalNumber } = require('./shared/utils.cjs');
|
|
2
3
|
|
|
3
4
|
const USDC_DECIMALS = 6;
|
|
4
5
|
const HISTORY_SCHEMA_VERSION = '1.0.0';
|
|
5
6
|
|
|
6
|
-
function toNumber(value) {
|
|
7
|
-
if (value === null || value === undefined || value === '') return null;
|
|
8
|
-
const numeric = Number(value);
|
|
9
|
-
if (!Number.isFinite(numeric)) return null;
|
|
10
|
-
return numeric;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function round(value, decimals = 6) {
|
|
14
|
-
if (!Number.isFinite(value)) return null;
|
|
15
|
-
const factor = 10 ** decimals;
|
|
16
|
-
return Math.round(value * factor) / factor;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
function toUsdc(raw) {
|
|
20
|
-
const numeric =
|
|
8
|
+
const numeric = toOptionalNumber(raw);
|
|
21
9
|
if (numeric === null) return null;
|
|
22
10
|
return round(numeric / 10 ** USDC_DECIMALS, 6);
|
|
23
11
|
}
|
|
24
12
|
|
|
25
13
|
function normalizeProbabilityFromYesChance(rawYesChance) {
|
|
26
|
-
const raw =
|
|
14
|
+
const raw = toOptionalNumber(rawYesChance);
|
|
27
15
|
if (raw === null) return null;
|
|
28
16
|
if (raw >= 0 && raw <= 1) return raw;
|
|
29
17
|
if (raw > 1 && raw <= 100) return raw / 100;
|
|
@@ -34,8 +22,8 @@ function computeYesProbabilityFromMarket(market) {
|
|
|
34
22
|
const yesChanceProb = normalizeProbabilityFromYesChance(market && market.yesChance);
|
|
35
23
|
if (yesChanceProb !== null && yesChanceProb >= 0 && yesChanceProb <= 1) return yesChanceProb;
|
|
36
24
|
|
|
37
|
-
const reserveYes =
|
|
38
|
-
const reserveNo =
|
|
25
|
+
const reserveYes = toOptionalNumber(market && market.reserveYes);
|
|
26
|
+
const reserveNo = toOptionalNumber(market && market.reserveNo);
|
|
39
27
|
if (reserveYes === null || reserveNo === null) return null;
|
|
40
28
|
const total = reserveYes + reserveNo;
|
|
41
29
|
if (!Number.isFinite(total) || total <= 0) return null;
|
|
@@ -117,21 +105,21 @@ function buildSummary(items) {
|
|
|
117
105
|
};
|
|
118
106
|
|
|
119
107
|
for (const item of items) {
|
|
120
|
-
const collateral =
|
|
108
|
+
const collateral = toOptionalNumber(item.collateralAmountUsdc) || 0;
|
|
121
109
|
summary.grossVolumeUsdc += collateral;
|
|
122
110
|
|
|
123
111
|
if (item.status === 'open') {
|
|
124
112
|
summary.openCount += 1;
|
|
125
|
-
summary.unrealizedPnlApproxUsdc +=
|
|
113
|
+
summary.unrealizedPnlApproxUsdc += toOptionalNumber(item.pnlUnrealizedApproxUsdc) || 0;
|
|
126
114
|
} else if (item.status === 'won') {
|
|
127
115
|
summary.wonCount += 1;
|
|
128
|
-
summary.realizedPnlApproxUsdc +=
|
|
116
|
+
summary.realizedPnlApproxUsdc += toOptionalNumber(item.pnlRealizedApproxUsdc) || 0;
|
|
129
117
|
} else if (item.status === 'lost') {
|
|
130
118
|
summary.lostCount += 1;
|
|
131
|
-
summary.realizedPnlApproxUsdc +=
|
|
119
|
+
summary.realizedPnlApproxUsdc += toOptionalNumber(item.pnlRealizedApproxUsdc) || 0;
|
|
132
120
|
} else {
|
|
133
121
|
summary.closedOtherCount += 1;
|
|
134
|
-
summary.realizedPnlApproxUsdc +=
|
|
122
|
+
summary.realizedPnlApproxUsdc += toOptionalNumber(item.pnlRealizedApproxUsdc) || 0;
|
|
135
123
|
}
|
|
136
124
|
}
|
|
137
125
|
|
|
@@ -146,15 +134,15 @@ function sortHistoryItems(items, orderBy, orderDirection) {
|
|
|
146
134
|
const dir = String(orderDirection || 'desc').toLowerCase() === 'asc' ? 1 : -1;
|
|
147
135
|
const pick = (item) => {
|
|
148
136
|
if (orderBy === 'pnl') {
|
|
149
|
-
return
|
|
137
|
+
return toOptionalNumber(item.pnlRealizedApproxUsdc) ?? toOptionalNumber(item.pnlUnrealizedApproxUsdc) ?? -Infinity;
|
|
150
138
|
}
|
|
151
139
|
if (orderBy === 'entry-price') {
|
|
152
|
-
return
|
|
140
|
+
return toOptionalNumber(item.entryPriceUsdcPerToken) ?? -Infinity;
|
|
153
141
|
}
|
|
154
142
|
if (orderBy === 'mark-price') {
|
|
155
|
-
return
|
|
143
|
+
return toOptionalNumber(item.markPriceUsdcPerToken) ?? -Infinity;
|
|
156
144
|
}
|
|
157
|
-
return
|
|
145
|
+
return toOptionalNumber(item.timestamp) ?? 0;
|
|
158
146
|
};
|
|
159
147
|
|
|
160
148
|
items.sort((a, b) => {
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
const { createIndexerClient } = require('./indexer_client.cjs');
|
|
2
|
+
const { toNumber } = require('./shared/utils.cjs');
|
|
2
3
|
|
|
3
4
|
const LEADERBOARD_SCHEMA_VERSION = '1.0.0';
|
|
4
5
|
|
|
5
|
-
function toNumber(value) {
|
|
6
|
-
const numeric = Number(value);
|
|
7
|
-
if (!Number.isFinite(numeric)) return null;
|
|
8
|
-
return numeric;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
6
|
function toCount(value) {
|
|
12
7
|
const numeric = toNumber(value);
|
|
13
8
|
if (numeric === null || numeric < 0) return 0;
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
const DEFAULT_INDEXER_URL = '
|
|
2
|
-
const DEFAULT_RPC_BY_CHAIN_ID = {
|
|
3
|
-
1: 'https://ethereum.publicnode.com',
|
|
4
|
-
146: 'https://rpc.soniclabs.com',
|
|
5
|
-
};
|
|
1
|
+
const { DEFAULT_INDEXER_URL, DEFAULT_RPC_BY_CHAIN_ID } = require('./shared/constants.cjs');
|
|
6
2
|
|
|
7
3
|
const ERC20_ABI = [
|
|
8
4
|
{
|
|
@@ -180,6 +176,12 @@ function normalizeTimeoutMs(value) {
|
|
|
180
176
|
return Math.trunc(numeric);
|
|
181
177
|
}
|
|
182
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Convert numeric-like input into a finite number.
|
|
181
|
+
* Used by LP/resolve input normalization paths.
|
|
182
|
+
* @param {*} value
|
|
183
|
+
* @returns {number|null}
|
|
184
|
+
*/
|
|
183
185
|
function toFiniteNumber(value) {
|
|
184
186
|
const numeric = Number(value);
|
|
185
187
|
if (!Number.isFinite(numeric)) return null;
|
|
@@ -313,6 +315,14 @@ async function readDecimals(publicClient, address, fallback = 18) {
|
|
|
313
315
|
}
|
|
314
316
|
}
|
|
315
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Read optional `calcRemoveLiquidity` preview outputs.
|
|
320
|
+
* Returned values are raw on-chain integers as decimal strings.
|
|
321
|
+
* @param {object} publicClient
|
|
322
|
+
* @param {string} marketAddress
|
|
323
|
+
* @param {bigint} sharesRaw
|
|
324
|
+
* @returns {Promise<{collateralOutRaw: string|null, yesOutRaw: string|null, noOutRaw: string|null}|null>}
|
|
325
|
+
*/
|
|
316
326
|
async function readCalcRemoveLiquidity(publicClient, marketAddress, sharesRaw) {
|
|
317
327
|
for (const abi of CALC_REMOVE_LIQUIDITY_ABI_CANDIDATES) {
|
|
318
328
|
try {
|
|
@@ -395,6 +405,11 @@ async function discoverLiquidityMarkets(indexerUrl, wallet, chainId, timeoutMs)
|
|
|
395
405
|
return Array.from(new Set(addresses));
|
|
396
406
|
}
|
|
397
407
|
|
|
408
|
+
/**
|
|
409
|
+
* Resolve a market outcome by calling `resolveMarket(bool)`.
|
|
410
|
+
* @param {object} [options]
|
|
411
|
+
* @returns {Promise<object>}
|
|
412
|
+
*/
|
|
398
413
|
async function runResolve(options = {}) {
|
|
399
414
|
const schemaVersion = '1.0.0';
|
|
400
415
|
const generatedAt = new Date().toISOString();
|
|
@@ -468,6 +483,13 @@ async function runResolve(options = {}) {
|
|
|
468
483
|
}
|
|
469
484
|
}
|
|
470
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Read LP balances and remove-liquidity previews for a wallet.
|
|
488
|
+
* LP/share values are raw and human-readable decimal strings in the payload.
|
|
489
|
+
* `collateralOutUsdc` is decimal USDC (6 decimals).
|
|
490
|
+
* @param {object} [options]
|
|
491
|
+
* @returns {Promise<object>}
|
|
492
|
+
*/
|
|
471
493
|
async function runLpPositions(options = {}) {
|
|
472
494
|
const schemaVersion = '1.0.0';
|
|
473
495
|
const generatedAt = new Date().toISOString();
|
|
@@ -570,6 +592,12 @@ async function runLpPositions(options = {}) {
|
|
|
570
592
|
};
|
|
571
593
|
}
|
|
572
594
|
|
|
595
|
+
/**
|
|
596
|
+
* Build or execute `addLiquidity`.
|
|
597
|
+
* `amountUsdc` is decimal USDC input; `collateralAmountRaw` is 6-decimal raw units.
|
|
598
|
+
* @param {object} [options]
|
|
599
|
+
* @returns {Promise<object>}
|
|
600
|
+
*/
|
|
573
601
|
async function runLpAdd(options = {}) {
|
|
574
602
|
const schemaVersion = '1.0.0';
|
|
575
603
|
const generatedAt = new Date().toISOString();
|
|
@@ -719,6 +747,12 @@ async function runLpAdd(options = {}) {
|
|
|
719
747
|
}
|
|
720
748
|
}
|
|
721
749
|
|
|
750
|
+
/**
|
|
751
|
+
* Build or execute `removeLiquidity`.
|
|
752
|
+
* `lpTokens` is decimal LP token amount; `sharesToBurnRaw` is on-chain raw units.
|
|
753
|
+
* @param {object} [options]
|
|
754
|
+
* @returns {Promise<object>}
|
|
755
|
+
*/
|
|
722
756
|
async function runLpRemove(options = {}) {
|
|
723
757
|
const schemaVersion = '1.0.0';
|
|
724
758
|
const generatedAt = new Date().toISOString();
|
|
@@ -825,6 +859,11 @@ async function runLpRemove(options = {}) {
|
|
|
825
859
|
}
|
|
826
860
|
}
|
|
827
861
|
|
|
862
|
+
/**
|
|
863
|
+
* Dispatch LP admin action (`positions`, `add`, `remove`).
|
|
864
|
+
* @param {object} [options]
|
|
865
|
+
* @returns {Promise<object>}
|
|
866
|
+
*/
|
|
828
867
|
async function runLp(options = {}) {
|
|
829
868
|
if (options.action === 'positions') {
|
|
830
869
|
return runLpPositions(options);
|
|
@@ -838,6 +877,7 @@ async function runLp(options = {}) {
|
|
|
838
877
|
throw createServiceError('INVALID_ARGS', 'lp requires action add|remove|positions.');
|
|
839
878
|
}
|
|
840
879
|
|
|
880
|
+
/** Public market admin API consumed by CLI `resolve` and `lp` commands. */
|
|
841
881
|
module.exports = {
|
|
842
882
|
runResolve,
|
|
843
883
|
runLp,
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical usage string for `mirror go`.
|
|
3
|
+
* Exported for CLI help rendering and integration tests.
|
|
4
|
+
* @type {string}
|
|
5
|
+
*/
|
|
6
|
+
const MIRROR_GO_USAGE =
|
|
7
|
+
'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--manifest-file <path>] [--trust-deploy] [--skip-gate] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>]';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Canonical usage string for `mirror sync`.
|
|
11
|
+
* Exported for CLI help rendering and integration tests.
|
|
12
|
+
* @type {string}
|
|
13
|
+
*/
|
|
14
|
+
const MIRROR_SYNC_USAGE =
|
|
15
|
+
'pandora [--output table|json] mirror sync run|once|start --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--trust-deploy] [--manifest-file <path>] [--skip-gate] [--daemon] [--stream|--no-stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--depth-slippage-bps <n>] [--min-time-to-close-sec <n>] [--iterations <n>] [--state-file <path>] [--kill-switch-file <path>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--webhook-url <url>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>]';
|
|
16
|
+
|
|
17
|
+
const INVALID_SUBCOMMAND_MESSAGE =
|
|
18
|
+
'mirror requires subcommand: browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build the `mirror` subcommand dispatcher with lazy-loaded action handlers.
|
|
22
|
+
* @param {object} deps
|
|
23
|
+
* @returns {(args: string[], context: {outputMode: 'table'|'json'}) => Promise<any>}
|
|
24
|
+
*/
|
|
25
|
+
function createRunMirrorCommand(deps) {
|
|
26
|
+
const {
|
|
27
|
+
CliError,
|
|
28
|
+
emitSuccess,
|
|
29
|
+
commandHelpPayload,
|
|
30
|
+
parseIndexerSharedFlags,
|
|
31
|
+
} = deps;
|
|
32
|
+
|
|
33
|
+
const handlerLoaders = {
|
|
34
|
+
browse: () => require('./mirror_handlers/browse.cjs'),
|
|
35
|
+
plan: () => require('./mirror_handlers/plan.cjs'),
|
|
36
|
+
deploy: () => require('./mirror_handlers/deploy.cjs'),
|
|
37
|
+
verify: () => require('./mirror_handlers/verify.cjs'),
|
|
38
|
+
'lp-explain': () => require('./mirror_handlers/lp_explain.cjs'),
|
|
39
|
+
'hedge-calc': () => require('./mirror_handlers/hedge_calc.cjs'),
|
|
40
|
+
simulate: () => require('./mirror_handlers/simulate.cjs'),
|
|
41
|
+
go: () => require('./mirror_handlers/go.cjs'),
|
|
42
|
+
sync: () => require('./mirror_handlers/sync.cjs'),
|
|
43
|
+
status: () => require('./mirror_handlers/status.cjs'),
|
|
44
|
+
close: () => require('./mirror_handlers/close.cjs'),
|
|
45
|
+
};
|
|
46
|
+
const handlerCache = new Map();
|
|
47
|
+
|
|
48
|
+
function getHandler(action) {
|
|
49
|
+
if (handlerCache.has(action)) {
|
|
50
|
+
return handlerCache.get(action);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const load = handlerLoaders[action];
|
|
54
|
+
if (!load) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const loaded = load();
|
|
59
|
+
const handler =
|
|
60
|
+
typeof loaded === 'function'
|
|
61
|
+
? loaded
|
|
62
|
+
: loaded && typeof loaded.handle === 'function'
|
|
63
|
+
? loaded.handle
|
|
64
|
+
: null;
|
|
65
|
+
|
|
66
|
+
if (!handler) {
|
|
67
|
+
throw new Error(`Invalid mirror handler module for action: ${action}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
handlerCache.set(action, handler);
|
|
71
|
+
return handler;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return async function runMirrorCommand(args, context) {
|
|
75
|
+
const action = args[0];
|
|
76
|
+
const actionArgs = args.slice(1);
|
|
77
|
+
|
|
78
|
+
if (!action || action === '--help' || action === '-h') {
|
|
79
|
+
if (context.outputMode === 'json') {
|
|
80
|
+
emitSuccess(
|
|
81
|
+
context.outputMode,
|
|
82
|
+
'mirror.help',
|
|
83
|
+
commandHelpPayload(
|
|
84
|
+
'pandora [--output table|json] mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close ...',
|
|
85
|
+
),
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
console.log(
|
|
89
|
+
'Usage: pandora [--output table|json] mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close ...',
|
|
90
|
+
);
|
|
91
|
+
console.log('');
|
|
92
|
+
console.log('Subcommands:');
|
|
93
|
+
console.log(
|
|
94
|
+
' browse --min-yes-pct <n> --max-yes-pct <n> --min-volume-24h <n> [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>] [--chain-id <id>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
95
|
+
);
|
|
96
|
+
console.log(
|
|
97
|
+
' plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
98
|
+
);
|
|
99
|
+
console.log(
|
|
100
|
+
' deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--oracle <address>] [--factory <address>] [--usdc <address>] [--distribution-yes <parts>] [--distribution-no <parts>] [--sources <url...>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
|
|
101
|
+
);
|
|
102
|
+
console.log(
|
|
103
|
+
' verify --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--trust-deploy] [--manifest-file <path>] [--include-similarity] [--with-rules] [--allow-rule-mismatch] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
104
|
+
);
|
|
105
|
+
console.log(
|
|
106
|
+
' lp-explain --liquidity-usdc <n> [--source-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>]',
|
|
107
|
+
);
|
|
108
|
+
console.log(
|
|
109
|
+
' hedge-calc [--reserve-yes-usdc <n> --reserve-no-usdc <n>] [--excess-yes-usdc <n>] [--excess-no-usdc <n>] [--polymarket-yes-pct <0-100>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--volume-scenarios <csv>] [--pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug>]',
|
|
110
|
+
);
|
|
111
|
+
console.log(
|
|
112
|
+
' simulate --liquidity-usdc <n> [--source-yes-pct <0-100>] [--target-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>] [--fee-tier 500|3000|10000] [--volume-scenarios <csv>] [--hedge-ratio <n>] [--polymarket-yes-pct <0-100>]',
|
|
113
|
+
);
|
|
114
|
+
console.log(
|
|
115
|
+
` go ${MIRROR_GO_USAGE.replace('pandora [--output table|json] mirror go ', '')}`,
|
|
116
|
+
);
|
|
117
|
+
console.log(
|
|
118
|
+
` sync ${MIRROR_SYNC_USAGE.replace('pandora [--output table|json] mirror sync ', '')}`,
|
|
119
|
+
);
|
|
120
|
+
console.log(' stop|status selector: --pid-file <path>|--strategy-hash <hash>');
|
|
121
|
+
console.log(' status --state-file <path>|--strategy-hash <hash> [--with-live] [--trust-deploy]');
|
|
122
|
+
console.log(' close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute');
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const handler = getHandler(action);
|
|
128
|
+
if (!handler) {
|
|
129
|
+
throw new CliError('INVALID_ARGS', INVALID_SUBCOMMAND_MESSAGE);
|
|
130
|
+
}
|
|
131
|
+
const shared = parseIndexerSharedFlags(actionArgs);
|
|
132
|
+
|
|
133
|
+
return handler({
|
|
134
|
+
actionArgs: shared.rest,
|
|
135
|
+
shared,
|
|
136
|
+
context,
|
|
137
|
+
deps,
|
|
138
|
+
mirrorGoUsage: MIRROR_GO_USAGE,
|
|
139
|
+
mirrorSyncUsage: MIRROR_SYNC_USAGE,
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Public mirror command service exports. */
|
|
145
|
+
module.exports = {
|
|
146
|
+
MIRROR_GO_USAGE,
|
|
147
|
+
MIRROR_SYNC_USAGE,
|
|
148
|
+
createRunMirrorCommand,
|
|
149
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { computeDistributionHint, normalizeProbability } = require('./mirror_sizing_service.cjs');
|
|
2
|
+
const { round, clamp, toOptionalNumber } = require('./shared/utils.cjs');
|
|
2
3
|
|
|
3
4
|
const MIRROR_LP_EXPLAIN_SCHEMA_VERSION = '1.0.0';
|
|
4
5
|
const MIRROR_HEDGE_CALC_SCHEMA_VERSION = '1.0.0';
|
|
@@ -6,25 +7,8 @@ const MIRROR_SIMULATE_SCHEMA_VERSION = '1.0.0';
|
|
|
6
7
|
const USDC_DECIMALS = 6;
|
|
7
8
|
const USDC_SCALE = 10 ** USDC_DECIMALS;
|
|
8
9
|
|
|
9
|
-
function toNumber(value) {
|
|
10
|
-
if (value === null || value === undefined || value === '') return null;
|
|
11
|
-
const numeric = Number(value);
|
|
12
|
-
if (!Number.isFinite(numeric)) return null;
|
|
13
|
-
return numeric;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function round(value, decimals = 6) {
|
|
17
|
-
if (!Number.isFinite(value)) return null;
|
|
18
|
-
const factor = 10 ** decimals;
|
|
19
|
-
return Math.round(value * factor) / factor;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function clamp(value, min, max) {
|
|
23
|
-
return Math.max(min, Math.min(max, value));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
10
|
function toRawUsdc(usdc) {
|
|
27
|
-
const numeric =
|
|
11
|
+
const numeric = toOptionalNumber(usdc);
|
|
28
12
|
if (numeric === null) return null;
|
|
29
13
|
return BigInt(Math.round(numeric * USDC_SCALE));
|
|
30
14
|
}
|
|
@@ -35,7 +19,7 @@ function rawUsdcToNumber(rawValue) {
|
|
|
35
19
|
}
|
|
36
20
|
|
|
37
21
|
function toPercent(value) {
|
|
38
|
-
const numeric =
|
|
22
|
+
const numeric = toOptionalNumber(value);
|
|
39
23
|
if (numeric === null) return null;
|
|
40
24
|
if (numeric >= 0 && numeric <= 100) return round(numeric, 6);
|
|
41
25
|
return null;
|
|
@@ -51,8 +35,8 @@ function resolveDistribution(options = {}) {
|
|
|
51
35
|
const diagnostics = [];
|
|
52
36
|
const sourceYesPct = toPercent(options.sourceYesPct);
|
|
53
37
|
|
|
54
|
-
const explicitYes =
|
|
55
|
-
const explicitNo =
|
|
38
|
+
const explicitYes = toOptionalNumber(options.distributionYes);
|
|
39
|
+
const explicitNo = toOptionalNumber(options.distributionNo);
|
|
56
40
|
if (explicitYes !== null || explicitNo !== null) {
|
|
57
41
|
if (explicitYes === null || explicitNo === null) {
|
|
58
42
|
throw new Error('distributionYes and distributionNo must both be provided together.');
|
|
@@ -94,8 +78,8 @@ function resolveDistribution(options = {}) {
|
|
|
94
78
|
}
|
|
95
79
|
|
|
96
80
|
function deriveYesPctFromReserves(reserveYesUsdc, reserveNoUsdc) {
|
|
97
|
-
const yesReserve =
|
|
98
|
-
const noReserve =
|
|
81
|
+
const yesReserve = toOptionalNumber(reserveYesUsdc);
|
|
82
|
+
const noReserve = toOptionalNumber(reserveNoUsdc);
|
|
99
83
|
if (yesReserve === null || noReserve === null) return null;
|
|
100
84
|
const total = yesReserve + noReserve;
|
|
101
85
|
if (!Number.isFinite(total) || total <= 0) return null;
|
|
@@ -108,8 +92,8 @@ function computeCompleteSetAllocation(options = {}) {
|
|
|
108
92
|
throw new Error('liquidityUsdc must be a positive number.');
|
|
109
93
|
}
|
|
110
94
|
|
|
111
|
-
const distributionYes =
|
|
112
|
-
const distributionNo =
|
|
95
|
+
const distributionYes = toOptionalNumber(options.distributionYes);
|
|
96
|
+
const distributionNo = toOptionalNumber(options.distributionNo);
|
|
113
97
|
if (!Number.isInteger(distributionYes) || !Number.isInteger(distributionNo)) {
|
|
114
98
|
throw new Error('distributionYes and distributionNo must be integers.');
|
|
115
99
|
}
|
|
@@ -163,9 +147,9 @@ function computeCompleteSetAllocation(options = {}) {
|
|
|
163
147
|
}
|
|
164
148
|
|
|
165
149
|
function simulateDirectionalSwap(options = {}) {
|
|
166
|
-
const reserveYes = Math.max(0,
|
|
167
|
-
const reserveNo = Math.max(0,
|
|
168
|
-
const volumeUsdc = Math.max(0,
|
|
150
|
+
const reserveYes = Math.max(0, toOptionalNumber(options.reserveYesUsdc) || 0);
|
|
151
|
+
const reserveNo = Math.max(0, toOptionalNumber(options.reserveNoUsdc) || 0);
|
|
152
|
+
const volumeUsdc = Math.max(0, toOptionalNumber(options.volumeUsdc) || 0);
|
|
169
153
|
const feeTier = Number.isFinite(Number(options.feeTier)) ? Number(options.feeTier) : 3000;
|
|
170
154
|
const feeRate = clamp(feeTier / 1_000_000, 0, 0.1);
|
|
171
155
|
const side = String(options.side || 'none').toLowerCase();
|
|
@@ -224,8 +208,8 @@ function simulateDirectionalSwap(options = {}) {
|
|
|
224
208
|
|
|
225
209
|
function solveVolumeForTargetYesPct(options = {}) {
|
|
226
210
|
const targetYesPct = toPercent(options.targetYesPct);
|
|
227
|
-
const reserveYesUsdc =
|
|
228
|
-
const reserveNoUsdc =
|
|
211
|
+
const reserveYesUsdc = toOptionalNumber(options.reserveYesUsdc);
|
|
212
|
+
const reserveNoUsdc = toOptionalNumber(options.reserveNoUsdc);
|
|
229
213
|
const feeTier = Number.isFinite(Number(options.feeTier)) ? Number(options.feeTier) : 3000;
|
|
230
214
|
|
|
231
215
|
if (targetYesPct === null || reserveYesUsdc === null || reserveNoUsdc === null) {
|
|
@@ -276,7 +260,7 @@ function solveVolumeForTargetYesPct(options = {}) {
|
|
|
276
260
|
|
|
277
261
|
function normalizeVolumeScenarios(value, liquidityUsdc) {
|
|
278
262
|
const fallback = () => {
|
|
279
|
-
const liq = Math.max(1,
|
|
263
|
+
const liq = Math.max(1, toOptionalNumber(liquidityUsdc) || 0);
|
|
280
264
|
return [
|
|
281
265
|
round(liq * 0.25, 6),
|
|
282
266
|
round(liq * 0.5, 6),
|
|
@@ -286,7 +270,7 @@ function normalizeVolumeScenarios(value, liquidityUsdc) {
|
|
|
286
270
|
};
|
|
287
271
|
|
|
288
272
|
if (Array.isArray(value)) {
|
|
289
|
-
const parsed = value.map((item) =>
|
|
273
|
+
const parsed = value.map((item) => toOptionalNumber(item)).filter((item) => Number.isFinite(item) && item >= 0).map((item) => round(item, 6));
|
|
290
274
|
return parsed.length ? parsed : fallback();
|
|
291
275
|
}
|
|
292
276
|
|
|
@@ -294,7 +278,7 @@ function normalizeVolumeScenarios(value, liquidityUsdc) {
|
|
|
294
278
|
if (raw) {
|
|
295
279
|
const parsed = raw
|
|
296
280
|
.split(',')
|
|
297
|
-
.map((entry) =>
|
|
281
|
+
.map((entry) => toOptionalNumber(entry.trim()))
|
|
298
282
|
.filter((entry) => Number.isFinite(entry) && entry >= 0)
|
|
299
283
|
.map((entry) => round(entry, 6));
|
|
300
284
|
return parsed.length ? parsed : fallback();
|
|
@@ -304,13 +288,13 @@ function normalizeVolumeScenarios(value, liquidityUsdc) {
|
|
|
304
288
|
}
|
|
305
289
|
|
|
306
290
|
function computeHedgeMetrics(options = {}) {
|
|
307
|
-
const reserveYesUsdc =
|
|
308
|
-
const reserveNoUsdc =
|
|
309
|
-
const excessYesUsdc =
|
|
310
|
-
const excessNoUsdc =
|
|
311
|
-
const hedgeRatio = clamp(
|
|
291
|
+
const reserveYesUsdc = toOptionalNumber(options.reserveYesUsdc) || 0;
|
|
292
|
+
const reserveNoUsdc = toOptionalNumber(options.reserveNoUsdc) || 0;
|
|
293
|
+
const excessYesUsdc = toOptionalNumber(options.excessYesUsdc) || 0;
|
|
294
|
+
const excessNoUsdc = toOptionalNumber(options.excessNoUsdc) || 0;
|
|
295
|
+
const hedgeRatio = clamp(toOptionalNumber(options.hedgeRatio) || 1, 0, 5);
|
|
312
296
|
const polymarketYesPct = toPercent(options.polymarketYesPct);
|
|
313
|
-
const hedgeCostBps = Math.max(0,
|
|
297
|
+
const hedgeCostBps = Math.max(0, toOptionalNumber(options.hedgeCostBps) || 35);
|
|
314
298
|
const feeTier = Number.isFinite(Number(options.feeTier)) ? Number(options.feeTier) : 3000;
|
|
315
299
|
const feeRate = clamp(feeTier / 1_000_000, 0, 0.1);
|
|
316
300
|
|
|
@@ -453,8 +437,8 @@ function buildMirrorSimulate(options = {}) {
|
|
|
453
437
|
: 'none';
|
|
454
438
|
|
|
455
439
|
const feeTier = Number.isFinite(Number(options.feeTier)) ? Number(options.feeTier) : 3000;
|
|
456
|
-
const hedgeRatio = clamp(
|
|
457
|
-
const hedgeCostBps = Math.max(0,
|
|
440
|
+
const hedgeRatio = clamp(toOptionalNumber(options.hedgeRatio) || 1, 0, 5);
|
|
441
|
+
const hedgeCostBps = Math.max(0, toOptionalNumber(options.hedgeCostBps) || 35);
|
|
458
442
|
const polymarketYesPct = toPercent(
|
|
459
443
|
options.polymarketYesPct !== undefined ? options.polymarketYesPct : (targetYesPct !== null ? targetYesPct : 50),
|
|
460
444
|
);
|