pandora-cli-skills 1.1.55 → 1.1.56
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/arb_command_service.cjs +30 -2
- package/cli/lib/arbitrage_service.cjs +14 -0
- package/cli/lib/parsers/core_command_flags.cjs +14 -1
- package/cli/lib/polymarket_adapter.cjs +173 -15
- package/cli/lib/scan_command_service.cjs +6 -0
- package/cli/pandora.cjs +310 -31
- package/package.json +1 -1
- package/tests/cli/cli.integration.test.cjs +158 -0
- package/tests/unit/combinatorial_arb.test.cjs +47 -0
- package/tests/unit/core_command_flags.test.cjs +113 -0
- package/tests/unit/polymarket_adapter.test.cjs +81 -0
- package/tests/unit/scan_command_service.test.cjs +68 -0
package/SKILL.md
CHANGED
|
@@ -87,6 +87,12 @@ function buildMarketSnapshots(markets, orderedIds) {
|
|
|
87
87
|
function buildCrossVenueArbOpportunities(payload, options) {
|
|
88
88
|
const opportunities = Array.isArray(payload && payload.opportunities) ? payload.opportunities : [];
|
|
89
89
|
const minSpreadPct = Number.isFinite(options.minNetSpreadPct) ? Number(options.minNetSpreadPct) : 0;
|
|
90
|
+
const feePctPerLeg = Number.isFinite(options.feePctPerLeg) ? Number(options.feePctPerLeg) : 0;
|
|
91
|
+
const slippagePctPerLeg = Number.isFinite(options.slippagePctPerLeg) ? Number(options.slippagePctPerLeg) : 0;
|
|
92
|
+
const feeImpactPct = roundNumber(Math.max(0, feePctPerLeg) * 2, 6);
|
|
93
|
+
const slippageImpactPct = roundNumber(Math.max(0, slippagePctPerLeg) * 2, 6);
|
|
94
|
+
const totalImpactPct = roundNumber(feeImpactPct + slippageImpactPct, 6);
|
|
95
|
+
const minTvlUsdc = Number.isFinite(options.minTvlUsdc) ? Number(options.minTvlUsdc) : 0;
|
|
90
96
|
const rows = [];
|
|
91
97
|
for (const item of opportunities) {
|
|
92
98
|
const legs = Array.isArray(item.legs) ? item.legs : [];
|
|
@@ -96,7 +102,21 @@ function buildCrossVenueArbOpportunities(payload, options) {
|
|
|
96
102
|
|
|
97
103
|
const spreadYesPct = toFiniteNumber(item.spreadYesPct);
|
|
98
104
|
const spreadNoPct = toFiniteNumber(item.spreadNoPct);
|
|
99
|
-
const
|
|
105
|
+
const grossSpreadPct = Math.max(spreadYesPct === null ? 0 : spreadYesPct, spreadNoPct === null ? 0 : spreadNoPct);
|
|
106
|
+
const pandoraLiquidityUsd = toFiniteNumber(pandoraLeg.liquidityUsd);
|
|
107
|
+
const polymarketLiquidityUsd = toFiniteNumber(polyLeg.liquidityUsd);
|
|
108
|
+
const minLegLiquidityUsd =
|
|
109
|
+
pandoraLiquidityUsd !== null && polymarketLiquidityUsd !== null
|
|
110
|
+
? Math.min(pandoraLiquidityUsd, polymarketLiquidityUsd)
|
|
111
|
+
: null;
|
|
112
|
+
|
|
113
|
+
if (minTvlUsdc > 0) {
|
|
114
|
+
if (minLegLiquidityUsd === null || minLegLiquidityUsd < minTvlUsdc) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const netSpreadPct = grossSpreadPct - totalImpactPct;
|
|
100
120
|
if (netSpreadPct < minSpreadPct) continue;
|
|
101
121
|
|
|
102
122
|
rows.push({
|
|
@@ -109,8 +129,14 @@ function buildCrossVenueArbOpportunities(payload, options) {
|
|
|
109
129
|
polymarketUrl: polyLeg.url || null,
|
|
110
130
|
pandoraYesPct: toFiniteNumber(pandoraLeg.yesPct),
|
|
111
131
|
polymarketYesPct: toFiniteNumber(polyLeg.yesPct),
|
|
132
|
+
pandoraLiquidityUsd,
|
|
133
|
+
polymarketLiquidityUsd,
|
|
134
|
+
minLegLiquidityUsd,
|
|
135
|
+
grossSpreadPct: roundNumber(grossSpreadPct, 6),
|
|
112
136
|
spreadYesPct,
|
|
113
137
|
spreadNoPct,
|
|
138
|
+
feeImpactPct,
|
|
139
|
+
slippageImpactPct,
|
|
114
140
|
netSpreadPct: roundNumber(netSpreadPct, 6),
|
|
115
141
|
confidenceScore: toFiniteNumber(item.confidenceScore),
|
|
116
142
|
riskFlags: Array.isArray(item.riskFlags) ? item.riskFlags : [],
|
|
@@ -530,11 +556,12 @@ function createRunArbCommand(deps) {
|
|
|
530
556
|
timeoutMs: shared.timeoutMs,
|
|
531
557
|
chainId: null,
|
|
532
558
|
venues: ['pandora', 'polymarket'],
|
|
533
|
-
limit: options.limit,
|
|
559
|
+
limit: Math.max(options.limit * 4, 100),
|
|
534
560
|
minSpreadPct: options.minNetSpreadPct,
|
|
535
561
|
minLiquidityUsd: options.minTvlUsdc,
|
|
536
562
|
maxCloseDiffHours: 24,
|
|
537
563
|
similarityThreshold: 0.86,
|
|
564
|
+
minTokenScore: 0.12,
|
|
538
565
|
crossVenueOnly: true,
|
|
539
566
|
withRules: false,
|
|
540
567
|
includeSimilarity: false,
|
|
@@ -645,6 +672,7 @@ function createRunArbCommand(deps) {
|
|
|
645
672
|
module.exports = {
|
|
646
673
|
ARB_USAGE,
|
|
647
674
|
buildArbOpportunities,
|
|
675
|
+
buildCrossVenueArbOpportunities,
|
|
648
676
|
buildCombinatorialArbOpportunities,
|
|
649
677
|
createRunArbCommand,
|
|
650
678
|
parseArbScanFlags,
|
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
const { toNumber, round } = require('./shared/utils.cjs');
|
|
9
9
|
|
|
10
10
|
const ARBITRAGE_SCHEMA_VERSION = '1.1.0';
|
|
11
|
+
const DEFAULT_MIN_TOKEN_SCORE = 0.12;
|
|
11
12
|
|
|
12
13
|
function toUsdc(raw) {
|
|
13
14
|
const numeric = toNumber(raw);
|
|
@@ -170,6 +171,9 @@ async function fetchPandoraLegs(options, diagnostics) {
|
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
function buildGroups(legs, options) {
|
|
174
|
+
const minTokenScore = Number.isFinite(options && options.minTokenScore)
|
|
175
|
+
? Number(options.minTokenScore)
|
|
176
|
+
: DEFAULT_MIN_TOKEN_SCORE;
|
|
173
177
|
const parent = new Map();
|
|
174
178
|
const acceptedPairChecks = new Map();
|
|
175
179
|
const makePairKey = (a, b) => [a, b].sort().join('|');
|
|
@@ -202,6 +206,7 @@ function buildGroups(legs, options) {
|
|
|
202
206
|
if (options.crossVenueOnly && left.venue === right.venue) continue;
|
|
203
207
|
|
|
204
208
|
const similarity = questionSimilarityBreakdown(left.question, right.question);
|
|
209
|
+
if (similarity.tokenScore < minTokenScore) continue;
|
|
205
210
|
if (similarity.score < options.similarityThreshold) continue;
|
|
206
211
|
|
|
207
212
|
let closeDiffHours = null;
|
|
@@ -587,6 +592,9 @@ async function scanArbitrage(options) {
|
|
|
587
592
|
timeoutMs: options.timeoutMs,
|
|
588
593
|
limit: Math.max(options.limit * 3, 100),
|
|
589
594
|
});
|
|
595
|
+
if (Array.isArray(poly.diagnostics) && poly.diagnostics.length) {
|
|
596
|
+
diagnostics.push(...poly.diagnostics);
|
|
597
|
+
}
|
|
590
598
|
|
|
591
599
|
const filtered = poly.items.filter((item) => {
|
|
592
600
|
if (!item.question) return false;
|
|
@@ -627,6 +635,9 @@ async function scanArbitrage(options) {
|
|
|
627
635
|
minLiquidityUsd: options.minLiquidityUsd,
|
|
628
636
|
maxCloseDiffHours: options.maxCloseDiffHours,
|
|
629
637
|
similarityThreshold: options.similarityThreshold,
|
|
638
|
+
minTokenScore: Number.isFinite(options.minTokenScore)
|
|
639
|
+
? options.minTokenScore
|
|
640
|
+
: DEFAULT_MIN_TOKEN_SCORE,
|
|
630
641
|
crossVenueOnly: options.crossVenueOnly,
|
|
631
642
|
withRules: options.withRules,
|
|
632
643
|
includeSimilarity: options.includeSimilarity,
|
|
@@ -694,6 +705,9 @@ async function scanArbitrage(options) {
|
|
|
694
705
|
minLiquidityUsd: options.minLiquidityUsd,
|
|
695
706
|
maxCloseDiffHours: options.maxCloseDiffHours,
|
|
696
707
|
similarityThreshold: options.similarityThreshold,
|
|
708
|
+
minTokenScore: Number.isFinite(options.minTokenScore)
|
|
709
|
+
? options.minTokenScore
|
|
710
|
+
: DEFAULT_MIN_TOKEN_SCORE,
|
|
697
711
|
crossVenueOnly: options.crossVenueOnly,
|
|
698
712
|
withRules: options.withRules,
|
|
699
713
|
includeSimilarity: options.includeSimilarity,
|
|
@@ -1153,6 +1153,7 @@ function createCoreCommandFlagParsers(deps) {
|
|
|
1153
1153
|
minLiquidityUsd: 1000,
|
|
1154
1154
|
maxCloseDiffHours: 24,
|
|
1155
1155
|
similarityThreshold: 0.86,
|
|
1156
|
+
minTokenScore: 0.12,
|
|
1156
1157
|
crossVenueOnly: true,
|
|
1157
1158
|
withRules: false,
|
|
1158
1159
|
includeSimilarity: false,
|
|
@@ -1186,7 +1187,7 @@ function createCoreCommandFlagParsers(deps) {
|
|
|
1186
1187
|
continue;
|
|
1187
1188
|
}
|
|
1188
1189
|
if (token === '--min-spread-pct') {
|
|
1189
|
-
options.minSpreadPct =
|
|
1190
|
+
options.minSpreadPct = parseNumber(requireFlagValue(args, i, '--min-spread-pct'), '--min-spread-pct');
|
|
1190
1191
|
i += 1;
|
|
1191
1192
|
continue;
|
|
1192
1193
|
}
|
|
@@ -1214,6 +1215,14 @@ function createCoreCommandFlagParsers(deps) {
|
|
|
1214
1215
|
i += 1;
|
|
1215
1216
|
continue;
|
|
1216
1217
|
}
|
|
1218
|
+
if (token === '--min-token-score') {
|
|
1219
|
+
options.minTokenScore = parseNumber(requireFlagValue(args, i, '--min-token-score'), '--min-token-score');
|
|
1220
|
+
if (options.minTokenScore < 0 || options.minTokenScore > 1) {
|
|
1221
|
+
throw new CliError('INVALID_FLAG_VALUE', '--min-token-score must be between 0 and 1.');
|
|
1222
|
+
}
|
|
1223
|
+
i += 1;
|
|
1224
|
+
continue;
|
|
1225
|
+
}
|
|
1217
1226
|
if (token === '--cross-venue-only') {
|
|
1218
1227
|
options.crossVenueOnly = true;
|
|
1219
1228
|
continue;
|
|
@@ -1249,6 +1258,10 @@ function createCoreCommandFlagParsers(deps) {
|
|
|
1249
1258
|
throw new CliError('UNKNOWN_FLAG', `Unknown flag for arbitrage: ${token}`);
|
|
1250
1259
|
}
|
|
1251
1260
|
|
|
1261
|
+
if (options.minSpreadPct < 0) {
|
|
1262
|
+
throw new CliError('INVALID_FLAG_VALUE', '--min-spread-pct must be >= 0.');
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1252
1265
|
return options;
|
|
1253
1266
|
}
|
|
1254
1267
|
|
|
@@ -2,6 +2,7 @@ const { ClobClient, Chain } = require('@polymarket/clob-client');
|
|
|
2
2
|
const { toNumber } = require('./shared/utils.cjs');
|
|
3
3
|
|
|
4
4
|
const DEFAULT_POLYMARKET_HOST = 'https://clob.polymarket.com';
|
|
5
|
+
const DEFAULT_POLYMARKET_GAMMA_HOST = 'https://gamma-api.polymarket.com';
|
|
5
6
|
|
|
6
7
|
function toTimestampSeconds(value) {
|
|
7
8
|
if (!value) return null;
|
|
@@ -53,6 +54,66 @@ function normalizeTokens(tokens) {
|
|
|
53
54
|
};
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
function safeParseJsonArray(value) {
|
|
58
|
+
if (Array.isArray(value)) return value;
|
|
59
|
+
if (typeof value !== 'string') return null;
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(value);
|
|
62
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function buildTokensFromGammaPayload(row) {
|
|
69
|
+
const outcomes = safeParseJsonArray(row && row.outcomes) || safeParseJsonArray(row && row.outcomeNames);
|
|
70
|
+
const prices = safeParseJsonArray(row && row.outcomePrices) || safeParseJsonArray(row && row.prices);
|
|
71
|
+
if (!Array.isArray(outcomes) || !Array.isArray(prices) || outcomes.length !== prices.length || !outcomes.length) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const tokens = [];
|
|
75
|
+
for (let index = 0; index < outcomes.length; index += 1) {
|
|
76
|
+
tokens.push({
|
|
77
|
+
outcome: String(outcomes[index] || ''),
|
|
78
|
+
price: prices[index],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return tokens;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function mapGammaRow(row) {
|
|
85
|
+
const tokens =
|
|
86
|
+
(Array.isArray(row && row.tokens) ? row.tokens : null) ||
|
|
87
|
+
(Array.isArray(row && row.outcomePrices) && Array.isArray(row && row.outcomes)
|
|
88
|
+
? row.outcomes.map((outcome, index) => ({ outcome, price: row.outcomePrices[index] }))
|
|
89
|
+
: null) ||
|
|
90
|
+
buildTokensFromGammaPayload(row);
|
|
91
|
+
const mapped = normalizeTokens(tokens || []);
|
|
92
|
+
const marketId = row && (row.conditionId || row.condition_id || row.id || row.questionID) ? String(
|
|
93
|
+
row.conditionId || row.condition_id || row.id || row.questionID,
|
|
94
|
+
) : null;
|
|
95
|
+
const closeTimestamp = toTimestampSeconds(
|
|
96
|
+
row && (row.endDateIso || row.end_date_iso || row.endDate || row.game_start_time || row.closedTime),
|
|
97
|
+
);
|
|
98
|
+
return {
|
|
99
|
+
legId: `polymarket:${String(marketId || '')}`,
|
|
100
|
+
venue: 'polymarket',
|
|
101
|
+
marketId,
|
|
102
|
+
question: row && (row.question || row.title || row.description) ? String(row.question || row.title || row.description) : null,
|
|
103
|
+
closeTimestamp,
|
|
104
|
+
yesPct: mapped.yes,
|
|
105
|
+
noPct: mapped.no,
|
|
106
|
+
liquidityUsd: toNumber(row && (row.liquidityNum || row.liquidity || row.liquidityClob)),
|
|
107
|
+
volumeUsd: toNumber(row && (row.volumeNum || row.volume || row.volumeClob)),
|
|
108
|
+
url: row && (row.market_slug || row.slug) ? `https://polymarket.com/event/${String(row.market_slug || row.slug)}` : null,
|
|
109
|
+
oddsSource: 'polymarket:gamma-markets',
|
|
110
|
+
diagnostics: mapped.diagnostics,
|
|
111
|
+
rules: row && row.description ? String(row.description) : null,
|
|
112
|
+
sources: [],
|
|
113
|
+
pollStatus: null,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
56
117
|
function mapPolymarketRow(row) {
|
|
57
118
|
const mapped = normalizeTokens(row.tokens || []);
|
|
58
119
|
const question = row.question || row.description || null;
|
|
@@ -101,41 +162,138 @@ async function fetchMockPolymarketMarkets(mockUrl, timeoutMs) {
|
|
|
101
162
|
}
|
|
102
163
|
}
|
|
103
164
|
|
|
104
|
-
async function
|
|
105
|
-
const
|
|
165
|
+
async function fetchJsonWithTimeout(url, timeoutMs) {
|
|
166
|
+
const controller = new AbortController();
|
|
167
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
168
|
+
try {
|
|
169
|
+
const response = await fetch(url, {
|
|
170
|
+
method: 'GET',
|
|
171
|
+
headers: { accept: 'application/json' },
|
|
172
|
+
signal: controller.signal,
|
|
173
|
+
});
|
|
174
|
+
if (!response.ok) {
|
|
175
|
+
throw new Error(`HTTP ${response.status} from ${url}`);
|
|
176
|
+
}
|
|
177
|
+
return response.json();
|
|
178
|
+
} finally {
|
|
179
|
+
clearTimeout(timeout);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeGammaMarketRows(payload) {
|
|
184
|
+
if (Array.isArray(payload)) return payload;
|
|
185
|
+
if (payload && Array.isArray(payload.data)) return payload.data;
|
|
186
|
+
if (payload && Array.isArray(payload.markets)) return payload.markets;
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function fetchGammaPolymarketMarkets(options = {}) {
|
|
191
|
+
const gammaHostRaw =
|
|
192
|
+
options.gammaHost ||
|
|
193
|
+
process.env.POLYMARKET_GAMMA_HOST ||
|
|
194
|
+
DEFAULT_POLYMARKET_GAMMA_HOST;
|
|
195
|
+
const gammaHost = String(gammaHostRaw || '').replace(/\/+$/, '');
|
|
106
196
|
const limit = Number.isInteger(options.limit) && options.limit > 0 ? options.limit : 100;
|
|
107
197
|
const timeoutMs = Number.isInteger(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : 12_000;
|
|
108
198
|
|
|
199
|
+
const rows = [];
|
|
200
|
+
let offset = 0;
|
|
201
|
+
let loopCount = 0;
|
|
202
|
+
while (rows.length < limit && loopCount < 10) {
|
|
203
|
+
loopCount += 1;
|
|
204
|
+
const pageLimit = Math.min(200, Math.max(25, limit - rows.length));
|
|
205
|
+
const url =
|
|
206
|
+
`${gammaHost}/markets?active=true&closed=false&archived=false&order=volume` +
|
|
207
|
+
`&ascending=false&limit=${pageLimit}&offset=${offset}`;
|
|
208
|
+
const payload = await fetchJsonWithTimeout(url, timeoutMs);
|
|
209
|
+
const batch = normalizeGammaMarketRows(payload);
|
|
210
|
+
if (!batch.length) break;
|
|
211
|
+
rows.push(...batch);
|
|
212
|
+
if (batch.length < pageLimit) break;
|
|
213
|
+
offset += batch.length;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
host: gammaHost,
|
|
218
|
+
rows,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function fetchClobPolymarketMarkets(options = {}) {
|
|
223
|
+
const host = options.host || DEFAULT_POLYMARKET_HOST;
|
|
224
|
+
const limit = Number.isInteger(options.limit) && options.limit > 0 ? options.limit : 100;
|
|
225
|
+
const client = new ClobClient(host, Chain.POLYGON);
|
|
226
|
+
const rows = [];
|
|
227
|
+
let cursor;
|
|
228
|
+
let loops = 0;
|
|
229
|
+
while (rows.length < limit && loops < 8) {
|
|
230
|
+
loops += 1;
|
|
231
|
+
const page = cursor ? await client.getMarkets(cursor) : await client.getMarkets();
|
|
232
|
+
const data = Array.isArray(page && page.data) ? page.data : [];
|
|
233
|
+
rows.push(...data);
|
|
234
|
+
if (!page || !page.next_cursor || page.next_cursor === cursor) break;
|
|
235
|
+
cursor = page.next_cursor;
|
|
236
|
+
}
|
|
237
|
+
return { host, rows };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function fetchPolymarketMarkets(options = {}) {
|
|
241
|
+
const limit = Number.isInteger(options.limit) && options.limit > 0 ? options.limit : 100;
|
|
242
|
+
const timeoutMs = Number.isInteger(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : 12_000;
|
|
243
|
+
const diagnostics = [];
|
|
109
244
|
let rows = [];
|
|
245
|
+
let source = 'polymarket:clob';
|
|
246
|
+
let host = options.host || DEFAULT_POLYMARKET_HOST;
|
|
110
247
|
|
|
111
248
|
if (options.mockUrl) {
|
|
112
249
|
rows = await fetchMockPolymarketMarkets(options.mockUrl, timeoutMs);
|
|
250
|
+
source = 'polymarket:mock';
|
|
113
251
|
} else {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
252
|
+
let gammaResult = null;
|
|
253
|
+
try {
|
|
254
|
+
const preferredGammaHost =
|
|
255
|
+
options.host && /gamma-api\.polymarket\.com/i.test(String(options.host))
|
|
256
|
+
? options.host
|
|
257
|
+
: options.gammaHost;
|
|
258
|
+
gammaResult = await fetchGammaPolymarketMarkets({
|
|
259
|
+
gammaHost: preferredGammaHost,
|
|
260
|
+
timeoutMs,
|
|
261
|
+
limit,
|
|
262
|
+
});
|
|
263
|
+
if (Array.isArray(gammaResult.rows) && gammaResult.rows.length) {
|
|
264
|
+
rows = gammaResult.rows;
|
|
265
|
+
source = 'polymarket:gamma';
|
|
266
|
+
host = gammaResult.host;
|
|
124
267
|
}
|
|
125
|
-
|
|
268
|
+
} catch (err) {
|
|
269
|
+
diagnostics.push(`Gamma markets fetch failed: ${err && err.message ? err.message : String(err)}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!rows.length) {
|
|
273
|
+
const clob = await fetchClobPolymarketMarkets({
|
|
274
|
+
host: options.host,
|
|
275
|
+
timeoutMs,
|
|
276
|
+
limit,
|
|
277
|
+
});
|
|
278
|
+
rows = clob.rows;
|
|
279
|
+
source = 'polymarket:clob';
|
|
280
|
+
host = clob.host;
|
|
126
281
|
}
|
|
127
282
|
}
|
|
128
283
|
|
|
129
|
-
const
|
|
284
|
+
const mapper = source === 'polymarket:gamma' ? mapGammaRow : mapPolymarketRow;
|
|
285
|
+
const mapped = rows.slice(0, limit).map(mapper);
|
|
130
286
|
return {
|
|
131
287
|
host,
|
|
132
|
-
source
|
|
288
|
+
source,
|
|
133
289
|
count: mapped.length,
|
|
134
290
|
items: mapped,
|
|
291
|
+
diagnostics,
|
|
135
292
|
};
|
|
136
293
|
}
|
|
137
294
|
|
|
138
295
|
module.exports = {
|
|
139
296
|
DEFAULT_POLYMARKET_HOST,
|
|
297
|
+
DEFAULT_POLYMARKET_GAMMA_HOST,
|
|
140
298
|
fetchPolymarketMarkets,
|
|
141
299
|
};
|
|
@@ -58,8 +58,10 @@ function createRunScanCommand(deps) {
|
|
|
58
58
|
const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
|
|
59
59
|
|
|
60
60
|
const options = parseMarketsListFlags(shared.rest);
|
|
61
|
+
options.expand = true;
|
|
61
62
|
options.withOdds = true;
|
|
62
63
|
|
|
64
|
+
let hedgeableDiagnostics = [];
|
|
63
65
|
let { items, pageInfo, unfilteredCount } = await fetchMarketsListPage(indexerUrl, options, shared.timeoutMs);
|
|
64
66
|
if (options.hedgeable && typeof filterHedgeableMarkets === 'function') {
|
|
65
67
|
const filtered = await filterHedgeableMarkets({
|
|
@@ -72,6 +74,9 @@ function createRunScanCommand(deps) {
|
|
|
72
74
|
if (typeof filtered.unfilteredCount === 'number') {
|
|
73
75
|
unfilteredCount = filtered.unfilteredCount;
|
|
74
76
|
}
|
|
77
|
+
if (Array.isArray(filtered && filtered.diagnostics)) {
|
|
78
|
+
hedgeableDiagnostics = filtered.diagnostics;
|
|
79
|
+
}
|
|
75
80
|
}
|
|
76
81
|
const enrichmentContext = await buildMarketsEnrichmentContext(indexerUrl, items, options, shared.timeoutMs);
|
|
77
82
|
const payload = buildMarketsListPayload(indexerUrl, options, items, pageInfo, {
|
|
@@ -79,6 +84,7 @@ function createRunScanCommand(deps) {
|
|
|
79
84
|
scanMode: true,
|
|
80
85
|
enrichmentContext,
|
|
81
86
|
unfilteredCount,
|
|
87
|
+
externalDiagnostics: hedgeableDiagnostics,
|
|
82
88
|
});
|
|
83
89
|
|
|
84
90
|
emitSuccess(context.outputMode, 'scan', payload, renderScanTable);
|