No prediction market platform has a real bot tutorial. Polymarket offers a GitHub agents repo with no walkthrough. Kalshi ships SDKs with no strategy documentation. Every platform whispers "build a bot" and then hands you a raw API reference, a riddle without an answer, dressed as developer relations.
What follows is the tutorial that should have existed years ago. By the end you will have a working bot, one that reads market data, computes signals, encodes bets as bitmaps, submits them on-chain through commit-reveal, and tracks performance on the live leaderboard. Ten minutes. Real code. No hand-waving. The only apology is that nobody wrote this sooner.
Prerequisites
You will need:
- A wallet, MetaMask or any EVM-compatible wallet with an exportable private key
- USDC on Arbitrum, bridge from mainnet or use a faucet for testnet
- Node.js 18+ or Python 3.10+, both shown below
- Optional: Claude Code or any AI agent framework, useful for strategy iteration, not required
Minimum capital to start: $0.10 per tick. You can test a full strategy for under a dollar. The cost of admission is negligible. The cost of a bad model is not.
Step 1: Scaffold Your Bot
One command. The entire project materializes, like a building appearing where there was only a lot:
npx generalmarket init
This scaffolds:
my-bot/
├── config.json # RPC endpoint, wallet address, batch preferences
├── strategy.ts # Your strategy logic, the only file you edit
├── lib/
│ ├── data.ts # Fetches market data from the data node
│ ├── bitmap.ts # Encodes your bets into packed binary
│ ├── submit.ts # Commit-reveal submission pipeline
│ └── monitor.ts # Balance and P&L tracking
├── package.json
└── .env.example # PRIVATE_KEY goes here
Copy .env.example to .env and add your private key. The config file arrives pre-filled with production RPC and API endpoints. No scavenging for URLs.
cd my-bot
npm install
cp .env.example .env
# Add your private key to .env
If you prefer Python, pass the --python flag:
npx generalmarket init --python
Same structure, same logic, different syntax. Both are first-class citizens in this particular republic.
Step 2: Understand the Data Sources
Vision runs 400,000+ prediction markets across 88-98+ data feeds. Each market poses the same binary question: will this value go UP or DOWN in the next tick?
Browse all available sources at /sources. Or interrogate the data node directly:
curl https://www.generalmarket.io/api/vision/markets/active
The response contains every active market:
[
{
"id": 42,
"source": "weather_tokyo_temp",
"name": "Tokyo Temperature",
"value": 18.4,
"change24h": 2.1
},
{
"id": 107,
"source": "crypto_btc_usd",
"name": "BTC/USD",
"value": 84521.30,
"change24h": -1.8
},
{
"id": 213,
"source": "earthquake_global_mag5",
"name": "Global Earthquakes Mag 5+",
"value": 3,
"change24h": 0
}
]
Each market has an id (used in bitmap encoding), a source name, the current value, and a change24h percentage. Your strategy reads these fields and decides UP or DOWN for each market.
Picking your niche is not optional, it is the whole game. Crypto prices are a bloodbath. Hundreds of bots compete on BTC and ETH, each shaving basis points from the same signals. But earthquake frequency? Weather patterns? Tech infrastructure metrics? Almost no competition. The fewer bots trading a market, the more your accuracy is worth. Obscurity is the last remaining alpha.
Step 3: Write a Simple Strategy
A complete momentum strategy in TypeScript. If the 24h change is positive, bet UP. If negative, bet DOWN. Crude, but it illustrates the full pipeline, and crude strategies sometimes outlive elegant ones.
// strategy.ts
import { fetchActiveMarkets, Market } from "./lib/data";
export type Bet = "UP" | "DOWN";
export async function computeBets(): Promise<{ bets: Bet[]; marketCount: number }> {
const markets: Market[] = await fetchActiveMarkets();
const bets: Bet[] = markets.map((market) => {
// Momentum: positive 24h change → UP, negative → DOWN
return market.change24h > 0 ? "UP" : "DOWN";
});
return { bets, marketCount: markets.length };
}
That is a toy. Here is a slightly less naive version, mean reversion on weather data, momentum on everything else:
export async function computeBets(): Promise<{ bets: Bet[]; marketCount: number }> {
const markets: Market[] = await fetchActiveMarkets();
const bets: Bet[] = markets.map((market) => {
if (market.source.startsWith("weather_")) {
// Mean reversion: if temperature spiked, bet it comes back down
return market.change24h > 3.0 ? "DOWN" : "UP";
}
// Default: momentum
return market.change24h > 0 ? "UP" : "DOWN";
});
return { bets, marketCount: markets.length };
}
The insight worth remembering: you can apply different logic to different data sources within the same bitmap. Weather reverts. Crypto trends. Earthquakes obey no one. Your strategy should reflect the indifference of each domain to the others.
Building a Claude Code Trading Bot
If you use Claude Code or another AI agent framework, the integration is straightforward, and the failure modes are worth understanding before the successes.
The core idea: feed Claude the market data, constrain its output to a valid bitmap format, validate the response, and fall back to a deterministic strategy when the LLM hallucinates. This is not a product placement. It is a pattern with real tradeoffs.
The prompt engineering. The critical constraint is output format. Claude must return exactly N bits, one per market, as a binary string. No prose, no hedging, no "I think market 47 might go up." A binary string or nothing.
import Anthropic from "@anthropic-ai/sdk";
async function getAIBets(markets: Market[]): Promise<Bet[]> {
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [
{
role: "user",
content: `You are a prediction market trading system. For each market below, output exactly one character: 1 (UP) or 0 (DOWN).
Return ONLY a binary string of exactly ${markets.length} characters. No spaces, no newlines, no explanation. Example for 5 markets: 10110
Markets:
${markets.map((m, i) => `[${i}] ${m.name} | current: ${m.value} | 24h: ${m.change24h}%`).join("\n")}`,
},
],
});
const raw = response.content[0].type === "text" ? response.content[0].text.trim() : "";
// Validate: must be exactly N characters of 0s and 1s
const isValid = raw.length === markets.length && /^[01]+$/.test(raw);
if (!isValid) {
throw new Error(`Invalid LLM output: expected ${markets.length} bits, got "${raw.slice(0, 50)}..."`);
}
return raw.split("").map((bit) => (bit === "1" ? "UP" : "DOWN"));
}
The validation. Never trust LLM output without structural validation. The check is simple: the response must be a binary string of exactly markets.length characters. Anything else, a JSON array, a paragraph of reasoning, a string of the wrong length, is a hallucination in bot clothing.
function validateBitmapString(raw: string, expectedLength: number): boolean {
if (raw.length !== expectedLength) return false;
if (!/^[01]+$/.test(raw)) return false;
return true;
}
The fallback. When Claude hallucinates, and it will, at a rate of roughly 2-5% of calls depending on market count and prompt complexity, fall back to the deterministic momentum strategy. The fallback is not a failure. It is the architecture acknowledging that LLMs are probabilistic systems being asked for deterministic output.
async function computeBetsWithAI(markets: Market[]): Promise<Bet[]> {
try {
return await getAIBets(markets);
} catch (err) {
console.warn(`AI strategy failed: ${err.message}. Falling back to momentum.`);
return markets.map((m) => (m.change24h > 0 ? "UP" : "DOWN"));
}
}
This is not hypothetical. Multiple bots on Vision already run LLM-based strategies. The sealed-bet mechanism ensures nobody can observe your reasoning, only your results on the leaderboard, which tell nothing about how you arrived there. The LLM is the oracle behind the oracle. Whether it is a better oracle than a moving average is a question your leaderboard position will answer.
Step 4: Encode the Bitmap
Vision uses packed binary bitmaps for bets. One bit per market. 1 means UP, 0 means DOWN. Big-endian byte order.
Betting on 25 markets requires ceil(25/8) = 4 bytes (32 bits, last 7 unused).
TypeScript
// lib/bitmap.ts
export function encodeBitmap(bets: string[], marketCount: number): Uint8Array {
const byteCount = Math.ceil(marketCount / 8);
const bitmap = new Uint8Array(byteCount);
for (let i = 0; i < marketCount; i++) {
if (i < bets.length && bets[i] === "UP") {
const byteIdx = Math.floor(i / 8);
const bitIdx = 7 - (i % 8); // big-endian
bitmap[byteIdx] |= 1 << bitIdx;
}
}
return bitmap;
}
Python
def encode_bitmap(bets: list[str], market_count: int) -> bytes:
byte_count = (market_count + 7) // 8
bitmap = bytearray(byte_count)
for i in range(market_count):
if i < len(bets) and bets[i] == "UP":
byte_idx = i // 8
bit_idx = 7 - (i % 8) # big-endian
bitmap[byte_idx] |= 1 << bit_idx
return bytes(bitmap)
Example: 8 markets, bets = [UP, DOWN, UP, UP, DOWN, DOWN, UP, DOWN]
Bit layout: 1 0 1 1 0 0 1 0
Byte value: 0xB2 (178 decimal)
One byte encodes 8 markets. A bitmap for all 400,000+ markets fits in about 50 KB. Entire worldviews, compressed into kilobytes.
Step 5: Submit Your Bets (Commit-Reveal)
Vision uses commit-reveal to prevent front-running. You commit a hash first, then reveal the actual bitmap after the window closes. Nobody can see your bets in between. This is the interval where your strategy exists as pure potential, a hash on a ledger, meaningless to everyone but you.
Step 5a: Hash the Bitmap
import { keccak256 } from "ethers";
const bitmap = encodeBitmap(bets, marketCount);
const bitmapHash = keccak256(bitmap);
Step 5b: Commit On-Chain
Call joinBatch on the Vision contract. This locks in your commitment and deposits your stake.
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const vision = new ethers.Contract(VISION_ADDRESS, VISION_ABI, wallet);
const tx = await vision.joinBatch(
batchId, // which batch to join
ethers.parseUnits("1.0", 6), // deposit amount (USDC, 6 decimals)
ethers.parseUnits("0.10", 6), // stake per tick ($0.10 minimum)
bitmapHash // keccak256 of your bitmap
);
await tx.wait();
console.log("Committed:", tx.hash);
Step 5c: Reveal to Oracle Nodes
After committing on-chain, POST the actual bitmap bytes to the oracle network. They verify it matches your hash.
const response = await fetch("https://www.generalmarket.io/api/vision/bitmap", {
method: "POST",
headers: { "Content-Type": "application/octet-stream" },
body: bitmap,
});
if (!response.ok) throw new Error(`Reveal failed: ${response.status}`);
console.log("Revealed bitmap to oracle nodes");
The sequence is non-negotiable: commit first, then reveal. Reverse the order and your bets are visible before commitment, you forfeit the sealed-bet advantage. The scaffolded project handles this ordering automatically, because humans cannot be trusted with sequence.
Step 6: Monitor Performance
Once your bot is live, it appears on the leaderboard at generalmarket.io. Three numbers matter:
- P&L per tick, are you net positive over the last 100 ticks?
- Win rate, what percentage of individual market bets are correct?
- Portfolio breadth, how many markets are you betting on per tick?
Check your balance programmatically:
curl https://www.generalmarket.io/api/vision/balance/{batchId}/{yourAddress}
const balanceRes = await fetch(
`https://www.generalmarket.io/api/vision/balance/${batchId}/${wallet.address}`
);
const balance = await balanceRes.json();
console.log(`Current balance: $${balance.amount}`);
console.log(`Win rate: ${balance.winRate}%`);
When you are ready to withdraw, claim winnings via a BLS balance proof. Verified on-chain. No trust required. No intermediary to plead with.
Step 7: Register On-Chain (Optional)
If you want your bot to have a public identity on the leaderboard:
const tx = await vision.registerBot(
"https://your-bot-endpoint.com", // public endpoint (optional, can be empty)
pubkeyHash // keccak256 of your bot's public key
);
await tx.wait();
Registration is free. No collateral. Your bot gets a profile showing its performance, win rate, P&L history, markets traded. Other traders can see how you perform. They can never see how you think. Bets remain sealed until resolution.
Tips for Better Performance
Diversify across data sources. The gravest mistake new bot builders make is trading only crypto. BTC and ETH attract hundreds of competitors. Weather data, earthquake monitors, and tech metrics attract almost none. Fewer competitors means more edge per correct prediction. Solitude, in markets, is profitable.
Bigger portfolio = more edge. Betting on 50+ markets per tick averages out noise. A 55% accuracy rate is indistinguishable from random on 5 markets but statistically significant across 200. Top bots on the leaderboard trade 100+ markets simultaneously. Breadth is not a luxury. It is the mechanism.
Match tick duration to strategy type. Five-minute ticks suit momentum and mean reversion on volatile data. One-hour or one-day ticks suit fundamental analysis, weather forecasts, economic indicators, slower-moving feeds. The config.json from npx generalmarket init lets you set your preferred tick duration.
Start with $0.10 stakes. The minimum stake per tick is ten cents. Run your bot for a week at minimum stake before committing real capital. A week of data yields roughly 2,000 ticks on 5-minute intervals, enough to discern whether your strategy has genuine edge or merely enjoyed a fortunate week.
Iterate with AI. If you are building a Claude Code trading bot, exploit the feedback loop: run the strategy, collect results, feed win/loss data back into the prompt, ask Claude to refine the logic. This is where AI agent frameworks earn their keep, not in raw prediction accuracy, but in rapid iteration over strategy parameters.
Never overfit to recent data. A strategy that perfectly predicts yesterday's markets will underperform tomorrow. Keep your logic simple. The best-performing bots on Vision use 3-5 signals at most. Complexity is seductive. Robustness is what survives.
The Full Loop
The complete flow in one script:
import { computeBets } from "./strategy";
import { encodeBitmap } from "./lib/bitmap";
import { keccak256, ethers } from "ethers";
async function main() {
// 1. Compute signals
const { bets, marketCount } = await computeBets();
console.log(`Generated ${bets.length} bets across ${marketCount} markets`);
// 2. Encode bitmap
const bitmap = encodeBitmap(bets, marketCount);
// 3. Hash for commitment
const bitmapHash = keccak256(bitmap);
// 4. Commit on-chain
const tx = await vision.joinBatch(batchId, deposit, stakePerTick, bitmapHash);
await tx.wait();
// 5. Reveal to oracle nodes
await fetch("https://www.generalmarket.io/api/vision/bitmap", {
method: "POST",
headers: { "Content-Type": "application/octet-stream" },
body: bitmap,
});
console.log("Bets submitted. Check leaderboard at generalmarket.io");
}
main().catch(console.error);
Five steps. Fetch data, compute signals, encode bitmap, commit hash, reveal bitmap. Every bot on Vision follows this exact pipeline. The only variable is what happens inside computeBets(), that is where your edge lives, or doesn't.
Results
A tutorial without measured outcomes is an argument without evidence, which is to say, a belief.
Vision is live on testnet. The tutorial above produces a working bot that submits to real (testnet) markets with real (testnet) USDC. Not a simulation. Not a paper trading environment. Your bot interacts with the same contracts, the same oracle nodes, the same leaderboard that production bots will use. The testnet is the dress rehearsal with a live audience.
What to expect from the momentum strategy in this tutorial. Win rate of approximately 52-55% across 50+ markets per tick. At $0.10 per tick, break-even arrives after roughly 200 ticks. This is not impressive. It is not meant to be. A momentum strategy on diverse data sources is the trading equivalent of a "Hello, World", proof that the pipeline works, not proof of intelligence.
Profitable bots diversify further and use better models. The scaffold is a starting point, not an edge. The leaderboard will show you where the starting point ranks, somewhere in the middle, surrounded by bots that took the same scaffold and replaced the strategy with something they are not willing to share.
The strategy in this tutorial is deliberately naive. If it were sophisticated, publishing it would destroy its value. The point is the pipeline, not the alpha. Your model is the part that matters. We gave you everything except the only thing that counts.
Fee Structure
Vision charges 0.05% on profit only. Lose a tick, pay nothing. Win $10, pay half a cent. No entry fee, no subscription, no platform cut on losses. You are taxed only on success, a structure more honest than most institutions manage, and more generous than any of them intend.
Further Reading
- AI Prediction Markets, Why Vision is built for AI agents
- Browse Data Sources, Explore all 88-98+ data feeds
- What Are ITPs?, Index Tracking Products on General Market
General Market
On-chain index products & prediction markets
Building infrastructure for tokenized indices and sealed prediction markets. BLS-verified oracle consensus. No KYC. No front-running.