pandora-cli-skills 1.1.29 → 1.1.30
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/history_service.cjs +1 -6
- package/cli/lib/leaderboard_service.cjs +1 -6
- package/cli/lib/market_admin_service.cjs +45 -5
- package/cli/lib/mirror_command_service.cjs +133 -0
- package/cli/lib/mirror_econ_service.cjs +1 -10
- package/cli/lib/mirror_handlers/browse.cjs +40 -0
- package/cli/lib/mirror_handlers/close.cjs +25 -0
- package/cli/lib/mirror_handlers/deploy.cjs +47 -0
- package/cli/lib/mirror_handlers/go.cjs +270 -0
- package/cli/lib/mirror_handlers/hedge_calc.cjs +110 -0
- package/cli/lib/mirror_handlers/lp_explain.cjs +31 -0
- package/cli/lib/mirror_handlers/plan.cjs +40 -0
- package/cli/lib/mirror_handlers/simulate.cjs +31 -0
- package/cli/lib/mirror_handlers/status.cjs +159 -0
- package/cli/lib/mirror_handlers/sync.cjs +293 -0
- package/cli/lib/mirror_handlers/verify.cjs +67 -0
- package/cli/lib/mirror_service.cjs +2 -4
- package/cli/lib/mirror_sizing_service.cjs +1 -16
- package/cli/lib/mirror_sync_service.cjs +110 -40
- package/cli/lib/mirror_verify_service.cjs +1 -6
- package/cli/lib/pandora_deploy_service.cjs +6 -8
- package/cli/lib/parsers/autopilot_flags.cjs +256 -0
- package/cli/lib/parsers/mirror_deploy_flags.cjs +239 -0
- package/cli/lib/parsers/mirror_go_flags.cjs +341 -0
- package/cli/lib/parsers/mirror_sync_flags.cjs +444 -0
- package/cli/lib/parsers/watch_flags.cjs +211 -0
- package/cli/lib/polymarket_adapter.cjs +1 -6
- package/cli/lib/polymarket_trade_adapter.cjs +98 -6
- package/cli/lib/shared/constants.cjs +17 -0
- package/cli/lib/shared/utils.cjs +27 -0
- package/cli/lib/similarity_service.cjs +1 -12
- package/cli/pandora.cjs +4352 -6383
- 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,4 +1,5 @@
|
|
|
1
1
|
const { createIndexerClient } = require('./indexer_client.cjs');
|
|
2
|
+
const { round } = require('./shared/utils.cjs');
|
|
2
3
|
|
|
3
4
|
const USDC_DECIMALS = 6;
|
|
4
5
|
const HISTORY_SCHEMA_VERSION = '1.0.0';
|
|
@@ -10,12 +11,6 @@ function toNumber(value) {
|
|
|
10
11
|
return numeric;
|
|
11
12
|
}
|
|
12
13
|
|
|
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
14
|
function toUsdc(raw) {
|
|
20
15
|
const numeric = toNumber(raw);
|
|
21
16
|
if (numeric === null) return null;
|
|
@@ -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,133 @@
|
|
|
1
|
+
const MIRROR_GO_USAGE =
|
|
2
|
+
'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>]';
|
|
3
|
+
|
|
4
|
+
const MIRROR_SYNC_USAGE =
|
|
5
|
+
'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>]';
|
|
6
|
+
|
|
7
|
+
const INVALID_SUBCOMMAND_MESSAGE =
|
|
8
|
+
'mirror requires subcommand: browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close';
|
|
9
|
+
|
|
10
|
+
function createRunMirrorCommand(deps) {
|
|
11
|
+
const {
|
|
12
|
+
CliError,
|
|
13
|
+
emitSuccess,
|
|
14
|
+
commandHelpPayload,
|
|
15
|
+
parseIndexerSharedFlags,
|
|
16
|
+
} = deps;
|
|
17
|
+
|
|
18
|
+
const handlerLoaders = {
|
|
19
|
+
browse: () => require('./mirror_handlers/browse.cjs'),
|
|
20
|
+
plan: () => require('./mirror_handlers/plan.cjs'),
|
|
21
|
+
deploy: () => require('./mirror_handlers/deploy.cjs'),
|
|
22
|
+
verify: () => require('./mirror_handlers/verify.cjs'),
|
|
23
|
+
'lp-explain': () => require('./mirror_handlers/lp_explain.cjs'),
|
|
24
|
+
'hedge-calc': () => require('./mirror_handlers/hedge_calc.cjs'),
|
|
25
|
+
simulate: () => require('./mirror_handlers/simulate.cjs'),
|
|
26
|
+
go: () => require('./mirror_handlers/go.cjs'),
|
|
27
|
+
sync: () => require('./mirror_handlers/sync.cjs'),
|
|
28
|
+
status: () => require('./mirror_handlers/status.cjs'),
|
|
29
|
+
close: () => require('./mirror_handlers/close.cjs'),
|
|
30
|
+
};
|
|
31
|
+
const handlerCache = new Map();
|
|
32
|
+
|
|
33
|
+
function getHandler(action) {
|
|
34
|
+
if (handlerCache.has(action)) {
|
|
35
|
+
return handlerCache.get(action);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const load = handlerLoaders[action];
|
|
39
|
+
if (!load) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const loaded = load();
|
|
44
|
+
const handler =
|
|
45
|
+
typeof loaded === 'function'
|
|
46
|
+
? loaded
|
|
47
|
+
: loaded && typeof loaded.handle === 'function'
|
|
48
|
+
? loaded.handle
|
|
49
|
+
: null;
|
|
50
|
+
|
|
51
|
+
if (!handler) {
|
|
52
|
+
throw new Error(`Invalid mirror handler module for action: ${action}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
handlerCache.set(action, handler);
|
|
56
|
+
return handler;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return async function runMirrorCommand(args, context) {
|
|
60
|
+
const action = args[0];
|
|
61
|
+
const actionArgs = args.slice(1);
|
|
62
|
+
|
|
63
|
+
if (!action || action === '--help' || action === '-h') {
|
|
64
|
+
if (context.outputMode === 'json') {
|
|
65
|
+
emitSuccess(
|
|
66
|
+
context.outputMode,
|
|
67
|
+
'mirror.help',
|
|
68
|
+
commandHelpPayload(
|
|
69
|
+
'pandora [--output table|json] mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close ...',
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(
|
|
74
|
+
'Usage: pandora [--output table|json] mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close ...',
|
|
75
|
+
);
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log('Subcommands:');
|
|
78
|
+
console.log(
|
|
79
|
+
' 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>]',
|
|
80
|
+
);
|
|
81
|
+
console.log(
|
|
82
|
+
' 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>]',
|
|
83
|
+
);
|
|
84
|
+
console.log(
|
|
85
|
+
' 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>]',
|
|
86
|
+
);
|
|
87
|
+
console.log(
|
|
88
|
+
' 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>]',
|
|
89
|
+
);
|
|
90
|
+
console.log(
|
|
91
|
+
' lp-explain --liquidity-usdc <n> [--source-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>]',
|
|
92
|
+
);
|
|
93
|
+
console.log(
|
|
94
|
+
' 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>]',
|
|
95
|
+
);
|
|
96
|
+
console.log(
|
|
97
|
+
' 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>]',
|
|
98
|
+
);
|
|
99
|
+
console.log(
|
|
100
|
+
` go ${MIRROR_GO_USAGE.replace('pandora [--output table|json] mirror go ', '')}`,
|
|
101
|
+
);
|
|
102
|
+
console.log(
|
|
103
|
+
` sync ${MIRROR_SYNC_USAGE.replace('pandora [--output table|json] mirror sync ', '')}`,
|
|
104
|
+
);
|
|
105
|
+
console.log(' stop|status selector: --pid-file <path>|--strategy-hash <hash>');
|
|
106
|
+
console.log(' status --state-file <path>|--strategy-hash <hash> [--with-live] [--trust-deploy]');
|
|
107
|
+
console.log(' close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute');
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const handler = getHandler(action);
|
|
113
|
+
if (!handler) {
|
|
114
|
+
throw new CliError('INVALID_ARGS', INVALID_SUBCOMMAND_MESSAGE);
|
|
115
|
+
}
|
|
116
|
+
const shared = parseIndexerSharedFlags(actionArgs);
|
|
117
|
+
|
|
118
|
+
return handler({
|
|
119
|
+
actionArgs: shared.rest,
|
|
120
|
+
shared,
|
|
121
|
+
context,
|
|
122
|
+
deps,
|
|
123
|
+
mirrorGoUsage: MIRROR_GO_USAGE,
|
|
124
|
+
mirrorSyncUsage: MIRROR_SYNC_USAGE,
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = {
|
|
130
|
+
MIRROR_GO_USAGE,
|
|
131
|
+
MIRROR_SYNC_USAGE,
|
|
132
|
+
createRunMirrorCommand,
|
|
133
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { computeDistributionHint, normalizeProbability } = require('./mirror_sizing_service.cjs');
|
|
2
|
+
const { round, clamp } = 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';
|
|
@@ -13,16 +14,6 @@ function toNumber(value) {
|
|
|
13
14
|
return numeric;
|
|
14
15
|
}
|
|
15
16
|
|
|
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
17
|
function toRawUsdc(usdc) {
|
|
27
18
|
const numeric = toNumber(usdc);
|
|
28
19
|
if (numeric === null) return null;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module.exports = async function handleMirrorBrowse({ shared, context, deps }) {
|
|
2
|
+
const {
|
|
3
|
+
includesHelpFlag,
|
|
4
|
+
emitSuccess,
|
|
5
|
+
commandHelpPayload,
|
|
6
|
+
maybeLoadIndexerEnv,
|
|
7
|
+
resolveIndexerUrl,
|
|
8
|
+
parseMirrorBrowseFlags,
|
|
9
|
+
browseMirrorMarkets,
|
|
10
|
+
renderMirrorBrowseTable,
|
|
11
|
+
} = deps;
|
|
12
|
+
|
|
13
|
+
if (includesHelpFlag(shared.rest)) {
|
|
14
|
+
if (context.outputMode === 'json') {
|
|
15
|
+
emitSuccess(
|
|
16
|
+
context.outputMode,
|
|
17
|
+
'mirror.browse.help',
|
|
18
|
+
commandHelpPayload(
|
|
19
|
+
'pandora [--output table|json] mirror browse [--min-yes-pct <n>] [--max-yes-pct <n>] [--min-volume-24h <n>] [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>]',
|
|
20
|
+
),
|
|
21
|
+
);
|
|
22
|
+
} else {
|
|
23
|
+
console.log(
|
|
24
|
+
'Usage: pandora [--output table|json] mirror browse [--min-yes-pct <n>] [--max-yes-pct <n>] [--min-volume-24h <n>] [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>]',
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
maybeLoadIndexerEnv(shared);
|
|
31
|
+
const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
|
|
32
|
+
const options = parseMirrorBrowseFlags(shared.rest);
|
|
33
|
+
const payload = await browseMirrorMarkets({
|
|
34
|
+
...options,
|
|
35
|
+
indexerUrl,
|
|
36
|
+
timeoutMs: shared.timeoutMs,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
emitSuccess(context.outputMode, 'mirror.browse', payload, renderMirrorBrowseTable);
|
|
40
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module.exports = async function handleMirrorClose({ shared, context, deps }) {
|
|
2
|
+
const {
|
|
3
|
+
includesHelpFlag,
|
|
4
|
+
emitSuccess,
|
|
5
|
+
commandHelpPayload,
|
|
6
|
+
parseMirrorCloseFlags,
|
|
7
|
+
buildMirrorClosePlan,
|
|
8
|
+
renderMirrorCloseTable,
|
|
9
|
+
} = deps;
|
|
10
|
+
|
|
11
|
+
if (includesHelpFlag(shared.rest)) {
|
|
12
|
+
const usage =
|
|
13
|
+
'pandora [--output table|json] mirror close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute';
|
|
14
|
+
if (context.outputMode === 'json') {
|
|
15
|
+
emitSuccess(context.outputMode, 'mirror.close.help', commandHelpPayload(usage));
|
|
16
|
+
} else {
|
|
17
|
+
console.log(`Usage: ${usage}`);
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const options = parseMirrorCloseFlags(shared.rest);
|
|
23
|
+
const payload = buildMirrorClosePlan(options);
|
|
24
|
+
emitSuccess(context.outputMode, 'mirror.close', payload, renderMirrorCloseTable);
|
|
25
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module.exports = async function handleMirrorDeploy({ shared, context, deps }) {
|
|
2
|
+
const {
|
|
3
|
+
includesHelpFlag,
|
|
4
|
+
emitSuccess,
|
|
5
|
+
commandHelpPayload,
|
|
6
|
+
maybeLoadTradeEnv,
|
|
7
|
+
resolveIndexerUrl,
|
|
8
|
+
parseMirrorDeployFlags,
|
|
9
|
+
deployMirror,
|
|
10
|
+
coerceMirrorServiceError,
|
|
11
|
+
renderMirrorDeployTable,
|
|
12
|
+
} = deps;
|
|
13
|
+
|
|
14
|
+
if (includesHelpFlag(shared.rest)) {
|
|
15
|
+
if (context.outputMode === 'json') {
|
|
16
|
+
emitSuccess(
|
|
17
|
+
context.outputMode,
|
|
18
|
+
'mirror.deploy.help',
|
|
19
|
+
commandHelpPayload(
|
|
20
|
+
'pandora [--output table|json] mirror 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>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
} else {
|
|
24
|
+
console.log(
|
|
25
|
+
'Usage: pandora [--output table|json] mirror 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>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
maybeLoadTradeEnv(shared);
|
|
32
|
+
const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
|
|
33
|
+
const options = parseMirrorDeployFlags(shared.rest);
|
|
34
|
+
let payload;
|
|
35
|
+
try {
|
|
36
|
+
payload = await deployMirror({
|
|
37
|
+
...options,
|
|
38
|
+
indexerUrl,
|
|
39
|
+
timeoutMs: shared.timeoutMs,
|
|
40
|
+
execute: options.execute,
|
|
41
|
+
});
|
|
42
|
+
} catch (err) {
|
|
43
|
+
throw coerceMirrorServiceError(err, 'MIRROR_DEPLOY_FAILED');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
emitSuccess(context.outputMode, 'mirror.deploy', payload, renderMirrorDeployTable);
|
|
47
|
+
};
|