
Smarkets
- 13 installs
- 610 repo stars
- Updated June 26, 2026
- alsk1992/cloddsbot
Smarkets is a Claude Code skill for the clodds bot that integrates the Smarkets betting exchange for market data and order placement.
About
Smarkets is a clodds skill that integrates the Smarkets betting exchange for politics, sports, and entertainment markets. Developers use it to search markets, view prices and orderbooks, place buy and sell orders, and check balance via chat commands. It matters for accessing exchange betting at lower fees than Betfair. Requires a SMARKETS_SESSION_TOKEN.
- Full Smarkets betting-exchange integration with 2% commission vs Betfair's 5%
- Market search, prices, orderbook, buy/sell, cancel, and balance commands
- Politics, sports, and entertainment markets with real-time WebSocket price updates
Smarkets by the numbers
- 13 all-time installs (skills.sh)
- Ranked #759 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
smarkets capabilities & compatibility
- Capabilities
- betting exchange · order placement · market data
- Use cases
- trading
- Pricing
- Bring your own API key
What smarkets says it does
Full integration with Smarkets, a betting exchange with lower fees than Betfair (2% vs 5%). Politics, sports, and entertainment markets.
npx skills add https://github.com/alsk1992/cloddsbot --skill smarketsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 13 |
|---|---|
| repo stars | ★ 610 |
| Last updated | June 26, 2026 |
| Repository | alsk1992/cloddsbot ↗ |
What it does
Search Smarkets markets and place buy or sell exchange bets on politics, sports, or entertainment.
Who is it for?
Placing exchange bets on Smarkets politics, sports, and entertainment markets at 2% commission.
Skip if: Crypto or DeFi trading, or exchanges other than Smarkets.
When should I use this skill?
You want to search Smarkets markets or place a buy/sell order on the exchange.
What you get
You can search markets, read prices and orderbooks, and place or cancel orders on Smarkets.
- market data
- placed orders
- account balance
By the numbers
- 2% commission vs Betfair 5%
- 5 market domains (politics, sport, entertainment, current_affairs, esports)
Files
Smarkets Exchange
Full integration with Smarkets, a betting exchange with lower fees than Betfair (2% vs 5%). Politics, sports, and entertainment markets.
Quick Start
# Set credentials
export SMARKETS_SESSION_TOKEN="your-session-token"
# Search markets
/sm markets election
# Get market prices
/sm prices 12345
# Place buy order
/sm buy 12345 67890 0.55 10
# Place sell order
/sm sell 12345 67890 0.60 10Commands
Market Data
| Command | Description |
|---|---|
/sm markets [query] | Search markets |
/sm market <id> | Get market details |
/sm prices <marketId> | Current prices |
/sm book <marketId> <contractId> | Show orderbook |
Trading
| Command | Description |
|---|---|
/sm buy <marketId> <contractId> <price> <quantity> | Place buy order |
/sm sell <marketId> <contractId> <price> <quantity> | Place sell order |
/sm cancel <orderId> | Cancel order |
/sm cancelall [marketId] | Cancel all orders |
/sm orders [marketId] | List open orders |
Examples:
/sm buy 123 456 0.55 10 # Buy at 55% price, £10
/sm sell 123 456 0.60 10 # Sell at 60% price, £10
/sm cancel abc123 # Cancel specific orderAccount
| Command | Description |
|---|---|
/sm balance | Check account balance |
Configuration
# Required for trading
export SMARKETS_SESSION_TOKEN="your-session-token"
# Optional - API token for market data only
export SMARKETS_API_TOKEN="your-api-token"Features
- Low Fees - 2% commission vs Betfair's 5%
- Politics Markets - UK, US, EU elections
- Sports - Football, tennis, horse racing
- Entertainment - TV, music, awards
- Real-time Streaming - WebSocket price updates
Market Types
| Domain | Examples |
|---|---|
| politics | Elections, referendums |
| sport | Football, tennis, cricket |
| entertainment | Oscars, Eurovision, reality TV |
| current_affairs | Economic events, news |
| esports | CS:GO, League of Legends |
Trading Notes
1. Prices: Expressed as percentages (0.55 = 55%) 2. Quantities: In GBP 3. Buy: Betting FOR an outcome 4. Sell: Betting AGAINST an outcome
Resources
/**
* Smarkets CLI Skill
*
* Commands:
* /sm markets [query] - Search markets
* /sm market <id> - Get market details
* /sm prices <marketId> - Show current prices
* /sm book <marketId> <contractId> - Show orderbook
* /sm buy <marketId> <contractId> <price> <quantity> - Place buy order
* /sm sell <marketId> <contractId> <price> <quantity> - Place sell order
* /sm cancel <orderId> - Cancel order
* /sm cancelall [marketId] - Cancel all orders
* /sm orders [marketId] - List open orders
* /sm balance - Check account balance
*/
import { createSmarketsFeed, SmarketsFeed, SMARKETS_DOMAINS } from '../../../feeds/smarkets/index';
import { logger } from '../../../utils/logger';
let feed: SmarketsFeed | null = null;
async function getFeed(): Promise<SmarketsFeed | null> {
if (feed) return feed;
const sessionToken = process.env.SMARKETS_SESSION_TOKEN;
const apiToken = process.env.SMARKETS_API_TOKEN;
if (!sessionToken && !apiToken) {
return null;
}
try {
feed = await createSmarketsFeed({ sessionToken, apiToken });
await feed.start();
return feed;
} catch (error) {
logger.error({ error }, 'Failed to initialize Smarkets feed');
return null;
}
}
async function handleMarkets(query: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN or SMARKETS_API_TOKEN.';
try {
const markets = await f.searchMarkets(query || '');
if (markets.length === 0) {
return 'No markets found.';
}
let output = `**Smarkets Markets** (${markets.length} results)\n\n`;
for (const market of markets.slice(0, 20)) {
output += `**${market.question}**\n`;
output += ` ID: \`${market.id}\`\n`;
output += ` Volume: £${market.volume24h.toLocaleString()}\n`;
if (market.outcomes.length > 0) {
output += ` Outcomes:\n`;
for (const o of market.outcomes.slice(0, 4)) {
output += ` - ${o.name}: ${(o.price * 100).toFixed(1)}%\n`;
}
}
output += '\n';
}
return output;
} catch (error) {
return `Error searching markets: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handleMarket(marketId: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN or SMARKETS_API_TOKEN.';
try {
const market = await f.getMarket(marketId);
if (!market) {
return `Market ${marketId} not found.`;
}
let output = `**${market.question}**\n\n`;
output += `ID: \`${market.id}\`\n`;
output += `Volume: £${market.volume24h.toLocaleString()}\n`;
if (market.endDate) {
output += `End: ${market.endDate.toLocaleString()}\n`;
}
output += `Status: ${market.resolved ? 'Settled' : 'Open'}\n`;
output += `Tags: ${market.tags.join(', ')}\n\n`;
output += `**Contracts:**\n`;
for (const o of market.outcomes) {
output += `- **${o.name}** (ID: ${o.id})\n`;
output += ` Price: ${(o.price * 100).toFixed(1)}% | Volume: £${(o.volume24h ?? 0).toLocaleString()}\n`;
}
return output;
} catch (error) {
return `Error fetching market: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handlePrices(marketId: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN or SMARKETS_API_TOKEN.';
try {
const quotes = await f.getQuotes(marketId);
if (quotes.length === 0) {
return `No quotes for market ${marketId}.`;
}
let output = `**Market Prices: ${marketId}**\n\n`;
for (const quote of quotes) {
output += `**Contract ${quote.contract_id}**\n`;
if (quote.bids?.[0]) {
output += ` Best Bid: ${quote.bids[0].price}% (£${(quote.bids[0].quantity / 100).toFixed(2)})\n`;
}
if (quote.offers?.[0]) {
output += ` Best Offer: ${quote.offers[0].price}% (£${(quote.offers[0].quantity / 100).toFixed(2)})\n`;
}
if (quote.last_executed_price) {
output += ` Last Traded: ${quote.last_executed_price}%\n`;
}
if (quote.volume) {
output += ` Volume: £${(quote.volume / 100).toLocaleString()}\n`;
}
output += '\n';
}
return output;
} catch (error) {
return `Error fetching prices: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handleBook(marketId: string, contractId: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN or SMARKETS_API_TOKEN.';
try {
const orderbook = await f.getOrderbook(marketId, contractId);
if (!orderbook) {
return `Orderbook not found for ${marketId}/${contractId}.`;
}
let output = `**Orderbook: ${marketId} / ${contractId}**\n\n`;
output += `Spread: ${(orderbook.spread * 100).toFixed(2)}%\n`;
output += `Mid Price: ${(orderbook.midPrice * 100).toFixed(1)}%\n\n`;
output += `**Bids (Buy):**\n`;
for (const [price, size] of orderbook.bids.slice(0, 5)) {
output += ` ${(price * 100).toFixed(1)}% - £${size.toFixed(2)}\n`;
}
output += `\n**Offers (Sell):**\n`;
for (const [price, size] of orderbook.asks.slice(0, 5)) {
output += ` ${(price * 100).toFixed(1)}% - £${size.toFixed(2)}\n`;
}
return output;
} catch (error) {
return `Error fetching orderbook: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handleBuy(marketId: string, contractId: string, price: string, quantity: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN.';
if (!f.isAuthenticated()) return 'Smarkets: Session token required for trading.';
try {
const priceNum = parseFloat(price);
const quantityNum = parseFloat(quantity);
if (isNaN(priceNum) || isNaN(quantityNum) || priceNum <= 0 || quantityNum <= 0) {
return 'Invalid price or quantity. Both must be positive numbers.';
}
// Circuit breaker pre-trade check
try {
const { getGlobalCircuitBreaker } = await import('../../../execution/circuit-breaker');
const cb = getGlobalCircuitBreaker();
if (!cb.canTrade()) {
const state = cb.getState();
return `**Trade blocked** — Circuit breaker tripped: ${state.tripReason || 'unknown'}\nUse \`/risk reset\` to re-arm.`;
}
} catch { /* circuit breaker non-critical */ }
const result = await f.placeBuyOrder(marketId, contractId, priceNum, quantityNum);
// Circuit breaker post-trade recording
try {
const { getGlobalCircuitBreaker } = await import('../../../execution/circuit-breaker');
const cb = getGlobalCircuitBreaker();
cb.recordTrade({ pnlUsd: 0, success: !!result, sizeUsd: quantityNum / 100, error: result ? undefined : 'Order failed' });
} catch { /* circuit breaker non-critical */ }
if (!result) {
return 'Failed to place buy order.';
}
// Position tracking
try {
const { getGlobalPositionManager } = await import('../../../execution/position-manager');
const pm = getGlobalPositionManager();
pm.updatePosition({
platform: 'smarkets' as any,
marketId,
tokenId: contractId,
outcomeName: `Contract ${contractId}`,
side: 'long',
size: quantityNum / 100,
entryPrice: priceNum,
currentPrice: priceNum,
openedAt: new Date(),
});
} catch { /* position tracking non-critical */ }
return `**Buy Order Placed**\n\n` +
`Order ID: \`${result.id}\`\n` +
`Market: ${result.market_id}\n` +
`Contract: ${result.contract_id}\n` +
`Price: ${result.price}%\n` +
`Quantity: £${(result.quantity / 100).toFixed(2)}\n` +
`Status: ${result.state}`;
} catch (error) {
return `Error placing buy order: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handleSell(marketId: string, contractId: string, price: string, quantity: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN.';
if (!f.isAuthenticated()) return 'Smarkets: Session token required for trading.';
try {
const priceNum = parseFloat(price);
const quantityNum = parseFloat(quantity);
if (isNaN(priceNum) || isNaN(quantityNum) || priceNum <= 0 || quantityNum <= 0) {
return 'Invalid price or quantity. Both must be positive numbers.';
}
// Circuit breaker pre-trade check
try {
const { getGlobalCircuitBreaker } = await import('../../../execution/circuit-breaker');
const cb = getGlobalCircuitBreaker();
if (!cb.canTrade()) {
const state = cb.getState();
return `**Trade blocked** — Circuit breaker tripped: ${state.tripReason || 'unknown'}\nUse \`/risk reset\` to re-arm.`;
}
} catch { /* circuit breaker non-critical */ }
const result = await f.placeSellOrder(marketId, contractId, priceNum, quantityNum);
// Circuit breaker post-trade recording
try {
const { getGlobalCircuitBreaker } = await import('../../../execution/circuit-breaker');
const cb = getGlobalCircuitBreaker();
cb.recordTrade({ pnlUsd: 0, success: !!result, sizeUsd: quantityNum / 100, error: result ? undefined : 'Order failed' });
} catch { /* circuit breaker non-critical */ }
if (!result) {
return 'Failed to place sell order.';
}
// Position tracking - close existing position on sell
try {
const { getGlobalPositionManager } = await import('../../../execution/position-manager');
const pm = getGlobalPositionManager();
const existing = pm.getPositionsByPlatform('smarkets' as any)
.find(p => p.tokenId === contractId && p.status === 'open');
if (existing) {
pm.closePosition(existing.id, priceNum, 'manual');
}
} catch { /* position tracking non-critical */ }
return `**Sell Order Placed**\n\n` +
`Order ID: \`${result.id}\`\n` +
`Market: ${result.market_id}\n` +
`Contract: ${result.contract_id}\n` +
`Price: ${result.price}%\n` +
`Quantity: £${(result.quantity / 100).toFixed(2)}\n` +
`Status: ${result.state}`;
} catch (error) {
return `Error placing sell order: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handleCancel(orderId: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN.';
if (!f.isAuthenticated()) return 'Smarkets: Session token required for trading.';
try {
const success = await f.cancelOrder(orderId);
return success
? `Order ${orderId} cancelled successfully.`
: `Failed to cancel order ${orderId}.`;
} catch (error) {
return `Error cancelling order: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handleCancelAll(marketId?: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN.';
if (!f.isAuthenticated()) return 'Smarkets: Session token required for trading.';
try {
const count = await f.cancelAllOrders(marketId);
return `Cancelled ${count} orders.`;
} catch (error) {
return `Error cancelling orders: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handleOrders(marketId?: string): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN.';
if (!f.isAuthenticated()) return 'Smarkets: Session token required for trading.';
try {
const orders = await f.getOpenOrders(marketId);
if (orders.length === 0) {
return 'No open orders.';
}
let output = `**Open Orders** (${orders.length})\n\n`;
for (const order of orders) {
output += `**${order.id}**\n`;
output += ` Market: ${order.market_id}\n`;
output += ` Contract: ${order.contract_id}\n`;
output += ` Side: ${order.side.toUpperCase()}\n`;
output += ` Price: ${order.price}%\n`;
output += ` Quantity: £${(order.quantity / 100).toFixed(2)}\n`;
output += ` Filled: £${(order.quantity_filled / 100).toFixed(2)}\n`;
output += ` Status: ${order.state}\n\n`;
}
return output;
} catch (error) {
return `Error fetching orders: ${error instanceof Error ? error.message : String(error)}`;
}
}
async function handleBalance(): Promise<string> {
const f = await getFeed();
if (!f) return 'Smarkets not configured. Set SMARKETS_SESSION_TOKEN.';
if (!f.isAuthenticated()) return 'Smarkets: Session token required.';
try {
const funds = await f.getBalance();
return `**Smarkets Account**\n\n` +
`Total: £${funds.total.toLocaleString()}\n` +
`Available: £${funds.available.toLocaleString()}\n` +
`Exposure: £${funds.exposure.toLocaleString()}`;
} catch (error) {
return `Error fetching balance: ${error instanceof Error ? error.message : String(error)}`;
}
}
export async function execute(args: string): Promise<string> {
const parts = args.trim().split(/\s+/);
const command = parts[0]?.toLowerCase() || 'help';
const rest = parts.slice(1);
switch (command) {
case 'markets':
case 'search':
return handleMarkets(rest.join(' '));
case 'market':
if (!rest[0]) return 'Usage: /sm market <marketId>';
return handleMarket(rest[0]);
case 'prices':
case 'price':
if (!rest[0]) return 'Usage: /sm prices <marketId>';
return handlePrices(rest[0]);
case 'book':
case 'orderbook':
if (!rest[0] || !rest[1]) return 'Usage: /sm book <marketId> <contractId>';
return handleBook(rest[0], rest[1]);
case 'buy':
if (rest.length < 4) return 'Usage: /sm buy <marketId> <contractId> <price> <quantity>';
return handleBuy(rest[0], rest[1], rest[2], rest[3]);
case 'sell':
if (rest.length < 4) return 'Usage: /sm sell <marketId> <contractId> <price> <quantity>';
return handleSell(rest[0], rest[1], rest[2], rest[3]);
case 'cancel':
if (!rest[0]) return 'Usage: /sm cancel <orderId>';
return handleCancel(rest[0]);
case 'cancelall':
return handleCancelAll(rest[0]);
case 'orders':
return handleOrders(rest[0]);
case 'balance':
return handleBalance();
case 'circuit': {
try {
const { getGlobalCircuitBreaker } = await import('../../../execution/circuit-breaker');
const cb = getGlobalCircuitBreaker();
const state = cb.getState();
return `**Circuit Breaker**\n\n` +
`Status: ${state.isTripped ? 'TRIPPED' : 'Armed'}\n` +
`Session PnL: $${state.sessionPnL.toFixed(2)}\n` +
`Daily trades: ${state.dailyTrades}\n` +
`Consecutive losses: ${state.consecutiveLosses}\n` +
`Error rate: ${(state.errorRate * 100).toFixed(0)}%\n` +
(state.tripReason ? `Trip reason: ${state.tripReason}\n` : '') +
`\nUse \`/risk trip\` / \`/risk reset\` to manually control.`;
} catch (e) {
return `Circuit breaker error: ${e instanceof Error ? e.message : String(e)}`;
}
}
case 'help':
default:
return `**Smarkets Exchange Commands**
**Market Data:**
/sm markets [query] - Search markets
/sm market <id> - Get market details
/sm prices <id> - Current prices
/sm book <id> <contract> - Show orderbook
**Trading:**
/sm buy <id> <cont> <price> <qty> - Place buy order
/sm sell <id> <cont> <price> <qty> - Place sell order
/sm cancel <orderId> - Cancel order
/sm cancelall [id] - Cancel all orders
/sm orders [id] - List open orders
**Account:**
/sm balance - Check balance
**Risk:**
/sm circuit - Circuit breaker status
**Examples:**
/sm markets uk election
/sm buy 12345 67890 0.55 10
/sm sell 12345 67890 0.60 10`;
}
}
export default {
name: 'smarkets',
description: 'Smarkets exchange - search markets, trade, manage orders, and check balances',
commands: ['/smarkets', '/sm'],
handle: execute,
};
Related skills
FAQ
Why use Smarkets over Betfair?
The docs cite 2% commission versus Betfair's 5%, with politics, sports, and entertainment markets.
What is required to trade?
A SMARKETS_SESSION_TOKEN environment variable; an optional SMARKETS_API_TOKEN covers market data only.