
Trading Manifold
- 20 installs
- 610 repo stars
- Updated June 26, 2026
- alsk1992/cloddsbot
trading-manifold is a skill that places bets on Manifold Markets using their REST API and Mana play money.
About
This skill places bets on Manifold Markets using their REST API. It searches markets, gets market details by ID or slug, places market and limit bets on YES or NO, cancels limit orders, sells shares, and reads bets and positions. A developer uses it to trade Manifold prediction markets with Mana play money from the clodds bot.
- Places bets on Manifold Markets via the REST API
- Search markets, bet YES/NO, limit orders, sell shares, list positions
- Uses Mana play money that can be donated to charity
Trading Manifold by the numbers
- 20 all-time installs (skills.sh)
- Ranked #714 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
trading-manifold capabilities & compatibility
Requires MANIFOLD_API_KEY; bets use Mana play money (free).
- Capabilities
- prediction market betting · market search · position tracking
- Use cases
- trading
- Runs
- Runs locally
- Pricing
- Bring your own API key
What trading-manifold says it does
Real, working methods to bet on Manifold Markets using Mana (play money that can be donated to charity).
Place a bet on Manifold
npx skills add https://github.com/alsk1992/cloddsbot --skill trading-manifoldAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 20 |
|---|---|
| repo stars | ★ 610 |
| Last updated | June 26, 2026 |
| Repository | alsk1992/cloddsbot ↗ |
What it does
Search Manifold markets and place YES/NO bets with Mana via the REST API.
Who is it for?
Betting on Manifold prediction markets with Mana via the REST API.
Skip if: Real-money exchange trading or crypto swaps.
When should I use this skill?
You need to search Manifold markets or place, cancel, or sell bets programmatically.
What you get
Placed and managed Manifold bets with position readouts.
- Placed Manifold bets
- Positions and bet history
By the numbers
- Search returns up to 10 markets by default
Files
Manifold Markets Trading Skill
Real, working methods to bet on Manifold Markets using Mana (play money that can be donated to charity).
Setup
Get your API key from: https://manifold.markets/profile (API key section)
import os
import requests
API_URL = "https://api.manifold.markets/v0"
API_KEY = os.getenv("MANIFOLD_API_KEY")
def headers():
return {
"Authorization": f"Key {API_KEY}",
"Content-Type": "application/json"
}Search Markets
def search_markets(query: str, limit: int = 10):
"""Search for markets"""
r = requests.get(f"{API_URL}/search-markets", params={
"term": query,
"limit": limit,
"filter": "open",
"sort": "liquidity"
})
r.raise_for_status()
markets = r.json()
for m in markets[:5]:
prob = m.get("probability", 0.5)
print(f"\nMarket: {m['question']}")
print(f"ID: {m['id']}")
print(f"Probability: {prob*100:.1f}%")
print(f"URL: {m.get('url', '')}")
return markets
markets = search_markets("AI")Get Market by ID or Slug
def get_market(id_or_slug: str):
"""Get market details"""
# Try by ID first
r = requests.get(f"{API_URL}/market/{id_or_slug}")
if r.status_code == 404:
# Try by slug
r = requests.get(f"{API_URL}/slug/{id_or_slug}")
r.raise_for_status()
return r.json()
market = get_market("will-gpt5-be-released-before-2025")
print(f"Question: {market['question']}")
print(f"Probability: {market.get('probability', 0.5)*100:.1f}%")Place a Bet
def place_bet(
market_id: str,
amount: int, # Mana amount to bet
outcome: str = "YES", # "YES" or "NO"
limit_prob: float = None # Optional limit order probability
):
"""
Place a bet on Manifold
Args:
market_id: The market ID (not slug!)
amount: Amount of Mana to bet
outcome: "YES" or "NO"
limit_prob: Optional - if set, creates a limit order at this probability
"""
payload = {
"contractId": market_id,
"amount": amount,
"outcome": outcome
}
if limit_prob is not None:
payload["limitProb"] = limit_prob
r = requests.post(f"{API_URL}/bet", headers=headers(), json=payload)
r.raise_for_status()
result = r.json()
print(f"Bet placed!")
print(f"Shares: {result.get('shares', 0):.2f}")
print(f"Probability after: {result.get('probAfter', 0)*100:.1f}%")
return result
# Market bet - buys at current price
result = place_bet(
market_id="abc123",
amount=100, # 100 Mana
outcome="YES"
)
# Limit order - only fills at 40% or below
result = place_bet(
market_id="abc123",
amount=100,
outcome="YES",
limit_prob=0.40
)Cancel Bet (Limit Orders Only)
def cancel_bet(bet_id: str):
"""Cancel a limit order"""
r = requests.post(f"{API_URL}/bet/cancel/{bet_id}", headers=headers())
r.raise_for_status()
return True
cancel_bet("bet123")Sell Shares
def sell_shares(
market_id: str,
outcome: str = "YES",
shares: float = None # None = sell all
):
"""
Sell shares in a market
Args:
market_id: The market ID
outcome: "YES" or "NO" - which shares to sell
shares: Number of shares to sell (None = all)
"""
payload = {
"contractId": market_id,
"outcome": outcome
}
if shares is not None:
payload["shares"] = shares
r = requests.post(f"{API_URL}/market/{market_id}/sell", headers=headers(), json=payload)
r.raise_for_status()
return r.json()
# Sell all YES shares
sell_shares("abc123", "YES")
# Sell specific amount
sell_shares("abc123", "YES", shares=50.0)Get Your Bets
def get_my_bets(market_id: str = None):
"""Get your bets"""
params = {}
if market_id:
params["contractId"] = market_id
r = requests.get(f"{API_URL}/bets", headers=headers(), params=params)
r.raise_for_status()
bets = r.json()
for b in bets[:10]:
print(f"Bet: {b['outcome']} {b['amount']}M @ {b.get('probBefore', 0)*100:.0f}%")
return bets
bets = get_my_bets()Get Your Positions
def get_positions():
"""Get current positions across all markets"""
# Get user info first
r = requests.get(f"{API_URL}/me", headers=headers())
r.raise_for_status()
user = r.json()
# Get bets to calculate positions
r = requests.get(f"{API_URL}/bets", headers=headers(), params={"limit": 1000})
bets = r.json()
# Aggregate by market
positions = {}
for bet in bets:
mid = bet["contractId"]
if mid not in positions:
positions[mid] = {"yes": 0, "no": 0, "invested": 0}
if bet["outcome"] == "YES":
positions[mid]["yes"] += bet.get("shares", 0)
else:
positions[mid]["no"] += bet.get("shares", 0)
if not bet.get("isSold", False):
positions[mid]["invested"] += bet["amount"]
return positions, user.get("balance", 0)
positions, balance = get_positions()
print(f"Balance: {balance} Mana")
for mid, pos in positions.items():
if pos["yes"] > 0 or pos["no"] > 0:
print(f"Market {mid}: YES={pos['yes']:.1f}, NO={pos['no']:.1f}")Get Balance
def get_balance():
"""Get your Mana balance"""
r = requests.get(f"{API_URL}/me", headers=headers())
r.raise_for_status()
user = r.json()
return user.get("balance", 0)
balance = get_balance()
print(f"Balance: {balance} Mana")Complete Trading Bot Example
#!/usr/bin/env python3
"""
Manifold arbitrage bot - finds mispriced markets
"""
import os
import time
import requests
API_URL = "https://api.manifold.markets/v0"
API_KEY = os.getenv("MANIFOLD_API_KEY")
def h():
return {"Authorization": f"Key {API_KEY}", "Content-Type": "application/json"}
def search(query):
r = requests.get(f"{API_URL}/search-markets",
params={"term": query, "limit": 20, "filter": "open"})
return r.json()
def bet(market_id, amount, outcome, limit_prob=None):
payload = {"contractId": market_id, "amount": amount, "outcome": outcome}
if limit_prob:
payload["limitProb"] = limit_prob
r = requests.post(f"{API_URL}/bet", headers=h(), json=payload)
return r.json()
def get_balance():
r = requests.get(f"{API_URL}/me", headers=h())
return r.json().get("balance", 0)
# Strategy: Buy extreme probabilities (likely to revert)
MIN_LIQUIDITY = 1000 # Only trade liquid markets
while True:
try:
balance = get_balance()
print(f"\nBalance: {balance} Mana")
# Search trending markets
markets = search("2024")
for m in markets:
prob = m.get("probability", 0.5)
liquidity = m.get("totalLiquidity", 0)
if liquidity < MIN_LIQUIDITY:
continue
# Buy YES on very low probability (< 10%)
if prob < 0.10:
print(f"LOW: {m['question'][:50]} at {prob*100:.1f}%")
if balance > 50:
bet(m["id"], 50, "YES", limit_prob=0.15)
# Buy NO on very high probability (> 90%)
elif prob > 0.90:
print(f"HIGH: {m['question'][:50]} at {prob*100:.1f}%")
if balance > 50:
bet(m["id"], 50, "NO", limit_prob=0.85)
time.sleep(300) # Check every 5 minutes
except Exception as e:
print(f"Error: {e}")
time.sleep(60)Multiple Choice Markets
def bet_multiple_choice(market_id: str, answer_id: str, amount: int):
"""Bet on a multiple choice market"""
payload = {
"contractId": market_id,
"amount": amount,
"answerId": answer_id
}
r = requests.post(f"{API_URL}/bet", headers=headers(), json=payload)
r.raise_for_status()
return r.json()
# Get market with answers
market = get_market("who-will-win-2024-election")
for answer in market.get("answers", []):
print(f"{answer['text']}: {answer['probability']*100:.1f}% (ID: {answer['id']})")
# Bet on specific answer
bet_multiple_choice("market123", "answer456", 100)Create a Market
def create_market(
question: str,
description: str = "",
close_time: int = None, # Unix timestamp
initial_prob: float = 0.5,
ante: int = 100 # Initial liquidity
):
"""Create a new binary market"""
payload = {
"outcomeType": "BINARY",
"question": question,
"description": description,
"initialProb": int(initial_prob * 100),
"ante": ante
}
if close_time:
payload["closeTime"] = close_time * 1000 # Milliseconds
r = requests.post(f"{API_URL}/market", headers=headers(), json=payload)
r.raise_for_status()
return r.json()
# Create market
new_market = create_market(
question="Will it rain tomorrow in NYC?",
initial_prob=0.3,
ante=100
)
print(f"Created: {new_market['url']}")Important Notes
1. Mana is play money - Can be donated to charity 2. No limit on bets - Unlike real money markets 3. Market maker AMM - Prices move based on bets 4. Limit orders - Use limitProb for better prices 5. API rate limits - Be gentle, ~60 requests/minute 6. Shares ≠ Mana - Shares vary based on probability
/**
* Trading Manifold CLI Skill
*
* Wired to:
* - src/feeds/manifold (createManifoldFeed - market search, data, WebSocket)
* - Manifold REST API v0 (bet placement, positions, balance)
*
* Commands:
* /manifold search <query> - Search markets
* /manifold market <id|slug> - Market details
* /manifold bet <market-id> <YES|NO> <amt> - Place bet (mana)
* /manifold positions - View positions
* /manifold balance - Mana balance
* /manifold trending - Trending markets
*/
import type { ManifoldFeed } from '../../../feeds/manifold';
import { logger } from '../../../utils/logger';
const MANIFOLD_API = 'https://api.manifold.markets/v0';
// =============================================================================
// HELPERS
// =============================================================================
function formatNumber(n: number, decimals = 2): string {
if (Math.abs(n) >= 1e9) return (n / 1e9).toFixed(decimals) + 'B';
if (Math.abs(n) >= 1e6) return (n / 1e6).toFixed(decimals) + 'M';
if (Math.abs(n) >= 1e3) return (n / 1e3).toFixed(decimals) + 'K';
return n.toFixed(decimals);
}
let feedInstance: ManifoldFeed | null = null;
async function getFeed(): Promise<ManifoldFeed> {
if (!feedInstance) {
const { createManifoldFeed } = await import('../../../feeds/manifold');
feedInstance = await createManifoldFeed();
}
return feedInstance;
}
function getApiKey(): string | null {
return process.env.MANIFOLD_API_KEY || null;
}
function authHeaders(): Record<string, string> {
const key = getApiKey();
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (key) {
headers['Authorization'] = `Key ${key}`;
}
return headers;
}
// =============================================================================
// HELP TEXT
// =============================================================================
function helpText(): string {
return [
'**Manifold Trading Commands**',
'',
'**Market Data:**',
' /manifold search <query> - Search markets',
' /manifold market <id|slug> - Market details',
' /manifold trending - Trending markets',
'',
'**Trading:**',
' /manifold bet <market-id> <YES|NO> <amt> - Place bet (mana)',
' /manifold positions - Your positions',
' /manifold balance - Mana balance',
'',
'**Env vars:** MANIFOLD_API_KEY (required for trading/positions)',
'',
'**Examples:**',
' /manifold search bitcoin',
' /manifold bet abc123 YES 100',
' /manifold market will-trump-win-2024',
].join('\n');
}
// =============================================================================
// MARKET DATA HANDLERS
// =============================================================================
async function handleSearch(query: string): Promise<string> {
if (!query) return 'Usage: /manifold search <query>';
try {
const feed = await getFeed();
const markets = await feed.searchMarkets(query);
if (markets.length === 0) {
return `No Manifold markets found for "${query}"`;
}
const lines = ['**Manifold Markets**', ''];
for (const m of markets.slice(0, 15)) {
const yesPrice = m.outcomes.find(o => o.name === 'Yes' || o.name === 'Higher')?.price ?? m.outcomes[0]?.price ?? 0;
lines.push(` [${m.id}] ${m.question}`);
lines.push(` ${(yesPrice * 100).toFixed(0)}% | Vol: M$${formatNumber(m.volume24h)} | Liq: M$${formatNumber(m.liquidity)}`);
}
if (markets.length > 15) {
lines.push('', `...and ${markets.length - 15} more`);
}
return lines.join('\n');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return `Error searching: ${message}`;
}
}
async function handleMarket(idOrSlug: string): Promise<string> {
if (!idOrSlug) return 'Usage: /manifold market <id-or-slug>';
try {
const feed = await getFeed();
const market = await feed.getMarket(idOrSlug);
if (!market) {
return `Market "${idOrSlug}" not found`;
}
const lines = [
`**${market.question}**`,
'',
`ID: ${market.id}`,
`Slug: ${market.slug}`,
`Platform: Manifold Markets`,
market.description ? `Description: ${typeof market.description === 'string' ? market.description.slice(0, 200) : ''}` : '',
'',
'**Outcomes:**',
];
for (const o of market.outcomes) {
lines.push(` ${o.name}: ${(o.price * 100).toFixed(1)}%`);
}
lines.push(
'',
`Volume 24h: M$${formatNumber(market.volume24h)}`,
`Liquidity: M$${formatNumber(market.liquidity)}`,
market.endDate ? `Closes: ${market.endDate.toLocaleDateString()}` : '',
`Resolved: ${market.resolved ? 'Yes' : 'No'}`,
market.resolutionValue !== undefined ? `Resolution: ${(market.resolutionValue * 100).toFixed(0)}%` : '',
'',
`URL: ${market.url}`,
);
return lines.filter(l => l !== '').join('\n');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return `Error: ${message}`;
}
}
async function handleTrending(): Promise<string> {
try {
// Use the Manifold API to get trending (sorted by activity)
const response = await fetch(`${MANIFOLD_API}/search-markets?limit=15&filter=open&sort=score`);
if (!response.ok) {
return `Failed to fetch trending: HTTP ${response.status}`;
}
const markets = await response.json() as Array<{
id: string;
question: string;
probability?: number;
volume24Hours: number;
totalLiquidity: number;
}>;
if (markets.length === 0) {
return 'No trending markets found';
}
const lines = ['**Trending Manifold Markets**', ''];
for (const m of markets) {
const prob = m.probability !== undefined ? `${(m.probability * 100).toFixed(0)}%` : '?';
lines.push(` [${m.id}] ${m.question}`);
lines.push(` ${prob} | Vol: M$${formatNumber(m.volume24Hours)} | Liq: M$${formatNumber(m.totalLiquidity)}`);
}
return lines.join('\n');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return `Error: ${message}`;
}
}
// =============================================================================
// TRADING HANDLERS
// =============================================================================
async function handleBet(marketId: string, outcome: string, amountStr: string): Promise<string> {
const apiKey = getApiKey();
if (!apiKey) {
return 'Set MANIFOLD_API_KEY to place bets on Manifold.';
}
if (!marketId || !outcome || !amountStr) {
return 'Usage: /manifold bet <market-id> <YES|NO> <amount>\nExample: /manifold bet abc123 YES 100';
}
const normalizedOutcome = outcome.toUpperCase();
if (normalizedOutcome !== 'YES' && normalizedOutcome !== 'NO') {
return 'Outcome must be YES or NO.';
}
const amount = parseFloat(amountStr);
if (isNaN(amount) || amount <= 0) {
return 'Amount must be a positive number (mana).';
}
try {
const response = await fetch(`${MANIFOLD_API}/bet`, {
method: 'POST',
headers: authHeaders(),
body: JSON.stringify({
contractId: marketId,
outcome: normalizedOutcome,
amount,
}),
});
if (!response.ok) {
const errorText = await response.text();
return `Bet failed: HTTP ${response.status} - ${errorText}`;
}
const data = await response.json() as {
betId?: string;
amount?: number;
probBefore?: number;
probAfter?: number;
shares?: number;
};
const lines = [
'**Bet Placed**',
`Market: ${marketId}`,
`${normalizedOutcome} M$${amount}`,
];
if (data.betId) lines.push(`Bet ID: ${data.betId}`);
if (data.shares) lines.push(`Shares: ${formatNumber(data.shares)}`);
if (data.probBefore !== undefined && data.probAfter !== undefined) {
lines.push(`Probability: ${(data.probBefore * 100).toFixed(1)}% -> ${(data.probAfter * 100).toFixed(1)}%`);
}
return lines.join('\n');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return `Error placing bet: ${message}`;
}
}
async function handlePositions(): Promise<string> {
const apiKey = getApiKey();
if (!apiKey) {
return 'Set MANIFOLD_API_KEY to view positions.';
}
try {
// First get user ID from /me
const meRes = await fetch(`${MANIFOLD_API}/me`, { headers: authHeaders() });
if (!meRes.ok) {
return `Failed to fetch profile: HTTP ${meRes.status}`;
}
const me = await meRes.json() as { id?: string; username?: string; balance?: number };
if (!me.id) {
return 'Could not identify user from API key.';
}
// Then get recent bets for this user
const betsRes = await fetch(`${MANIFOLD_API}/bets?userId=${me.id}&limit=50`, {
headers: authHeaders(),
});
if (!betsRes.ok) {
return `Failed to fetch bets: HTTP ${betsRes.status}`;
}
const bets = await betsRes.json() as Array<{
id: string;
contractId: string;
outcome: string;
amount: number;
shares: number;
probBefore: number;
probAfter: number;
createdTime: number;
isFilled?: boolean;
isCancelled?: boolean;
}>;
if (bets.length === 0) {
return 'No recent bets/positions found.';
}
// Group bets by contract to show net position
const contractBets = new Map<string, { outcome: string; totalShares: number; totalAmount: number; count: number }>();
for (const bet of bets) {
if (bet.isCancelled) continue;
const existing = contractBets.get(bet.contractId);
if (existing) {
existing.totalShares += bet.shares;
existing.totalAmount += bet.amount;
existing.count++;
} else {
contractBets.set(bet.contractId, {
outcome: bet.outcome,
totalShares: bet.shares,
totalAmount: bet.amount,
count: 1,
});
}
}
const lines = [`**Manifold Positions** (${me.username})`, ''];
for (const [contractId, pos] of contractBets) {
if (Math.abs(pos.totalShares) < 0.01) continue; // Skip near-zero positions
lines.push(` [${contractId}]`);
lines.push(` ${pos.outcome}: ${formatNumber(pos.totalShares)} shares | Cost: M$${formatNumber(pos.totalAmount)} (${pos.count} bet(s))`);
}
if (lines.length <= 2) {
return 'No active positions found.';
}
return lines.join('\n');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return `Error: ${message}`;
}
}
async function handleBalance(): Promise<string> {
const apiKey = getApiKey();
if (!apiKey) {
return 'Set MANIFOLD_API_KEY to check balance.';
}
try {
const response = await fetch(`${MANIFOLD_API}/me`, { headers: authHeaders() });
if (!response.ok) {
return `Failed to fetch balance: HTTP ${response.status}`;
}
const data = await response.json() as {
balance?: number;
totalDeposits?: number;
username?: string;
name?: string;
profitCached?: { allTime?: number; monthly?: number; weekly?: number; daily?: number };
};
const lines = [
`**Manifold Balance** (${data.name || data.username || 'unknown'})`,
'',
`Mana: M$${formatNumber(data.balance ?? 0)}`,
];
if (data.totalDeposits !== undefined) {
lines.push(`Total Deposits: M$${formatNumber(data.totalDeposits)}`);
}
if (data.profitCached) {
lines.push('');
lines.push('**Profit:**');
if (data.profitCached.daily !== undefined) lines.push(` Today: M$${formatNumber(data.profitCached.daily)}`);
if (data.profitCached.weekly !== undefined) lines.push(` Week: M$${formatNumber(data.profitCached.weekly)}`);
if (data.profitCached.monthly !== undefined) lines.push(` Month: M$${formatNumber(data.profitCached.monthly)}`);
if (data.profitCached.allTime !== undefined) lines.push(` All Time: M$${formatNumber(data.profitCached.allTime)}`);
}
return lines.join('\n');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return `Error: ${message}`;
}
}
// =============================================================================
// MAIN HANDLER
// =============================================================================
async function execute(args: string): Promise<string> {
const parts = args.trim().split(/\s+/);
const cmd = parts[0]?.toLowerCase() || 'help';
try {
switch (cmd) {
case 'search':
case 's':
return handleSearch(parts.slice(1).join(' '));
case 'market':
case 'm':
return handleMarket(parts.slice(1).join(' '));
case 'bet':
case 'buy':
case 'b':
return handleBet(parts[1], parts[2], parts[3]);
case 'positions':
case 'pos':
case 'portfolio':
case 'p':
return handlePositions();
case 'balance':
case 'bal':
return handleBalance();
case 'trending':
case 't':
return handleTrending();
case 'help':
default:
return helpText();
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
logger.error({ error: message, args }, 'Manifold command failed');
return `Error: ${message}`;
}
}
export default {
name: 'trading-manifold',
description: 'Manifold Markets trading - search, bet, and track positions',
commands: ['/manifold', '/trading-manifold'],
handle: execute,
};
Related skills
FAQ
What currency does Manifold use?
Mana, described as play money that can be donated to charity.
How is a bet authenticated?
With a MANIFOLD_API_KEY sent as an Authorization Key header.