← back to Ken
src/lib/polymarket.js
141 lines
const GAMMA_BASE = 'https://gamma-api.polymarket.com';
const CLOB_BASE = 'https://clob.polymarket.com';
const DATA_BASE = 'https://data-api.polymarket.com';
const WEATHER_KEYWORDS = [
'temperature', 'weather', 'rain', 'snow', 'hurricane', 'tornado',
'heat wave', 'cold', 'frost', 'wind', 'storm', 'precipitation',
'drought', 'flood', 'celsius', 'fahrenheit', 'degrees', 'inches',
'snowfall', 'rainfall', 'heatwave', 'blizzard', 'record high',
'record low', 'above average', 'below average', 'warmest', 'coldest'
];
async function fetchWithRetry(url, options = {}, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url, {
...options,
headers: {
'Accept': 'application/json',
'User-Agent': 'Bertha-Weather-Bot/1.0 (steve@designerwallcoverings.com)',
...options.headers
}
});
if (res.status === 429) {
const wait = Math.pow(2, i) * 1000 + Math.random() * 500;
console.log(`[Polymarket] Rate limited, waiting ${Math.round(wait)}ms...`);
await new Promise(r => setTimeout(r, wait));
continue;
}
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
return await res.json();
} catch (err) {
if (i === retries - 1) throw err;
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
}
async function getMarkets({ limit = 100, offset = 0, active = true, closed = false } = {}) {
const params = new URLSearchParams({
limit: String(limit),
offset: String(offset),
active: String(active),
closed: String(closed),
});
return fetchWithRetry(`${GAMMA_BASE}/markets?${params}`);
}
async function getMarketBySlug(slug) {
return fetchWithRetry(`${GAMMA_BASE}/markets?slug=${encodeURIComponent(slug)}`);
}
async function getMarketByCondition(conditionId) {
return fetchWithRetry(`${GAMMA_BASE}/markets?id=${encodeURIComponent(conditionId)}`);
}
function isWeatherMarket(market) {
const text = `${market.question || ''} ${market.description || ''} ${market.category || ''}`.toLowerCase();
return WEATHER_KEYWORDS.some(kw => text.includes(kw));
}
function parseWeatherEvent(market) {
const text = `${market.question || ''} ${market.description || ''}`;
const parsed = {
raw_question: market.question,
variable: null,
operator: null,
threshold: null,
threshold_unit: null,
location: null,
time_window: null,
confidence: 0
};
// Temperature patterns
const tempMatch = text.match(/(\d+)\s*(?:degrees?\s*)?(?:°?\s*)?(fahrenheit|celsius|F|C)/i);
if (tempMatch) {
parsed.variable = 'temperature';
parsed.threshold = parseFloat(tempMatch[1]);
parsed.threshold_unit = tempMatch[2].toLowerCase().startsWith('f') ? 'F' : 'C';
parsed.confidence = 0.8;
}
// Precipitation patterns
const precipMatch = text.match(/(\d+(?:\.\d+)?)\s*(?:inches?|in\.?|mm|cm)\s*(?:of\s+)?(rain|snow|precipitation)/i);
if (precipMatch) {
parsed.variable = precipMatch[2].toLowerCase();
parsed.threshold = parseFloat(precipMatch[1]);
parsed.threshold_unit = precipMatch[0].match(/mm/i) ? 'mm' : precipMatch[0].match(/cm/i) ? 'cm' : 'inches';
parsed.confidence = 0.7;
}
// Operator extraction
if (/above|over|exceed|more than|higher than|at least|≥|>=|>/i.test(text)) {
parsed.operator = '>=';
} else if (/below|under|less than|lower than|at most|≤|<=|</i.test(text)) {
parsed.operator = '<=';
} else if (/between/i.test(text)) {
parsed.operator = 'between';
} else if (/reach|hit/i.test(text)) {
parsed.operator = '>=';
}
// Location extraction (US cities)
const cities = [
'New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix', 'Philadelphia',
'San Antonio', 'San Diego', 'Dallas', 'San Jose', 'Austin', 'Jacksonville',
'Fort Worth', 'Columbus', 'Charlotte', 'Indianapolis', 'San Francisco',
'Seattle', 'Denver', 'Washington', 'Nashville', 'Oklahoma City', 'El Paso',
'Boston', 'Portland', 'Las Vegas', 'Memphis', 'Louisville', 'Baltimore',
'Milwaukee', 'Albuquerque', 'Tucson', 'Fresno', 'Sacramento', 'Kansas City',
'Mesa', 'Atlanta', 'Omaha', 'Colorado Springs', 'Raleigh', 'Long Beach',
'Virginia Beach', 'Miami', 'Oakland', 'Minneapolis', 'Tampa', 'Tulsa',
'Arlington', 'New Orleans', 'Detroit', 'Cleveland', 'Pittsburgh', 'St. Louis'
];
for (const city of cities) {
if (text.includes(city)) {
parsed.location = city;
parsed.confidence = Math.min(parsed.confidence + 0.1, 1.0);
break;
}
}
return parsed;
}
async function getOrderBook(tokenId) {
return fetchWithRetry(`${CLOB_BASE}/book?token_id=${tokenId}`);
}
async function getMarketTrades(conditionId, limit = 50) {
return fetchWithRetry(`${DATA_BASE}/trades?market=${conditionId}&limit=${limit}`);
}
module.exports = {
getMarkets, getMarketBySlug, getMarketByCondition,
isWeatherMarket, parseWeatherEvent,
getOrderBook, getMarketTrades,
GAMMA_BASE, CLOB_BASE, DATA_BASE, WEATHER_KEYWORDS
};