pandora-cli-skills 1.1.65 → 1.1.66
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 +1 -1
- package/cli/lib/agent_contract_registry.cjs +18 -3
- package/cli/lib/mirror_command_service.cjs +3 -3
- package/cli/lib/mirror_handlers/deploy.cjs +2 -2
- package/cli/lib/mirror_handlers/plan.cjs +2 -2
- package/cli/lib/mirror_service.cjs +224 -4
- package/cli/lib/parsers/mirror_deploy_flags.cjs +11 -1
- package/cli/lib/parsers/mirror_go_flags.cjs +11 -1
- package/cli/lib/parsers/mirror_parser_guard.cjs +17 -0
- package/cli/lib/parsers/mirror_plan_flags.cjs +9 -0
- package/cli/lib/polymarket_adapter.cjs +21 -10
- package/cli/lib/polymarket_trade_adapter.cjs +17 -3
- package/cli/lib/schema_command_service.cjs +2 -0
- package/cli/pandora.cjs +7 -0
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -1890,7 +1890,7 @@ const commandContracts = [
|
|
|
1890
1890
|
name: 'mirror.plan',
|
|
1891
1891
|
summary: 'Generate mirror sizing and distribution plan from a Polymarket source market.',
|
|
1892
1892
|
usage:
|
|
1893
|
-
'pandora [--output table|json] mirror 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>]',
|
|
1893
|
+
'pandora [--output table|json] mirror 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] [--min-close-lead-seconds <n>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
1894
1894
|
emits: ['mirror.plan', 'mirror.plan.help'],
|
|
1895
1895
|
dataSchema: '#/definitions/MirrorPlanPayload',
|
|
1896
1896
|
mcpExposed: true,
|
|
@@ -1911,6 +1911,7 @@ const commandContracts = [
|
|
|
1911
1911
|
'max-liquidity-usdc': numberSchema('Maximum liquidity in USDC.', { minimum: 0 }),
|
|
1912
1912
|
'with-rules': booleanSchema('Include market rules payloads.'),
|
|
1913
1913
|
'include-similarity': booleanSchema('Include similarity diagnostics.'),
|
|
1914
|
+
'min-close-lead-seconds': integerSchema('Lead time before targetTimestamp when Pandora trading closes.', { minimum: 1 }),
|
|
1914
1915
|
'polymarket-gamma-url': stringSchema('Polymarket Gamma API base URL.'),
|
|
1915
1916
|
'polymarket-gamma-mock-url': stringSchema('Polymarket Gamma mock URL.'),
|
|
1916
1917
|
'polymarket-mock-url': stringSchema('Polymarket mock CLOB URL.'),
|
|
@@ -1923,7 +1924,7 @@ const commandContracts = [
|
|
|
1923
1924
|
name: 'mirror.deploy',
|
|
1924
1925
|
summary: 'Deploy a mirror market from selector or plan in dry-run or execute mode.',
|
|
1925
1926
|
usage:
|
|
1926
|
-
'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-50000>] [--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...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
|
|
1927
|
+
'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-50000>] [--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...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
|
|
1927
1928
|
emits: ['mirror.deploy', 'mirror.deploy.help'],
|
|
1928
1929
|
dataSchema: '#/definitions/MirrorDeployPayload',
|
|
1929
1930
|
mcpExposed: true,
|
|
@@ -1963,6 +1964,13 @@ const commandContracts = [
|
|
|
1963
1964
|
'distribution-no': numberSchema('Initial NO distribution parts.', { minimum: 0 }),
|
|
1964
1965
|
sources: flexibleArraySchema(stringSchema(), 'Source URL list.'),
|
|
1965
1966
|
'validation-ticket': stringSchema('Ticket returned by agent.market.validate for the exact final payload (CLI execute mode).'),
|
|
1967
|
+
'target-timestamp': {
|
|
1968
|
+
description: 'Explicit target timestamp override.',
|
|
1969
|
+
anyOf: [
|
|
1970
|
+
integerSchema('Explicit target timestamp in unix seconds.', { minimum: 1 }),
|
|
1971
|
+
stringSchema('Explicit ISO date/time override for target timestamp.'),
|
|
1972
|
+
],
|
|
1973
|
+
},
|
|
1966
1974
|
'manifest-file': stringSchema('Mirror manifest path.'),
|
|
1967
1975
|
'polymarket-host': stringSchema('Polymarket host override.'),
|
|
1968
1976
|
'polymarket-gamma-url': stringSchema('Polymarket Gamma API base URL.'),
|
|
@@ -2091,7 +2099,7 @@ const commandContracts = [
|
|
|
2091
2099
|
name: 'mirror.go',
|
|
2092
2100
|
summary: 'Run mirror deploy, verify, and optional sync workflow.',
|
|
2093
2101
|
usage:
|
|
2094
|
-
'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--max-open-exposure-usdc <amount>] [--max-trades-per-day <n>] [--polymarket-rpc-url <url>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--dotenv-path <path>] [--rpc-url <url>] [--private-key <hex>]',
|
|
2102
|
+
'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--max-open-exposure-usdc <amount>] [--max-trades-per-day <n>] [--polymarket-rpc-url <url>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--dotenv-path <path>] [--rpc-url <url>] [--private-key <hex>]',
|
|
2095
2103
|
emits: ['mirror.go', 'mirror.go.help'],
|
|
2096
2104
|
dataSchema: '#/definitions/MirrorDeployPayload',
|
|
2097
2105
|
mcpExposed: true,
|
|
@@ -2130,6 +2138,13 @@ const commandContracts = [
|
|
|
2130
2138
|
'polymarket-rpc-url': stringSchema('Polygon RPC URL for Polymarket preflight.'),
|
|
2131
2139
|
sources: flexibleArraySchema(stringSchema(), 'Independent public source URL list.'),
|
|
2132
2140
|
'validation-ticket': stringSchema('Ticket returned by agent.market.validate for the exact final payload (CLI execute mode).'),
|
|
2141
|
+
'target-timestamp': {
|
|
2142
|
+
description: 'Explicit target timestamp override.',
|
|
2143
|
+
anyOf: [
|
|
2144
|
+
integerSchema('Explicit target timestamp in unix seconds.', { minimum: 1 }),
|
|
2145
|
+
stringSchema('Explicit ISO date/time override for target timestamp.'),
|
|
2146
|
+
],
|
|
2147
|
+
},
|
|
2133
2148
|
'manifest-file': stringSchema('Mirror manifest path.'),
|
|
2134
2149
|
'dotenv-path': stringSchema('Env file path.'),
|
|
2135
2150
|
'rpc-url': commonFlags.rpcUrl,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @type {string}
|
|
5
5
|
*/
|
|
6
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-50000>] [--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>] [--polymarket-rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--validation-ticket <ticket>] [--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>]';
|
|
7
|
+
'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--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>] [--polymarket-rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--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
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Canonical usage string for `mirror sync`.
|
|
@@ -94,10 +94,10 @@ function createRunMirrorCommand(deps) {
|
|
|
94
94
|
' browse --min-yes-pct <n> --max-yes-pct <n> --min-volume-24h <n> [--closes-after <date>|--end-date-after <date|72h>] [--closes-before <date>|--end-date-before <date|72h>] [--question-contains <text>|--keyword <text>] [--slug <text>] [--category sports|crypto|politics|entertainment] [--exclude-sports] [--sort-by volume24h|liquidity|endDate] [--limit <n>] [--chain-id <id>] [--polymarket-tag-id <id>] [--polymarket-tag-ids <csv>] [--sport-tag-id <id>] [--sport-tag-ids <csv>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
95
95
|
);
|
|
96
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>]',
|
|
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] [--min-close-lead-seconds <n>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
|
|
98
98
|
);
|
|
99
99
|
console.log(
|
|
100
|
-
' deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--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...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
|
|
100
|
+
' deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--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...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--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
101
|
);
|
|
102
102
|
console.log(
|
|
103
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>]',
|
|
@@ -24,12 +24,12 @@ module.exports = async function handleMirrorDeploy({ shared, context, deps }) {
|
|
|
24
24
|
context.outputMode,
|
|
25
25
|
'mirror.deploy.help',
|
|
26
26
|
commandHelpPayload(
|
|
27
|
-
'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-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
|
|
27
|
+
'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-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
|
|
28
28
|
),
|
|
29
29
|
);
|
|
30
30
|
} else {
|
|
31
31
|
console.log(
|
|
32
|
-
'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-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
|
|
32
|
+
'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-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
return;
|
|
@@ -22,12 +22,12 @@ module.exports = async function handleMirrorPlan({ shared, context, deps }) {
|
|
|
22
22
|
context.outputMode,
|
|
23
23
|
'mirror.plan.help',
|
|
24
24
|
commandHelpPayload(
|
|
25
|
-
'pandora [--output table|json] mirror 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>]',
|
|
25
|
+
'pandora [--output table|json] mirror 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] [--min-close-lead-seconds <n>] [--polymarket-gamma-url <url>]',
|
|
26
26
|
),
|
|
27
27
|
);
|
|
28
28
|
} else {
|
|
29
29
|
console.log(
|
|
30
|
-
'Usage: pandora [--output table|json] mirror 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>]',
|
|
30
|
+
'Usage: pandora [--output table|json] mirror 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] [--min-close-lead-seconds <n>] [--polymarket-gamma-url <url>]',
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
return;
|
|
@@ -19,6 +19,27 @@ const { round } = require('./shared/utils.cjs');
|
|
|
19
19
|
const MIRROR_PLAN_SCHEMA_VERSION = '1.0.0';
|
|
20
20
|
const MIRROR_DEPLOY_SCHEMA_VERSION = '1.0.0';
|
|
21
21
|
const MIRROR_BROWSE_SCHEMA_VERSION = '1.0.0';
|
|
22
|
+
const DEFAULT_MIRROR_MIN_CLOSE_LEAD_SECONDS = 3600;
|
|
23
|
+
const MIRROR_SPORT_TIMING_PROFILES = Object.freeze({
|
|
24
|
+
basketball: Object.freeze({
|
|
25
|
+
key: 'basketball',
|
|
26
|
+
expectedDurationMinutes: 150,
|
|
27
|
+
resolutionBufferMinutes: 30,
|
|
28
|
+
minimumTradingBufferMinutes: 90,
|
|
29
|
+
}),
|
|
30
|
+
soccer: Object.freeze({
|
|
31
|
+
key: 'soccer',
|
|
32
|
+
expectedDurationMinutes: 120,
|
|
33
|
+
resolutionBufferMinutes: 20,
|
|
34
|
+
minimumTradingBufferMinutes: 30,
|
|
35
|
+
}),
|
|
36
|
+
sports: Object.freeze({
|
|
37
|
+
key: 'sports',
|
|
38
|
+
expectedDurationMinutes: 150,
|
|
39
|
+
resolutionBufferMinutes: 30,
|
|
40
|
+
minimumTradingBufferMinutes: 45,
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
22
43
|
|
|
23
44
|
function createServiceError(code, message, details = undefined) {
|
|
24
45
|
const err = new Error(message);
|
|
@@ -50,6 +71,150 @@ function normalizeComparableText(value) {
|
|
|
50
71
|
.trim();
|
|
51
72
|
}
|
|
52
73
|
|
|
74
|
+
function toUnixSeconds(value) {
|
|
75
|
+
if (value === null || value === undefined || value === '') return null;
|
|
76
|
+
const numeric = Number(value);
|
|
77
|
+
if (Number.isFinite(numeric)) {
|
|
78
|
+
return numeric > 1e12 ? Math.floor(numeric / 1000) : Math.floor(numeric);
|
|
79
|
+
}
|
|
80
|
+
const parsed = Date.parse(String(value));
|
|
81
|
+
if (Number.isNaN(parsed)) return null;
|
|
82
|
+
return Math.floor(parsed / 1000);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function formatTimestampIso(value) {
|
|
86
|
+
const unixSeconds = toUnixSeconds(value);
|
|
87
|
+
return Number.isFinite(unixSeconds) ? new Date(unixSeconds * 1000).toISOString() : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function parseMirrorTargetTimestampInput(value) {
|
|
91
|
+
if (value === null || value === undefined || value === '') return null;
|
|
92
|
+
const unixSeconds = toUnixSeconds(value);
|
|
93
|
+
return Number.isFinite(unixSeconds) && unixSeconds > 0 ? unixSeconds : null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function collectMirrorTimingTextPool(sourceMarket) {
|
|
97
|
+
const raw = sourceMarket && sourceMarket.raw && typeof sourceMarket.raw === 'object' ? sourceMarket.raw : {};
|
|
98
|
+
const tagEntries = []
|
|
99
|
+
.concat(Array.isArray(raw.tags) ? raw.tags : [])
|
|
100
|
+
.concat(Array.isArray(raw.tag_ids) ? raw.tag_ids : [])
|
|
101
|
+
.concat(Array.isArray(raw.tagIds) ? raw.tagIds : []);
|
|
102
|
+
const tagText = [];
|
|
103
|
+
for (const entry of tagEntries) {
|
|
104
|
+
if (entry === null || entry === undefined) continue;
|
|
105
|
+
if (typeof entry === 'string' || typeof entry === 'number') {
|
|
106
|
+
tagText.push(String(entry));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (typeof entry === 'object') {
|
|
110
|
+
for (const key of ['name', 'slug', 'label', 'title', 'group', 'topic', 'category', 'shortName', 'short_name']) {
|
|
111
|
+
if (entry[key]) tagText.push(String(entry[key]));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return [
|
|
116
|
+
sourceMarket && sourceMarket.question,
|
|
117
|
+
sourceMarket && sourceMarket.eventTitle,
|
|
118
|
+
sourceMarket && sourceMarket.eventSlug,
|
|
119
|
+
sourceMarket && sourceMarket.slug,
|
|
120
|
+
raw.sport,
|
|
121
|
+
raw.sport_type,
|
|
122
|
+
raw.league,
|
|
123
|
+
raw.competition,
|
|
124
|
+
...tagText,
|
|
125
|
+
]
|
|
126
|
+
.map((value) => normalizeComparableText(value))
|
|
127
|
+
.filter(Boolean)
|
|
128
|
+
.join(' ');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function inferMirrorSportTimingProfile(sourceMarket) {
|
|
132
|
+
const haystack = collectMirrorTimingTextPool(sourceMarket);
|
|
133
|
+
if (!haystack) return null;
|
|
134
|
+
if (/\b(nba|basketball)\b/.test(haystack)) {
|
|
135
|
+
return MIRROR_SPORT_TIMING_PROFILES.basketball;
|
|
136
|
+
}
|
|
137
|
+
if (/\b(soccer|football|premier league|epl|uefa|fifa|mls|la liga|serie a|bundesliga|champions league)\b/.test(haystack)) {
|
|
138
|
+
return MIRROR_SPORT_TIMING_PROFILES.soccer;
|
|
139
|
+
}
|
|
140
|
+
if (/\b(sport|sports|nfl|nhl|mlb|tennis|ufc|mma|formula 1|f1|cricket)\b/.test(haystack)) {
|
|
141
|
+
return MIRROR_SPORT_TIMING_PROFILES.sports;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function buildMirrorTimingData(sourceMarket, minCloseLeadSecondsInput) {
|
|
147
|
+
const minCloseLeadSeconds = Number.isFinite(Number(minCloseLeadSecondsInput))
|
|
148
|
+
? Math.max(0, Math.trunc(Number(minCloseLeadSecondsInput)))
|
|
149
|
+
: DEFAULT_MIRROR_MIN_CLOSE_LEAD_SECONDS;
|
|
150
|
+
const sourceTimestamp = toUnixSeconds(sourceMarket && sourceMarket.closeTimestamp);
|
|
151
|
+
const eventStartTimestamp = toUnixSeconds(sourceMarket && sourceMarket.eventStartTimestamp) || sourceTimestamp;
|
|
152
|
+
const sourceCloseTimestamp = toUnixSeconds(sourceMarket && sourceMarket.sourceCloseTimestamp);
|
|
153
|
+
const timestampSource = String(
|
|
154
|
+
(sourceMarket && sourceMarket.timestampSource)
|
|
155
|
+
|| (sourceMarket && sourceMarket.eventStartTimestamp ? 'game_start_time' : sourceMarket && sourceMarket.closeTimestamp ? 'source_timestamp' : ''),
|
|
156
|
+
).trim() || null;
|
|
157
|
+
const profile = inferMirrorSportTimingProfile(sourceMarket);
|
|
158
|
+
const warnings = [];
|
|
159
|
+
let suggestedTargetTimestamp = sourceTimestamp;
|
|
160
|
+
let expectedEndTimestamp = null;
|
|
161
|
+
let tradingCutoffTimestamp = null;
|
|
162
|
+
let reason = null;
|
|
163
|
+
|
|
164
|
+
if (profile && eventStartTimestamp) {
|
|
165
|
+
expectedEndTimestamp = eventStartTimestamp + (profile.expectedDurationMinutes * 60);
|
|
166
|
+
const baseSuggestedTargetTimestamp = expectedEndTimestamp + (profile.resolutionBufferMinutes * 60);
|
|
167
|
+
const minimumTradingCutoffTimestamp = expectedEndTimestamp + (profile.minimumTradingBufferMinutes * 60);
|
|
168
|
+
suggestedTargetTimestamp = Math.max(baseSuggestedTargetTimestamp, minimumTradingCutoffTimestamp + minCloseLeadSeconds);
|
|
169
|
+
tradingCutoffTimestamp = suggestedTargetTimestamp - minCloseLeadSeconds;
|
|
170
|
+
|
|
171
|
+
if (timestampSource === 'game_start_time') {
|
|
172
|
+
warnings.push('Polymarket provided game_start_time, which is the event start. Mirror deploy should use a later targetTimestamp that covers event completion and a buffer.');
|
|
173
|
+
}
|
|
174
|
+
if (tradingCutoffTimestamp <= expectedEndTimestamp) {
|
|
175
|
+
warnings.push('With the current close lead, trading would stop before the expected regulation end. Increase targetTimestamp or reduce min-close-lead-seconds.');
|
|
176
|
+
} else if (tradingCutoffTimestamp < minimumTradingCutoffTimestamp) {
|
|
177
|
+
warnings.push('With the current close lead, trading would stop too close to the expected finish and may not cover overtime or stoppage time.');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
reason = `Suggested targetTimestamp uses ${profile.key} timing defaults: expected duration ${profile.expectedDurationMinutes}m, resolution buffer ${profile.resolutionBufferMinutes}m, and trading cutoff buffer ${profile.minimumTradingBufferMinutes}m before the close lead.`;
|
|
181
|
+
} else {
|
|
182
|
+
if (timestampSource === 'game_start_time' && sourceTimestamp) {
|
|
183
|
+
warnings.push('Only a sports start time was available from Polymarket. Review targetTimestamp manually if you deploy this market.');
|
|
184
|
+
}
|
|
185
|
+
if (sourceTimestamp) {
|
|
186
|
+
tradingCutoffTimestamp = sourceTimestamp - minCloseLeadSeconds;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
sourceTimestamp,
|
|
192
|
+
sourceTimestampIso: formatTimestampIso(sourceTimestamp),
|
|
193
|
+
sourceTimestampKind: timestampSource,
|
|
194
|
+
sourceCloseTimestamp,
|
|
195
|
+
sourceCloseTimestampIso: formatTimestampIso(sourceCloseTimestamp),
|
|
196
|
+
eventStartTimestamp,
|
|
197
|
+
eventStartTimestampIso: formatTimestampIso(eventStartTimestamp),
|
|
198
|
+
expectedEndTimestamp,
|
|
199
|
+
expectedEndTimestampIso: formatTimestampIso(expectedEndTimestamp),
|
|
200
|
+
suggestedTargetTimestamp,
|
|
201
|
+
suggestedTargetTimestampIso: formatTimestampIso(suggestedTargetTimestamp),
|
|
202
|
+
tradingCutoffTimestamp,
|
|
203
|
+
tradingCutoffTimestampIso: formatTimestampIso(tradingCutoffTimestamp),
|
|
204
|
+
minCloseLeadSeconds,
|
|
205
|
+
profile: profile
|
|
206
|
+
? {
|
|
207
|
+
sport: profile.key,
|
|
208
|
+
expectedDurationMinutes: profile.expectedDurationMinutes,
|
|
209
|
+
resolutionBufferMinutes: profile.resolutionBufferMinutes,
|
|
210
|
+
minimumTradingBufferMinutes: profile.minimumTradingBufferMinutes,
|
|
211
|
+
}
|
|
212
|
+
: null,
|
|
213
|
+
reason,
|
|
214
|
+
warnings,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
53
218
|
function hasPandoraBinaryRules(value) {
|
|
54
219
|
const text = String(value || '');
|
|
55
220
|
return /(^|\n)\s*YES\s*:/i.test(text) && /(^|\n)\s*NO\s*:/i.test(text);
|
|
@@ -366,6 +531,14 @@ function buildPlanDigest(planData) {
|
|
|
366
531
|
sourceCount: planData.rules.sourceCount || null,
|
|
367
532
|
}
|
|
368
533
|
: null,
|
|
534
|
+
timing: planData.timing
|
|
535
|
+
? {
|
|
536
|
+
sourceTimestamp: planData.timing.sourceTimestamp || null,
|
|
537
|
+
sourceTimestampKind: planData.timing.sourceTimestampKind || null,
|
|
538
|
+
suggestedTargetTimestamp: planData.timing.suggestedTargetTimestamp || null,
|
|
539
|
+
minCloseLeadSeconds: planData.timing.minCloseLeadSeconds || null,
|
|
540
|
+
}
|
|
541
|
+
: null,
|
|
369
542
|
liquidityRecommendation: planData.liquidityRecommendation,
|
|
370
543
|
distributionHint: planData.distributionHint,
|
|
371
544
|
}));
|
|
@@ -412,6 +585,12 @@ async function buildMirrorPlan(options = {}) {
|
|
|
412
585
|
|
|
413
586
|
const distribution = computeDistributionHint(sourceYesProbability === null ? 0.5 : sourceYesProbability);
|
|
414
587
|
const rules = buildRuleTemplate(sourceMarket);
|
|
588
|
+
const timing = buildMirrorTimingData(
|
|
589
|
+
sourceMarket,
|
|
590
|
+
Number.isFinite(Number(options.minCloseLeadSeconds))
|
|
591
|
+
? Number(options.minCloseLeadSeconds)
|
|
592
|
+
: DEFAULT_MIRROR_MIN_CLOSE_LEAD_SECONDS,
|
|
593
|
+
);
|
|
415
594
|
|
|
416
595
|
let match = { best: null, diagnostics: [] };
|
|
417
596
|
try {
|
|
@@ -440,6 +619,8 @@ async function buildMirrorPlan(options = {}) {
|
|
|
440
619
|
for (const item of distribution.diagnostics || []) diagnostics.push(item);
|
|
441
620
|
for (const item of rules.diagnostics || []) diagnostics.push(item);
|
|
442
621
|
for (const item of match.diagnostics || []) diagnostics.push(item);
|
|
622
|
+
for (const item of timing.warnings || []) diagnostics.push(item);
|
|
623
|
+
if (timing.reason) diagnostics.push(timing.reason);
|
|
443
624
|
|
|
444
625
|
const topMatch = match.best
|
|
445
626
|
? {
|
|
@@ -464,6 +645,9 @@ async function buildMirrorPlan(options = {}) {
|
|
|
464
645
|
question: sourceMarket.question,
|
|
465
646
|
description: options.withRules ? sourceMarket.description : undefined,
|
|
466
647
|
closeTimestamp: sourceMarket.closeTimestamp,
|
|
648
|
+
eventStartTimestamp: sourceMarket.eventStartTimestamp || null,
|
|
649
|
+
sourceCloseTimestamp: sourceMarket.sourceCloseTimestamp || null,
|
|
650
|
+
timestampSource: sourceMarket.timestampSource || null,
|
|
467
651
|
yesPct: sourceMarket.yesPct,
|
|
468
652
|
noPct: sourceMarket.noPct,
|
|
469
653
|
volume24hUsd: round(sourceMarket.volume24hUsd, 6),
|
|
@@ -481,6 +665,7 @@ async function buildMirrorPlan(options = {}) {
|
|
|
481
665
|
proposedPandoraRules: rules.rulesText,
|
|
482
666
|
sourceCount: normalizeSources(options.sources).length,
|
|
483
667
|
},
|
|
668
|
+
timing,
|
|
484
669
|
similarity: topMatch ? topMatch.similarity : null,
|
|
485
670
|
sizingInputs: {
|
|
486
671
|
V24: round(sourceMarket.volume24hUsd, 6),
|
|
@@ -545,7 +730,25 @@ async function deployMirror(options = {}) {
|
|
|
545
730
|
|
|
546
731
|
const diagnostics = [];
|
|
547
732
|
const question = String(planData.sourceMarket && planData.sourceMarket.question ? planData.sourceMarket.question : '').trim();
|
|
548
|
-
const
|
|
733
|
+
const minCloseLeadSeconds = Number.isFinite(Number(options.minCloseLeadSeconds))
|
|
734
|
+
? Number(options.minCloseLeadSeconds)
|
|
735
|
+
: Number(planData.timing && planData.timing.minCloseLeadSeconds);
|
|
736
|
+
const effectiveTiming = planData.timing && typeof planData.timing === 'object'
|
|
737
|
+
? {
|
|
738
|
+
...planData.timing,
|
|
739
|
+
...(Number.isFinite(Number(minCloseLeadSeconds))
|
|
740
|
+
? { minCloseLeadSeconds: Number(minCloseLeadSeconds) }
|
|
741
|
+
: {}),
|
|
742
|
+
}
|
|
743
|
+
: buildMirrorTimingData(planData.sourceMarket || {}, minCloseLeadSeconds);
|
|
744
|
+
const suggestedTargetTimestamp = parseMirrorTargetTimestampInput(
|
|
745
|
+
effectiveTiming && effectiveTiming.suggestedTargetTimestamp,
|
|
746
|
+
);
|
|
747
|
+
const fallbackTargetTimestamp = parseMirrorTargetTimestampInput(
|
|
748
|
+
planData.sourceMarket && planData.sourceMarket.closeTimestamp,
|
|
749
|
+
);
|
|
750
|
+
const explicitTargetTimestamp = parseMirrorTargetTimestampInput(options.targetTimestamp);
|
|
751
|
+
const targetTimestamp = explicitTargetTimestamp || suggestedTargetTimestamp || fallbackTargetTimestamp;
|
|
549
752
|
const refreshedRuleTemplate = buildRuleTemplate({
|
|
550
753
|
...(planData.sourceMarket || {}),
|
|
551
754
|
description:
|
|
@@ -569,6 +772,17 @@ async function deployMirror(options = {}) {
|
|
|
569
772
|
suggestedRules: refreshedRuleTemplate.rulesText,
|
|
570
773
|
});
|
|
571
774
|
diagnostics.push(...refreshedRuleTemplate.diagnostics);
|
|
775
|
+
if (explicitTargetTimestamp && suggestedTargetTimestamp && explicitTargetTimestamp < suggestedTargetTimestamp) {
|
|
776
|
+
diagnostics.push(
|
|
777
|
+
`Explicit --target-timestamp (${explicitTargetTimestamp}) is earlier than the suggested sports-safe target (${suggestedTargetTimestamp}). Trading may close before overtime or late completion.`,
|
|
778
|
+
);
|
|
779
|
+
} else if (!explicitTargetTimestamp && suggestedTargetTimestamp && suggestedTargetTimestamp !== fallbackTargetTimestamp) {
|
|
780
|
+
diagnostics.push(
|
|
781
|
+
`Using suggested targetTimestamp ${suggestedTargetTimestamp} instead of raw source timestamp ${fallbackTargetTimestamp}.`,
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
diagnostics.push(...(Array.isArray(effectiveTiming && effectiveTiming.warnings) ? effectiveTiming.warnings : []));
|
|
785
|
+
if (effectiveTiming && effectiveTiming.reason) diagnostics.push(effectiveTiming.reason);
|
|
572
786
|
|
|
573
787
|
const liquidityUsdc =
|
|
574
788
|
options.liquidityUsdc !== null && options.liquidityUsdc !== undefined
|
|
@@ -608,9 +822,9 @@ async function deployMirror(options = {}) {
|
|
|
608
822
|
rules: sourceRulesText,
|
|
609
823
|
sources,
|
|
610
824
|
targetTimestamp,
|
|
611
|
-
minCloseLeadSeconds: Number.isFinite(Number(
|
|
612
|
-
? Number(
|
|
613
|
-
:
|
|
825
|
+
minCloseLeadSeconds: Number.isFinite(Number(minCloseLeadSeconds))
|
|
826
|
+
? Number(minCloseLeadSeconds)
|
|
827
|
+
: DEFAULT_MIRROR_MIN_CLOSE_LEAD_SECONDS,
|
|
614
828
|
liquidityUsdc,
|
|
615
829
|
distributionYes,
|
|
616
830
|
distributionNo,
|
|
@@ -696,6 +910,12 @@ async function deployMirror(options = {}) {
|
|
|
696
910
|
generatedAt: new Date().toISOString(),
|
|
697
911
|
planDigest: planData.planDigest || buildPlanDigest(planData),
|
|
698
912
|
deploymentArgs: deployPayload.deploymentArgs,
|
|
913
|
+
timing: {
|
|
914
|
+
...(effectiveTiming || {}),
|
|
915
|
+
selectedTargetTimestamp: targetTimestamp,
|
|
916
|
+
selectedTargetTimestampIso: formatTimestampIso(targetTimestamp),
|
|
917
|
+
overrideApplied: Boolean(explicitTargetTimestamp),
|
|
918
|
+
},
|
|
699
919
|
dryRun: deployPayload.mode === 'dry-run',
|
|
700
920
|
requiredValidation: deployPayload.requiredValidation || validationGate.requiredValidation || null,
|
|
701
921
|
agentValidation: deployPayload.agentValidation || validationGate.agentValidation || null,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { MIN_AMM_FEE_TIER, MAX_AMM_FEE_TIER } = require('../shared/constants.cjs');
|
|
2
|
-
const { normalizeMirrorPathForMcp, validateMirrorUrl } = require('./mirror_parser_guard.cjs');
|
|
2
|
+
const { normalizeMirrorPathForMcp, parseMirrorTargetTimestamp, validateMirrorUrl } = require('./mirror_parser_guard.cjs');
|
|
3
3
|
|
|
4
4
|
const MAX_UINT24 = 16_777_215;
|
|
5
5
|
const DISTRIBUTION_SCALE = 1_000_000_000;
|
|
@@ -122,6 +122,7 @@ function createParseMirrorDeployFlags(deps) {
|
|
|
122
122
|
distributionNoPct: null,
|
|
123
123
|
rules: null,
|
|
124
124
|
validationTicket: null,
|
|
125
|
+
targetTimestamp: null,
|
|
125
126
|
polymarketHost: null,
|
|
126
127
|
polymarketGammaUrl: null,
|
|
127
128
|
polymarketGammaMockUrl: null,
|
|
@@ -258,6 +259,15 @@ function createParseMirrorDeployFlags(deps) {
|
|
|
258
259
|
i += 1;
|
|
259
260
|
continue;
|
|
260
261
|
}
|
|
262
|
+
if (token === '--target-timestamp') {
|
|
263
|
+
options.targetTimestamp = parseMirrorTargetTimestamp(
|
|
264
|
+
requireFlagValue(args, i, '--target-timestamp'),
|
|
265
|
+
'--target-timestamp',
|
|
266
|
+
CliError,
|
|
267
|
+
);
|
|
268
|
+
i += 1;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
261
271
|
if (token === '--distribution-yes') {
|
|
262
272
|
options.distributionYes = parseDistributionUnits(
|
|
263
273
|
requireFlagValue(args, i, '--distribution-yes'),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { MIN_AMM_FEE_TIER, MAX_AMM_FEE_TIER } = require('../shared/constants.cjs');
|
|
2
|
-
const { normalizeMirrorPathForMcp, validateMirrorUrl } = require('./mirror_parser_guard.cjs');
|
|
2
|
+
const { normalizeMirrorPathForMcp, parseMirrorTargetTimestamp, validateMirrorUrl } = require('./mirror_parser_guard.cjs');
|
|
3
3
|
|
|
4
4
|
const MAX_UINT24 = 16_777_215;
|
|
5
5
|
const DISTRIBUTION_SCALE = 1_000_000_000;
|
|
@@ -135,6 +135,7 @@ function createParseMirrorGoFlags(deps) {
|
|
|
135
135
|
sources: [],
|
|
136
136
|
sourcesProvided: false,
|
|
137
137
|
validationTicket: null,
|
|
138
|
+
targetTimestamp: null,
|
|
138
139
|
manifestFile: null,
|
|
139
140
|
trustDeploy: false,
|
|
140
141
|
forceGate: false,
|
|
@@ -380,6 +381,15 @@ function createParseMirrorGoFlags(deps) {
|
|
|
380
381
|
i += 1;
|
|
381
382
|
continue;
|
|
382
383
|
}
|
|
384
|
+
if (token === '--target-timestamp') {
|
|
385
|
+
options.targetTimestamp = parseMirrorTargetTimestamp(
|
|
386
|
+
requireFlagValue(args, i, '--target-timestamp'),
|
|
387
|
+
'--target-timestamp',
|
|
388
|
+
CliError,
|
|
389
|
+
);
|
|
390
|
+
i += 1;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
383
393
|
if (token === '--manifest-file') {
|
|
384
394
|
options.manifestFile = normalizeMirrorPathForMcp(
|
|
385
395
|
requireFlagValue(args, i, '--manifest-file'),
|
|
@@ -34,8 +34,25 @@ function validateMirrorUrl(rawValue, flagName, CliError, isSecureHttpUrlOrLocal)
|
|
|
34
34
|
return value;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function parseMirrorTargetTimestamp(rawValue, flagName, CliError) {
|
|
38
|
+
const value = String(rawValue || '').trim();
|
|
39
|
+
const numeric = Number(value);
|
|
40
|
+
if (Number.isFinite(numeric) && numeric > 0) {
|
|
41
|
+
return Math.trunc(numeric > 1e12 ? numeric / 1000 : numeric);
|
|
42
|
+
}
|
|
43
|
+
const parsed = Date.parse(value);
|
|
44
|
+
if (!Number.isNaN(parsed)) {
|
|
45
|
+
return Math.floor(parsed / 1000);
|
|
46
|
+
}
|
|
47
|
+
throw new CliError(
|
|
48
|
+
'INVALID_FLAG_VALUE',
|
|
49
|
+
`${flagName} must be a unix timestamp in seconds (or milliseconds) or an ISO date/time string.`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
module.exports = {
|
|
38
54
|
normalizeMirrorPathForMcp,
|
|
39
55
|
defaultMirrorWorkspacePath,
|
|
56
|
+
parseMirrorTargetTimestamp,
|
|
40
57
|
validateMirrorUrl,
|
|
41
58
|
};
|
|
@@ -34,6 +34,7 @@ function createParseMirrorPlanFlags(deps) {
|
|
|
34
34
|
maxLiquidityUsdc: 50_000,
|
|
35
35
|
withRules: false,
|
|
36
36
|
includeSimilarity: false,
|
|
37
|
+
minCloseLeadSeconds: 3600,
|
|
37
38
|
polymarketHost: null,
|
|
38
39
|
polymarketGammaUrl: null,
|
|
39
40
|
polymarketGammaMockUrl: null,
|
|
@@ -111,6 +112,14 @@ function createParseMirrorPlanFlags(deps) {
|
|
|
111
112
|
options.includeSimilarity = true;
|
|
112
113
|
continue;
|
|
113
114
|
}
|
|
115
|
+
if (token === '--min-close-lead-seconds') {
|
|
116
|
+
options.minCloseLeadSeconds = parsePositiveInteger(
|
|
117
|
+
requireFlagValue(args, i, '--min-close-lead-seconds'),
|
|
118
|
+
'--min-close-lead-seconds',
|
|
119
|
+
);
|
|
120
|
+
i += 1;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
114
123
|
if (token === '--polymarket-host') {
|
|
115
124
|
options.polymarketHost = validateMirrorUrl(
|
|
116
125
|
requireFlagValue(args, i, '--polymarket-host'),
|
|
@@ -14,17 +14,18 @@ function toTimestampSeconds(value) {
|
|
|
14
14
|
return Math.floor(parsed / 1000);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function
|
|
17
|
+
function resolvePolymarketEventStartValue(row) {
|
|
18
|
+
if (!row || typeof row !== 'object') return null;
|
|
19
|
+
return row.game_start_time || row.gameStartTime || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolvePolymarketCloseFallbackValue(row) {
|
|
18
23
|
if (!row || typeof row !== 'object') return null;
|
|
19
|
-
return
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
row.endDate ||
|
|
25
|
-
row.closedTime ||
|
|
26
|
-
null
|
|
27
|
-
);
|
|
24
|
+
return row.endDateIso || row.end_date_iso || row.endDate || row.closedTime || null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolvePolymarketTimestampValue(row) {
|
|
28
|
+
return resolvePolymarketEventStartValue(row) || resolvePolymarketCloseFallbackValue(row);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function normalizeTokens(tokens) {
|
|
@@ -105,12 +106,17 @@ function mapGammaRow(row) {
|
|
|
105
106
|
const marketId = row && (row.conditionId || row.condition_id || row.id || row.questionID) ? String(
|
|
106
107
|
row.conditionId || row.condition_id || row.id || row.questionID,
|
|
107
108
|
) : null;
|
|
109
|
+
const eventStartTimestamp = toTimestampSeconds(resolvePolymarketEventStartValue(row));
|
|
110
|
+
const sourceCloseTimestamp = toTimestampSeconds(resolvePolymarketCloseFallbackValue(row));
|
|
108
111
|
const closeTimestamp = toTimestampSeconds(resolvePolymarketTimestampValue(row));
|
|
109
112
|
return {
|
|
110
113
|
legId: `polymarket:${String(marketId || '')}`,
|
|
111
114
|
venue: 'polymarket',
|
|
112
115
|
marketId,
|
|
113
116
|
question: row && (row.question || row.title || row.description) ? String(row.question || row.title || row.description) : null,
|
|
117
|
+
eventStartTimestamp,
|
|
118
|
+
sourceCloseTimestamp,
|
|
119
|
+
timestampSource: eventStartTimestamp ? 'game_start_time' : sourceCloseTimestamp ? 'end_date_iso' : null,
|
|
114
120
|
closeTimestamp,
|
|
115
121
|
yesPct: mapped.yes,
|
|
116
122
|
noPct: mapped.no,
|
|
@@ -129,11 +135,16 @@ function mapPolymarketRow(row) {
|
|
|
129
135
|
const mapped = normalizeTokens(row.tokens || []);
|
|
130
136
|
const question = row.question || row.description || null;
|
|
131
137
|
const marketId = row.condition_id || row.question_id || null;
|
|
138
|
+
const eventStartTimestamp = toTimestampSeconds(resolvePolymarketEventStartValue(row));
|
|
139
|
+
const sourceCloseTimestamp = toTimestampSeconds(resolvePolymarketCloseFallbackValue(row));
|
|
132
140
|
return {
|
|
133
141
|
legId: `polymarket:${String(marketId || '')}`,
|
|
134
142
|
venue: 'polymarket',
|
|
135
143
|
marketId,
|
|
136
144
|
question,
|
|
145
|
+
eventStartTimestamp,
|
|
146
|
+
sourceCloseTimestamp,
|
|
147
|
+
timestampSource: eventStartTimestamp ? 'game_start_time' : sourceCloseTimestamp ? 'end_date_iso' : null,
|
|
137
148
|
closeTimestamp: toTimestampSeconds(resolvePolymarketTimestampValue(row)),
|
|
138
149
|
yesPct: mapped.yes,
|
|
139
150
|
noPct: mapped.no,
|
|
@@ -49,11 +49,14 @@ function toTimestampSeconds(value) {
|
|
|
49
49
|
return numeric > 1e12 ? Math.floor(numeric / 1000) : Math.floor(numeric);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function
|
|
52
|
+
function resolvePolymarketEventStartTimestampValue(row) {
|
|
53
|
+
if (!row || typeof row !== 'object') return null;
|
|
54
|
+
return row.game_start_time || row.gameStartTime || null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolvePolymarketCloseFallbackValue(row) {
|
|
53
58
|
if (!row || typeof row !== 'object') return null;
|
|
54
59
|
return (
|
|
55
|
-
row.game_start_time ||
|
|
56
|
-
row.gameStartTime ||
|
|
57
60
|
row.end_date_iso ||
|
|
58
61
|
row.endDateIso ||
|
|
59
62
|
row.endDate ||
|
|
@@ -64,6 +67,10 @@ function resolvePolymarketEventTimestamp(row) {
|
|
|
64
67
|
);
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
function resolvePolymarketEventTimestamp(row) {
|
|
71
|
+
return resolvePolymarketEventStartTimestampValue(row) || resolvePolymarketCloseFallbackValue(row);
|
|
72
|
+
}
|
|
73
|
+
|
|
67
74
|
function normalizeHostList(hostInput) {
|
|
68
75
|
const rawValues = Array.isArray(hostInput) ? hostInput : String(hostInput || '').split(',');
|
|
69
76
|
const hosts = rawValues
|
|
@@ -424,6 +431,13 @@ function normalizeMarketRow(row) {
|
|
|
424
431
|
eventSlug: toStringOrNull(row && (row.event_slug || row.eventSlug)),
|
|
425
432
|
eventTitle: toStringOrNull(row && (row.event_title || row.eventTitle)),
|
|
426
433
|
description: rulesSections.length ? rulesSections.join('\n\n') : null,
|
|
434
|
+
eventStartTimestamp: toTimestampSeconds(resolvePolymarketEventStartTimestampValue(row)),
|
|
435
|
+
sourceCloseTimestamp: toTimestampSeconds(resolvePolymarketCloseFallbackValue(row)),
|
|
436
|
+
timestampSource: resolvePolymarketEventStartTimestampValue(row)
|
|
437
|
+
? 'game_start_time'
|
|
438
|
+
: resolvePolymarketCloseFallbackValue(row)
|
|
439
|
+
? 'end_date_iso'
|
|
440
|
+
: null,
|
|
427
441
|
closeTimestamp: toTimestampSeconds(resolvePolymarketEventTimestamp(row)),
|
|
428
442
|
yesPct: tokens.yes,
|
|
429
443
|
noPct: tokens.no,
|
|
@@ -795,6 +795,7 @@ function buildSchemaPayload() {
|
|
|
795
795
|
type: 'object',
|
|
796
796
|
properties: {
|
|
797
797
|
sourceMarket: { type: 'object' },
|
|
798
|
+
timing: { type: 'object' },
|
|
798
799
|
liquidityRecommendation: { type: 'object' },
|
|
799
800
|
distributionHint: { type: 'object' },
|
|
800
801
|
rules: { type: 'object' },
|
|
@@ -808,6 +809,7 @@ function buildSchemaPayload() {
|
|
|
808
809
|
mode: { enum: ['dry-run', 'execute'] },
|
|
809
810
|
planDigest: { type: ['string', 'null'] },
|
|
810
811
|
deploymentArgs: { type: ['object', 'null'] },
|
|
812
|
+
timing: { type: ['object', 'null'] },
|
|
811
813
|
dryRun: { type: ['boolean', 'null'] },
|
|
812
814
|
requiredValidation: { type: ['object', 'null'] },
|
|
813
815
|
agentValidation: { type: ['object', 'null'] },
|
package/cli/pandora.cjs
CHANGED
|
@@ -2195,6 +2195,7 @@ function renderAutopilotTable(data) {
|
|
|
2195
2195
|
}
|
|
2196
2196
|
|
|
2197
2197
|
function renderMirrorPlanTable(data) {
|
|
2198
|
+
const timing = data.timing || {};
|
|
2198
2199
|
printTable(
|
|
2199
2200
|
['Field', 'Value'],
|
|
2200
2201
|
[
|
|
@@ -2202,6 +2203,10 @@ function renderMirrorPlanTable(data) {
|
|
|
2202
2203
|
['sourceMarketId', data.sourceMarket ? data.sourceMarket.marketId : ''],
|
|
2203
2204
|
['sourceSlug', data.sourceMarket ? data.sourceMarket.slug || '' : ''],
|
|
2204
2205
|
['sourceYesPct', data.sourceMarket && data.sourceMarket.yesPct !== null ? data.sourceMarket.yesPct : ''],
|
|
2206
|
+
['sourceTimestampKind', timing.sourceTimestampKind || (data.sourceMarket ? data.sourceMarket.timestampSource || '' : '')],
|
|
2207
|
+
['eventStartAt', timing.eventStartTimestampIso || ''],
|
|
2208
|
+
['suggestedTargetAt', timing.suggestedTargetTimestampIso || ''],
|
|
2209
|
+
['tradingCutoffAt', timing.tradingCutoffTimestampIso || ''],
|
|
2205
2210
|
['recommendedLiquidityUsdc', data.liquidityRecommendation ? data.liquidityRecommendation.liquidityUsdc : ''],
|
|
2206
2211
|
['distributionYes', data.distributionHint ? data.distributionHint.distributionYes : ''],
|
|
2207
2212
|
['distributionNo', data.distributionHint ? data.distributionHint.distributionNo : ''],
|
|
@@ -2369,6 +2374,8 @@ function renderMirrorDeployTable(data) {
|
|
|
2369
2374
|
['pollTxHash', data.tx && data.tx.pollTxHash ? data.tx.pollTxHash : ''],
|
|
2370
2375
|
['approveTxHash', data.tx && data.tx.approveTxHash ? data.tx.approveTxHash : ''],
|
|
2371
2376
|
['marketTxHash', data.tx && data.tx.marketTxHash ? data.tx.marketTxHash : ''],
|
|
2377
|
+
['targetTimestamp', data.timing && data.timing.selectedTargetTimestampIso ? data.timing.selectedTargetTimestampIso : ''],
|
|
2378
|
+
['tradingCutoffAt', data.timing && data.timing.tradingCutoffTimestampIso ? data.timing.tradingCutoffTimestampIso : ''],
|
|
2372
2379
|
['seedOddsMatch', data.postDeployChecks && data.postDeployChecks.seedOddsMatch !== null ? (data.postDeployChecks.seedOddsMatch ? 'yes' : 'no') : ''],
|
|
2373
2380
|
['seedDiffPct', data.postDeployChecks && data.postDeployChecks.diffPct !== null ? data.postDeployChecks.diffPct : ''],
|
|
2374
2381
|
['blockedLiveSync', data.postDeployChecks && data.postDeployChecks.blockedLiveSync ? 'yes' : 'no'],
|