const RAPID_API_HOST = "game-forecast-api.p.rapidapi.com";const MATCH_ID = 9527;const RAPID_API_KEY = "YOUR_RAPIDAPI_KEY";const headers = { "X-RapidAPI-Key": RAPID_API_KEY, "X-RapidAPI-Host": RAPID_API_HOST,};const canvas = document.getElementById("odds-chart");const status = document.getElementById("status");if (!(canvas instanceof HTMLCanvasElement) || !status) { throw new Error("Required DOM nodes are missing. Check your HTML markup.");}const context = canvas.getContext("2d");if (!context) { throw new Error("Unable to initialise the canvas context.");}const ChartJS = window.Chart;if (!ChartJS) { throw new Error("Chart.js failed to load. Check the CDN script tag.");}let chartInstance = null;function setStatus(message) { status.textContent = message;}function transformSnapshots(payload) { const data = payload.data || []; const event = data[0]; if (!event) { throw new Error("No event returned for MATCH_ID " + MATCH_ID); } const odds = Array.isArray(event.odds) ? event.odds : []; const matchWinnerSnapshots = odds.filter((entry) => entry.key === "match_winner"); if (matchWinnerSnapshots.length === 0) { throw new Error("The event does not expose a match_winner market."); } const accumulator = new Map(); const pushSnapshot = (runAt, values) => { if (!runAt || !values) { return; } const normalisedKey = new Date(runAt).toISOString(); accumulator.set(normalisedKey, { run_at: runAt, values: { Home: values.Home ?? values.home, Draw: values.Draw ?? values.draw, Away: values.Away ?? values.away, }, }); }; matchWinnerSnapshots.forEach((entry) => { pushSnapshot(entry.run_at, entry.values); if (Array.isArray(entry.history)) { entry.history.forEach((snapshot) => { pushSnapshot(snapshot.run_at, snapshot.values); }); } }); return Array.from(accumulator.values()).sort( (a, b) => new Date(a.run_at).getTime() - new Date(b.run_at).getTime() );}function renderChart(snapshots) { const labels = snapshots.map((item) => new Date(item.run_at)); const homeData = snapshots.map((item) => item.values.Home); const drawData = snapshots.map((item) => item.values.Draw); const awayData = snapshots.map((item) => item.values.Away); if (chartInstance) { chartInstance.destroy(); } chartInstance = new ChartJS(context, { type: "line", data: { labels, datasets: [ { label: "Home", data: homeData, borderColor: "#7c5fff", backgroundColor: "rgba(124,95,255,0.15)", tension: 0.2, }, { label: "Draw", data: drawData, borderColor: "#94a3b8", backgroundColor: "rgba(148,163,184,0.15)", tension: 0.2, }, { label: "Away", data: awayData, borderColor: "#f97316", backgroundColor: "rgba(249,115,22,0.15)", tension: 0.2, }, ], }, options: { responsive: true, maintainAspectRatio: false, animation: false, scales: { x: { type: "time", time: { unit: "day" }, ticks: { color: "#cbd5f5" }, grid: { color: "rgba(148,163,184,0.2)" }, }, y: { ticks: { color: "#cbd5f5" }, grid: { color: "rgba(148,163,184,0.15)" }, }, }, plugins: { legend: { position: "top", labels: { color: "#e2e8f0" } }, tooltip: { callbacks: { label: (context) => { const value = context.parsed.y; return context.dataset.label + ": " + value; }, }, }, }, }, });}async function fetchOdds() { setStatus("Loading odds history…"); try { const url = new URL("https://" + RAPID_API_HOST + "/events"); url.searchParams.set("id", String(MATCH_ID)); url.searchParams.set("include_all_history", "true"); const response = await fetch(url, { headers }); if (!response.ok) { throw new Error("Failed to fetch odds history"); } const payload = await response.json(); const snapshots = transformSnapshots(payload); if (snapshots.length === 0) { setStatus("No odds history available for this match."); return; } renderChart(snapshots); const lastSnapshot = snapshots[snapshots.length - 1]; setStatus("Last update: " + new Date(lastSnapshot.run_at).toLocaleString()); } catch (error) { console.error(error); setStatus("Unable to fetch odds history."); }}fetchOdds();