Initial commit with translated description
This commit is contained in:
103
SKILL.md
Normal file
103
SKILL.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
name: Polymarket
|
||||||
|
description: "通过CLI查询Polymarket预测市场赔率和事件。搜索市场、获取当前价格、按类别列出事件。支持体育博彩(NFL、NBA、足球/英超、欧冠)、政治、加密货币、选举、地缘政治。真钱市场比民意调查更准确。无需API密钥。当被问及赔率、概率、预测或"X的可能性有多大"时使用。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Polymarket Prediction Markets
|
||||||
|
|
||||||
|
Query real-time odds from Polymarket, the world's largest prediction market.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search for markets (instant via /public-search API)
|
||||||
|
polymarket search "Arsenal FC"
|
||||||
|
polymarket search "Super Bowl"
|
||||||
|
polymarket search "Bitcoin"
|
||||||
|
polymarket search "Trump"
|
||||||
|
|
||||||
|
# Browse by category
|
||||||
|
polymarket events --tag=sports
|
||||||
|
polymarket events --tag=crypto
|
||||||
|
polymarket events --tag=politics
|
||||||
|
|
||||||
|
# Get specific market details
|
||||||
|
polymarket market will-bitcoin-reach-100k
|
||||||
|
```
|
||||||
|
|
||||||
|
The CLI is at `polymarket.mjs` in this skill folder. Run with:
|
||||||
|
```bash
|
||||||
|
node /path/to/skill/polymarket.mjs <command>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `search <query>` | Search markets by keyword (recommended) |
|
||||||
|
| `events [options]` | List active events |
|
||||||
|
| `market <slug>` | Get market details by slug |
|
||||||
|
| `tags` | List available categories |
|
||||||
|
| `price <token_id>` | Get current price for a token |
|
||||||
|
| `book <token_id>` | Get orderbook depth |
|
||||||
|
|
||||||
|
## Event Options
|
||||||
|
|
||||||
|
- `--tag=<slug>` - Filter by category (crypto, politics, sports, etc.)
|
||||||
|
- `--limit=<n>` - Maximum results (default: 20)
|
||||||
|
|
||||||
|
## Understanding Odds
|
||||||
|
|
||||||
|
Prices = Probabilities:
|
||||||
|
- 0.65 (65¢) = 65% chance of "Yes"
|
||||||
|
- Volume = total $ traded
|
||||||
|
- Liquidity = available $ in orderbook
|
||||||
|
|
||||||
|
## Individual Match Betting
|
||||||
|
|
||||||
|
Polymarket has individual match markets for soccer, NFL, NBA, and more.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Soccer - use "FC" suffix for team names
|
||||||
|
polymarket search "Arsenal FC"
|
||||||
|
polymarket search "Manchester United FC"
|
||||||
|
polymarket search "Liverpool FC"
|
||||||
|
|
||||||
|
# NFL/NBA - team name works
|
||||||
|
polymarket search "Patriots"
|
||||||
|
polymarket search "Chiefs"
|
||||||
|
polymarket search "Lakers"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Market types available:**
|
||||||
|
- **Moneyline**: Win/Draw/Lose percentages
|
||||||
|
- **Spreads**: e.g., Arsenal -1.5
|
||||||
|
- **Totals**: Over/Under 2.5 goals
|
||||||
|
- **BTTS**: Both Teams to Score
|
||||||
|
|
||||||
|
## Common Categories
|
||||||
|
|
||||||
|
| Tag | Markets |
|
||||||
|
|-----|---------|
|
||||||
|
| `sports` | NFL, NBA, soccer, tennis, etc. |
|
||||||
|
| `politics` | Elections, legislation, appointments |
|
||||||
|
| `crypto` | Price targets, ETFs, regulations |
|
||||||
|
| `business` | IPOs, acquisitions, earnings |
|
||||||
|
| `tech` | Product launches, AI developments |
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
The CLI uses these public endpoints (no auth required):
|
||||||
|
|
||||||
|
- **Search**: `GET /public-search?q=<query>` - keyword search
|
||||||
|
- **Events**: `GET /events?active=true&closed=false` - list events
|
||||||
|
- **Markets**: `GET /markets?slug=<slug>` - market details
|
||||||
|
- **Tags**: `GET /tags` - available categories
|
||||||
|
|
||||||
|
Base URL: `https://gamma-api.polymarket.com`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Real money markets tend to be more accurate than polls/pundits
|
||||||
|
- Odds update in real-time as people trade
|
||||||
|
- Markets resolve to $1.00 (correct) or $0.00 (incorrect)
|
||||||
6
_meta.json
Normal file
6
_meta.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ownerId": "kn73vt6y69bfa21fmmwc7qrrrn7zwjxw",
|
||||||
|
"slug": "polymarket-odds",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"publishedAt": 1769364207598
|
||||||
|
}
|
||||||
277
polymarket.mjs
Normal file
277
polymarket.mjs
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Polymarket CLI - Query prediction market odds
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* polymarket search <query> - Search for markets by keyword
|
||||||
|
* polymarket events [options] - List events with filters
|
||||||
|
* polymarket market <slug> - Get market details and current odds
|
||||||
|
* polymarket sports - List sports leagues/series
|
||||||
|
* polymarket tags - List available categories
|
||||||
|
* polymarket price <token_id> - Get current price for a token
|
||||||
|
*/
|
||||||
|
|
||||||
|
const GAMMA_API = 'https://gamma-api.polymarket.com';
|
||||||
|
const CLOB_API = 'https://clob.polymarket.com';
|
||||||
|
|
||||||
|
async function fetchJSON(url) {
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePrice(pricesStr) {
|
||||||
|
try {
|
||||||
|
const prices = JSON.parse(pricesStr);
|
||||||
|
return prices.map(p => (parseFloat(p) * 100).toFixed(1) + '%');
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOutcomes(outcomesStr) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(outcomesStr);
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMarket(market, verbose = false) {
|
||||||
|
const outcomes = parseOutcomes(market.outcomes);
|
||||||
|
const prices = parsePrice(market.outcomePrices);
|
||||||
|
const odds = outcomes.map((o, i) => `${o}: ${prices[i] || '?'}`).join(' | ');
|
||||||
|
|
||||||
|
let out = `📊 ${market.question}\n`;
|
||||||
|
out += ` ${odds}\n`;
|
||||||
|
out += ` Volume: $${(market.volumeNum || 0).toLocaleString()}`;
|
||||||
|
if (market.liquidity) out += ` | Liquidity: $${parseFloat(market.liquidity).toLocaleString()}`;
|
||||||
|
if (verbose) {
|
||||||
|
out += `\n Slug: ${market.slug}`;
|
||||||
|
if (market.endDate) out += `\n Ends: ${market.endDate.split('T')[0]}`;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatEvent(event, verbose = false) {
|
||||||
|
let out = `\n🎯 ${event.title}\n`;
|
||||||
|
if (event.description && verbose) {
|
||||||
|
out += ` ${event.description.slice(0, 200)}${event.description.length > 200 ? '...' : ''}\n`;
|
||||||
|
}
|
||||||
|
out += ` Volume: $${(event.volume || 0).toLocaleString()}`;
|
||||||
|
if (event.liquidity) out += ` | Liquidity: $${parseFloat(event.liquidity).toLocaleString()}`;
|
||||||
|
out += '\n';
|
||||||
|
|
||||||
|
if (event.markets && event.markets.length > 0) {
|
||||||
|
for (const m of event.markets.slice(0, 5)) {
|
||||||
|
if (m.active && !m.closed) {
|
||||||
|
out += formatMarket(m, verbose) + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event.markets.length > 5) {
|
||||||
|
out += ` ... and ${event.markets.length - 5} more markets\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function search(query, limit = 10) {
|
||||||
|
// Use the public-search endpoint for efficient keyword search
|
||||||
|
const url = `${GAMMA_API}/public-search?q=${encodeURIComponent(query)}&limit=50`;
|
||||||
|
const data = await fetchJSON(url);
|
||||||
|
|
||||||
|
if (!data.events || data.events.length === 0) {
|
||||||
|
console.log(`No markets found for "${query}"`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter to only active, non-closed events
|
||||||
|
const matches = data.events.filter(e => e.active && !e.closed).slice(0, limit);
|
||||||
|
|
||||||
|
if (matches.length === 0) {
|
||||||
|
console.log(`No active markets found for "${query}"`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Found ${matches.length} active events matching "${query}":\n`);
|
||||||
|
for (const event of matches) {
|
||||||
|
console.log(formatEvent(event, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listEvents(options = {}) {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('active', 'true');
|
||||||
|
params.set('closed', 'false');
|
||||||
|
params.set('limit', options.limit || '20');
|
||||||
|
|
||||||
|
if (options.tag) params.set('tag_slug', options.tag);
|
||||||
|
if (options.series) params.set('series_id', options.series);
|
||||||
|
if (options.order) params.set('order', options.order);
|
||||||
|
|
||||||
|
const url = `${GAMMA_API}/events?${params}`;
|
||||||
|
const events = await fetchJSON(url);
|
||||||
|
|
||||||
|
console.log(`Active events (${events.length}):\n`);
|
||||||
|
for (const event of events) {
|
||||||
|
console.log(formatEvent(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMarket(slugOrId) {
|
||||||
|
const url = `${GAMMA_API}/markets?slug=${encodeURIComponent(slugOrId)}`;
|
||||||
|
const markets = await fetchJSON(url);
|
||||||
|
|
||||||
|
if (!markets || markets.length === 0) {
|
||||||
|
// Try by ID
|
||||||
|
const byIdUrl = `${GAMMA_API}/markets/${slugOrId}`;
|
||||||
|
try {
|
||||||
|
const market = await fetchJSON(byIdUrl);
|
||||||
|
console.log(formatMarket(market, true));
|
||||||
|
return;
|
||||||
|
} catch {
|
||||||
|
console.log(`Market not found: ${slugOrId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const market of markets) {
|
||||||
|
console.log(formatMarket(market, true));
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listSports() {
|
||||||
|
const url = `${GAMMA_API}/sports`;
|
||||||
|
try {
|
||||||
|
const sports = await fetchJSON(url);
|
||||||
|
console.log('Sports leagues:\n');
|
||||||
|
for (const sport of sports.slice(0, 30)) {
|
||||||
|
console.log(` ${sport.label || sport.title} (series_id: ${sport.id})`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Sports endpoint not available or empty');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listTags(limit = 50) {
|
||||||
|
const url = `${GAMMA_API}/tags?limit=${limit}`;
|
||||||
|
const tags = await fetchJSON(url);
|
||||||
|
|
||||||
|
console.log('Available categories:\n');
|
||||||
|
for (const tag of tags) {
|
||||||
|
console.log(` ${tag.label} (slug: ${tag.slug})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPrice(tokenId, side = 'buy') {
|
||||||
|
const url = `${CLOB_API}/price?token_id=${tokenId}&side=${side}`;
|
||||||
|
try {
|
||||||
|
const data = await fetchJSON(url);
|
||||||
|
console.log(`Price (${side}): ${(parseFloat(data.price) * 100).toFixed(1)}%`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Error fetching price: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getOrderbook(tokenId) {
|
||||||
|
const url = `${CLOB_API}/book?token_id=${tokenId}`;
|
||||||
|
try {
|
||||||
|
const data = await fetchJSON(url);
|
||||||
|
console.log('Orderbook:');
|
||||||
|
console.log(' Bids:', data.bids?.slice(0, 5).map(b => `${(parseFloat(b.price)*100).toFixed(1)}% x $${b.size}`).join(', ') || 'none');
|
||||||
|
console.log(' Asks:', data.asks?.slice(0, 5).map(a => `${(parseFloat(a.price)*100).toFixed(1)}% x $${a.size}`).join(', ') || 'none');
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Error fetching orderbook: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main
|
||||||
|
const [,, cmd, ...args] = process.argv;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
switch (cmd) {
|
||||||
|
case 'search':
|
||||||
|
case 's':
|
||||||
|
if (!args[0]) {
|
||||||
|
console.log('Usage: polymarket search <query>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await search(args.join(' '), parseInt(args.find(a => a.startsWith('--limit='))?.split('=')[1]) || 10);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'events':
|
||||||
|
case 'e':
|
||||||
|
const evtOpts = {};
|
||||||
|
for (const arg of args) {
|
||||||
|
if (arg.startsWith('--tag=')) evtOpts.tag = arg.split('=')[1];
|
||||||
|
if (arg.startsWith('--series=')) evtOpts.series = arg.split('=')[1];
|
||||||
|
if (arg.startsWith('--limit=')) evtOpts.limit = arg.split('=')[1];
|
||||||
|
if (arg.startsWith('--order=')) evtOpts.order = arg.split('=')[1];
|
||||||
|
}
|
||||||
|
await listEvents(evtOpts);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'market':
|
||||||
|
case 'm':
|
||||||
|
if (!args[0]) {
|
||||||
|
console.log('Usage: polymarket market <slug>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await getMarket(args[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sports':
|
||||||
|
await listSports();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'tags':
|
||||||
|
case 't':
|
||||||
|
await listTags(parseInt(args[0]) || 50);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'price':
|
||||||
|
case 'p':
|
||||||
|
if (!args[0]) {
|
||||||
|
console.log('Usage: polymarket price <token_id> [buy|sell]');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await getPrice(args[0], args[1] || 'buy');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'book':
|
||||||
|
case 'b':
|
||||||
|
if (!args[0]) {
|
||||||
|
console.log('Usage: polymarket book <token_id>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await getOrderbook(args[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log(`Polymarket CLI - Query prediction market odds
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
search <query> Search markets by keyword
|
||||||
|
events [options] List active events
|
||||||
|
--tag=<slug> Filter by category (crypto, politics, sports, etc.)
|
||||||
|
--limit=<n> Max results (default: 20)
|
||||||
|
market <slug> Get market details and current odds
|
||||||
|
sports List sports leagues
|
||||||
|
tags List available categories
|
||||||
|
price <token_id> Get current price for a token
|
||||||
|
book <token_id> Get orderbook for a token
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
polymarket search "super bowl"
|
||||||
|
polymarket search "bitcoin 100k"
|
||||||
|
polymarket events --tag=crypto --limit=10
|
||||||
|
polymarket market will-bitcoin-reach-100k
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error:', e.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user