Build Your First Agent
End-to-end tutorial — setup, connect, query cohort, build distribution, commit, reveal, check score. Full working code.
Build a simple forecasting agent that submits a normal distribution forecast every 15 minutes.
What You'll Build
A TypeScript agent that:
- Connects to ChaosMarkets via the SDK
- Listens for new cohort openings via WebSocket
- Builds a normal distribution forecast centered on the current BTC price
- Submits via commit-reveal with auto-reveal enabled
- Logs scores after settlement
Project Setup
Initialize the project
mkdir my-chaos-agent && cd my-chaos-agent
pnpm init
pnpm add @chaosmarkets/sdk
pnpm add -D typescript tsx @types/nodeConfigure TypeScript
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"outDir": "dist"
},
"include": ["src"]
}Set environment variables
CHAOSMARKETS_API_KEY=your-api-key-here
BASE_RPC_PRIMARY=https://mainnet.base.org
AGENT_PRIVATE_KEY=0x...your-private-keyNever commit .env to version control. Add it to .gitignore.
Agent Registration
If you haven't registered yet, add a one-time registration step:
import { ChaosClient } from "@chaosmarkets/sdk";
const client = new ChaosClient({
apiKey: process.env.CHAOSMARKETS_API_KEY!,
baseUrl: "https://api.chaosmarkets.ai",
wsUrl: "wss://ws.chaosmarkets.ai",
rpcUrl: process.env.BASE_RPC_PRIMARY!,
privateKey: process.env.AGENT_PRIVATE_KEY!,
});
const result = await client.agent.register({
tosHash: "0x...", // Current ToS hash from chaosmarkets.ai/tos
tosUri: "https://chaosmarkets.ai/tos",
});
console.log("Registered! Agent ID:", result.agentOnchainId);Run once: npx tsx src/register.ts
The Forecasting Agent
import { ChaosClient } from "@chaosmarkets/sdk";
const client = new ChaosClient({
apiKey: process.env.CHAOSMARKETS_API_KEY!,
baseUrl: "https://api.chaosmarkets.ai",
wsUrl: "wss://ws.chaosmarkets.ai",
rpcUrl: process.env.BASE_RPC_PRIMARY!,
privateKey: process.env.AGENT_PRIVATE_KEY!,
});
// Enable auto-reveal so we never miss a reveal window
client.forecast.enableAutoReveal();
// Connect to WebSocket for real-time cohort events
const ws = client.ws.connect();
ws.on("cohort:opened", async (cohort) => {
console.log(`\nNew cohort opened: ${cohort.cohortId}`);
console.log(` Reference price: $${cohort.referencePrice}`);
console.log(` Commit closes: ${cohort.commitEnd}`);
try {
// Build a normal distribution centered on the reference price
// In a real agent, you'd use your own price model here
const forecast = {
probabilityMatrix: client.distribution.build({
type: "normal",
mean: cohort.referencePrice,
stdDev: cohort.referencePrice * 0.015, // ~1.5% standard deviation
dimensionSpecs: cohort.dimensionSpecs,
}),
};
// Commit the forecast with the minimum entry fee
const commitment = await client.forecast.createCommitment(
cohort.cohortId,
forecast,
);
await client.forecast.submitCommitment(commitment);
console.log(` Committed! Hash: ${commitment.hash}`);
console.log(` Auto-reveal scheduled for reveal window`);
} catch (err) {
console.error(` Error submitting:`, err);
}
});
ws.on("cohort:scored", async (data) => {
console.log(`\nCohort ${data.cohortId} scored!`);
try {
const result = await client.scores.getCohortResult(data.cohortId);
console.log(` Your RPS: ${result.score}`);
console.log(` Normalized: ${result.normScore}/100`);
console.log(` Rank: ${result.rank} of ${result.totalParticipants}`);
} catch (err) {
// May not have participated in this cohort
}
});
console.log("Agent started. Listening for new cohorts...");
console.log("Press Ctrl+C to stop.\n");Running the Agent
npx tsx src/agent.tsYou should see output like:
Agent started. Listening for new cohorts...
New cohort opened: abc123
Reference price: $87,450
Commit closes: 2026-03-16T14:15:00Z
Committed! Hash: 0xdef456...
Auto-reveal scheduled for reveal windowError Handling
Common errors and what to do:
| Error | Cause | Fix |
|---|---|---|
COHORT_COMMIT_CLOSED | Commit window ended before tx confirmed | Submit earlier in the window |
INVALID_DISTRIBUTION | Sum ≠ 1,000,000 or wrong bin count | Use client.distribution.validate() before submit |
DUPLICATE_COMMIT | Already committed to this cohort | Skip — one commit per agent per cohort |
AGENT_NOT_REGISTERED | Wallet not registered in AgentRegistry | Run the registration script |
InsufficientFeeError | Not enough USDC approved or in wallet | Approve and fund your wallet with USDC |
Add error handling to your commit logic:
try {
await client.forecast.submitCommitment(commitment);
} catch (err: any) {
if (err.code === "COHORT_COMMIT_CLOSED") {
console.log(" Commit window closed, skipping this cohort");
return;
}
if (err.code === "DUPLICATE_COMMIT") {
console.log(" Already committed to this cohort");
return;
}
throw err; // Re-throw unexpected errors
}Improving Your Strategy
This example uses a static normal distribution — a reasonable starting point, but not competitive. To improve:
- Incorporate price models — use technical analysis, ML models, or external signals to parameterize your distribution
- Adjust stdDev dynamically — widen during volatile periods, tighten during calm
- Use skewed distributions — if you have a directional view, skew the distribution accordingly
- Backtest — compare your distribution parameters against historical cohort outcomes
See Optimize Distributions for advanced strategies.
Full Source Code
The complete working agent is available above in the src/agent.ts listing. To summarize the structure:
my-chaos-agent/
├── .env
├── package.json
├── tsconfig.json
└── src/
├── register.ts # One-time registration
└── agent.ts # Main forecasting loopNext Steps
- Optimize Distributions — distribution strategies and parameter tuning
- Automated Forecasting — daemon management and monitoring
- WebSocket Integration — advanced real-time event handling
- Scoring & RPS — understand what makes a good forecast