
Backtest
- 15 installs
- 610 repo stars
- Updated June 26, 2026
- alsk1992/cloddsbot
Backtest is a Claude Code skill that validates trading strategies on historical data using walk-forward analysis and Monte Carlo simulation.
About
Backtest is a skill that validates trading strategies against historical data. It runs basic backtests, walk-forward out-of-sample analysis, and Monte Carlo simulation with configurable fees and slippage, and reports metrics like Sharpe ratio, max drawdown, win rate, and profit factor. A developer uses it to check whether a strategy holds up before risking capital.
- Backtests trading strategies on historical prediction-market data
- Runs walk-forward analysis and Monte Carlo simulation to catch overfitting
- Reports Sharpe, Sortino, max drawdown, win rate, and profit factor
Backtest by the numbers
- 15 all-time installs (skills.sh)
- Ranked #745 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
backtest capabilities & compatibility
- Capabilities
- backtesting · walk forward analysis · monte carlo simulation
- Use cases
- trading · data analysis
- Runs
- Runs locally
What backtest says it does
Validate trading strategies using historical data, walk-forward analysis, and Monte Carlo simulation.
Use walk-forward** — Avoid overfitting
npx skills add https://github.com/alsk1992/cloddsbot --skill backtestAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 15 |
|---|---|
| repo stars | ★ 610 |
| Last updated | June 26, 2026 |
| Repository | alsk1992/cloddsbot ↗ |
What it does
Backtest trading strategies on historical data with walk-forward and Monte Carlo validation.
Who is it for?
validating a trading strategy before risking real capital
Skip if: live trade execution or real-time monitoring
When should I use this skill?
you want to test a trading strategy on historical data before going live
What you get
Risk-adjusted performance metrics and out-of-sample validation showing whether a strategy holds up.
- backtest performance report
- walk-forward validation results
- Monte Carlo distribution
By the numbers
- 5 built-in strategies
- 10000-simulation Monte Carlo default
Files
Backtest - Complete API Reference
Validate trading strategies using historical data, walk-forward analysis, and Monte Carlo simulation.
---
Chat Commands
Run Backtest
/backtest momentum --from 2024-01-01 --to 2024-12-31
/backtest mean-reversion --market "Trump 2028" --days 90
/backtest my-strategy --capital 10000Quick Stats
/backtest stats momentum Show strategy metrics
/backtest compare momentum arb Compare two strategies
/backtest monte-carlo momentum Run Monte Carlo simulationResults
/backtest results Show recent results
/backtest stats Alias for results
/backtest results <id> --detailed Detailed breakdown
/backtest export Export last results as CSV---
TypeScript API Reference
Create Backtest Engine
import { createBacktestEngine } from 'clodds/backtest';
const backtest = createBacktestEngine({
// Data source
dataSource: 'polymarket', // or custom data provider
// Capital
initialCapital: 10000,
// Fees (Polymarket: 0% on most markets; Kalshi: ~1.2% avg)
fees: {
maker: 0, // 0% maker fee (Polymarket most markets)
taker: 0, // 0% taker fee (Polymarket most markets)
// For 15-min crypto markets or Kalshi, use: taker: 0.012
},
// Slippage model
slippageModel: 'realistic', // 'none' | 'fixed' | 'realistic'
slippageBps: 10,
});Run Basic Backtest
const result = await backtest.run({
strategy: 'momentum',
startDate: '2024-01-01',
endDate: '2024-12-31',
parameters: {
lookbackPeriod: 14,
entryThreshold: 0.02,
exitThreshold: 0.01,
},
});
console.log(`Total Return: ${result.totalReturn}%`);
console.log(`Sharpe Ratio: ${result.sharpeRatio}`);
console.log(`Max Drawdown: ${result.maxDrawdown}%`);
console.log(`Win Rate: ${result.winRate}%`);
console.log(`Profit Factor: ${result.profitFactor}`);Walk-Forward Analysis
// Out-of-sample validation
const wf = await backtest.walkForward({
strategy: 'momentum',
startDate: '2023-01-01',
endDate: '2024-12-31',
// Train/test split
trainPeriod: '6M',
testPeriod: '1M',
step: '1M',
// Optimization
optimize: ['lookbackPeriod', 'entryThreshold'],
optimizationMetric: 'sharpe',
});
console.log(`In-Sample Sharpe: ${wf.inSampleSharpe}`);
console.log(`Out-of-Sample Sharpe: ${wf.outOfSampleSharpe}`);
console.log(`Overfitting Ratio: ${wf.overfitRatio}`);Monte Carlo Simulation
// Stress test with randomization
const mc = await backtest.monteCarlo({
strategy: 'momentum',
trades: historicalTrades,
// Simulation settings
simulations: 10000,
confidenceLevel: 0.95,
// Randomization
shuffleTrades: true,
randomizeReturns: true,
});
console.log(`Expected Return: ${mc.expectedReturn}%`);
console.log(`95% VaR: ${mc.valueAtRisk}%`);
console.log(`Worst Case: ${mc.worstCase}%`);
console.log(`Best Case: ${mc.bestCase}%`);
console.log(`Probability of Profit: ${mc.probProfit}%`);Performance Metrics
const metrics = await backtest.getMetrics(result);
console.log('=== Performance ===');
console.log(`Total Return: ${metrics.totalReturn}%`);
console.log(`CAGR: ${metrics.cagr}%`);
console.log(`Volatility: ${metrics.volatility}%`);
console.log('=== Risk ===');
console.log(`Sharpe Ratio: ${metrics.sharpeRatio}`);
console.log(`Sortino Ratio: ${metrics.sortinoRatio}`);
console.log(`Max Drawdown: ${metrics.maxDrawdown}%`);
console.log(`Max Drawdown Duration: ${metrics.maxDrawdownDuration} days`);
console.log('=== Trading ===');
console.log(`Total Trades: ${metrics.totalTrades}`);
console.log(`Win Rate: ${metrics.winRate}%`);
console.log(`Profit Factor: ${metrics.profitFactor}`);
console.log(`Avg Win: ${metrics.avgWin}%`);
console.log(`Avg Loss: ${metrics.avgLoss}%`);
console.log(`Expectancy: ${metrics.expectancy}%`);Custom Strategy
// Define custom strategy
const myStrategy = {
name: 'my-strategy',
onData: async (data, context) => {
const price = data.price;
const sma = data.indicators.sma(20);
if (price < sma * 0.95 && !context.hasPosition) {
return { action: 'buy', size: context.availableCapital * 0.1 };
}
if (price > sma * 1.05 && context.hasPosition) {
return { action: 'sell', size: 'all' };
}
return { action: 'hold' };
},
};
const result = await backtest.run({
strategy: myStrategy,
startDate: '2024-01-01',
endDate: '2024-12-31',
});---
Built-in Strategies
| Strategy | Description |
|---|---|
momentum | Follow price trends |
mean-reversion | Buy dips, sell rallies |
arbitrage | Cross-platform price differences |
breakout | Enter on range breakouts |
pairs | Correlated market pairs |
---
Metrics Explained
| Metric | Good Value | Description |
|---|---|---|
| Sharpe Ratio | > 1.0 | Risk-adjusted return |
| Sortino Ratio | > 1.5 | Downside-adjusted return |
| Max Drawdown | < 20% | Worst peak-to-trough |
| Win Rate | > 50% | Winning trades % |
| Profit Factor | > 1.5 | Gross profit / gross loss |
| Expectancy | > 0 | Expected $ per trade |
---
Best Practices
1. Use walk-forward — Avoid overfitting 2. Include fees — Realistic cost modeling 3. Test multiple periods — Don't cherry-pick dates 4. Monte Carlo — Understand variance 5. Out-of-sample — Always validate on unseen data
/**
* Backtest CLI Skill
*
* Commands:
* /backtest run <strategy> --market <id> - Run backtest
* /backtest results - Show last results
* /backtest compare <s1> <s2> - Compare strategies
* /backtest monte-carlo - Run Monte Carlo simulation
* /backtest list - List saved runs
*/
// Session storage for backtest results
const sessionResults: Array<{ id: string; result: any }> = [];
async function execute(args: string): Promise<string> {
const parts = args.trim().split(/\s+/);
const cmd = parts[0]?.toLowerCase() || 'help';
try {
const backtestMod = await import('../../../trading/backtest');
const { createDatabase } = await import('../../../db/index');
const db = createDatabase();
const engine = backtestMod.createBacktestEngine(db);
switch (cmd) {
case 'run': {
const strategyName = parts[1];
if (!strategyName) return 'Usage: /backtest run <strategy> --market <id> [--days <n>] [--capital <n>]\n\nStrategies: momentum, reversion, divergence, trend-follow, mean-revert';
const marketIdx = parts.indexOf('--market');
const marketId = marketIdx >= 0 ? parts[marketIdx + 1] : undefined;
if (!marketId) return 'Market ID required. Usage: /backtest run <strategy> --market <id>';
const daysIdx = parts.indexOf('--days');
const days = daysIdx >= 0 ? parseInt(parts[daysIdx + 1], 10) : 30;
const capitalIdx = parts.indexOf('--capital');
const capital = capitalIdx >= 0 ? parseFloat(parts[capitalIdx + 1]) : 10000;
const config: any = {
startDate: new Date(Date.now() - days * 24 * 60 * 60 * 1000),
endDate: new Date(),
initialCapital: capital,
commissionPct: 0.1,
slippagePct: 0.05,
resolutionMs: 60 * 60 * 1000,
riskFreeRate: 5,
};
// Load historical data
const bars = await engine.loadHistoricalData('polymarket', marketId, config.startDate, config.endDate);
if (!bars.length) return `No historical data found for market \`${marketId}\` in last ${days} days.`;
// Build a simple strategy to run
const { createMeanReversionStrategy, createMomentumStrategy } = await import('../../../trading/index') as any;
let strategy: any;
try {
if (strategyName === 'momentum' && createMomentumStrategy) {
strategy = createMomentumStrategy();
} else if (createMeanReversionStrategy) {
strategy = createMeanReversionStrategy();
}
} catch { /* fallback below */ }
if (!strategy) {
// If no built-in strategy factory, show data summary
let output = `**Backtest: ${strategyName}**\n\n`;
output += `Market: ${marketId}\n`;
output += `Period: ${days} days (${config.startDate.toLocaleDateString()} - ${config.endDate.toLocaleDateString()})\n`;
output += `Initial capital: $${capital.toFixed(2)}\n`;
output += `Data points: ${bars.length} bars\n`;
output += `\nNo strategy factory found for "${strategyName}". Available built-in: momentum, reversion.`;
return output;
}
// Ensure strategy config has the market
if (strategy.config) {
strategy.config.markets = strategy.config.markets || [];
if (!strategy.config.markets.includes(marketId)) {
strategy.config.markets.push(marketId);
}
strategy.config.platforms = strategy.config.platforms || ['polymarket'];
}
const data = new Map<string, typeof bars>();
data.set(`polymarket:${marketId}`, bars);
const result = await engine.runWithData(strategy, config, data);
// Store in session
const runId = `bt-${Date.now()}`;
sessionResults.push({ id: runId, result });
const m = result.metrics;
let output = `**Backtest: ${strategyName}**\n\n`;
output += `Market: ${marketId}\n`;
output += `Period: ${days} days | Bars: ${bars.length}\n`;
output += `Initial: $${capital.toFixed(2)} | Final: $${m.finalEquity.toFixed(2)}\n\n`;
output += `**Results:**\n`;
output += ` Return: ${m.totalReturnPct.toFixed(2)}% (annualized: ${m.annualizedReturnPct.toFixed(2)}%)\n`;
output += ` Trades: ${m.totalTrades} | Win Rate: ${m.winRate.toFixed(1)}%\n`;
output += ` Profit Factor: ${m.profitFactor.toFixed(2)}\n`;
output += ` Avg Trade: ${m.avgTradePct.toFixed(2)}% | Avg Win: ${m.avgWinPct.toFixed(2)}% | Avg Loss: ${m.avgLossPct.toFixed(2)}%\n`;
output += ` Max Drawdown: ${m.maxDrawdownPct.toFixed(2)}% (${m.maxDrawdownDays.toFixed(0)} days)\n`;
output += ` Sharpe: ${m.sharpeRatio.toFixed(2)} | Sortino: ${m.sortinoRatio.toFixed(2)} | Calmar: ${m.calmarRatio.toFixed(2)}\n`;
output += ` Commission: $${m.totalCommission.toFixed(2)} | Slippage: $${m.totalSlippage.toFixed(2)}\n`;
output += `\nRun ID: \`${runId}\``;
return output;
}
case 'stats':
case 'results':
case 'last': {
if (sessionResults.length === 0) {
return 'No backtest results in current session. Run one with `/backtest run <strategy> --market <id>`.';
}
const last = sessionResults[sessionResults.length - 1];
const m = last.result.metrics;
let output = `**Last Backtest Result** (\`${last.id}\`)\n\n`;
output += `Return: ${m.totalReturnPct.toFixed(2)}% | Trades: ${m.totalTrades} | Win Rate: ${m.winRate.toFixed(1)}%\n`;
output += `Final Equity: $${m.finalEquity.toFixed(2)} | Sharpe: ${m.sharpeRatio.toFixed(2)}\n`;
output += `Max Drawdown: ${m.maxDrawdownPct.toFixed(2)}% | Profit Factor: ${m.profitFactor.toFixed(2)}\n`;
output += `Trades in result: ${last.result.trades.length}\n`;
if (last.result.trades.length > 0) {
output += `\n**Last 5 Trades:**\n`;
for (const t of last.result.trades.slice(-5)) {
const pnl = t.pnl !== undefined ? ` PnL: $${t.pnl.toFixed(2)}` : '';
output += ` ${t.side.toUpperCase()} ${t.size} @ ${t.price.toFixed(4)}${pnl}\n`;
}
}
return output;
}
case 'monte-carlo':
case 'mc': {
if (sessionResults.length === 0) {
return 'Run a backtest first with `/backtest run <strategy> --market <id>`, then use Monte Carlo.';
}
const sims = parts[1] ? parseInt(parts[1], 10) : 1000;
if (isNaN(sims) || sims < 10) return 'Usage: /backtest monte-carlo [num-sims]\n\nDefault: 1000 simulations. Min: 10.';
const last = sessionResults[sessionResults.length - 1];
const mc = engine.monteCarlo(last.result, sims);
let output = `**Monte Carlo Simulation** (${mc.simulations} runs)\n\n`;
output += `Based on last backtest: \`${last.id}\`\n\n`;
output += `**Return Percentiles:**\n`;
output += ` 5th: ${mc.percentiles.p5.toFixed(2)}%\n`;
output += ` 25th: ${mc.percentiles.p25.toFixed(2)}%\n`;
output += ` 50th (median): ${mc.percentiles.p50.toFixed(2)}%\n`;
output += ` 75th: ${mc.percentiles.p75.toFixed(2)}%\n`;
output += ` 95th: ${mc.percentiles.p95.toFixed(2)}%\n\n`;
output += `Probability of profit: ${(mc.probabilityOfProfit * 100).toFixed(1)}%\n`;
output += `Probability of >20% loss: ${(mc.probabilityOfMajorLoss * 100).toFixed(1)}%\n`;
output += `Expected value: ${mc.expectedValue.toFixed(2)}%`;
return output;
}
case 'compare': {
if (parts.length < 3) return 'Usage: /backtest compare <strategy1> <strategy2> [--market <id>]';
const s1Name = parts[1];
const s2Name = parts[2];
// Find matching results in session
const r1 = sessionResults.find(r => r.id.includes(s1Name) || r.result.strategyId === s1Name);
const r2 = sessionResults.find(r => r.id.includes(s2Name) || r.result.strategyId === s2Name);
if (!r1 || !r2) {
return `Run backtests for both strategies first.\nSession has ${sessionResults.length} results: ${sessionResults.map(r => r.id).join(', ') || 'none'}`;
}
const m1 = r1.result.metrics;
const m2 = r2.result.metrics;
let output = `**Strategy Comparison**\n\n`;
output += `| Metric | ${s1Name} | ${s2Name} |\n`;
output += `|--------|--------|--------|\n`;
output += `| Return | ${m1.totalReturnPct.toFixed(2)}% | ${m2.totalReturnPct.toFixed(2)}% |\n`;
output += `| Win Rate | ${m1.winRate.toFixed(1)}% | ${m2.winRate.toFixed(1)}% |\n`;
output += `| Sharpe | ${m1.sharpeRatio.toFixed(2)} | ${m2.sharpeRatio.toFixed(2)} |\n`;
output += `| Max DD | ${m1.maxDrawdownPct.toFixed(2)}% | ${m2.maxDrawdownPct.toFixed(2)}% |\n`;
output += `| Profit Factor | ${m1.profitFactor.toFixed(2)} | ${m2.profitFactor.toFixed(2)} |\n`;
output += `| Trades | ${m1.totalTrades} | ${m2.totalTrades} |`;
return output;
}
case 'list':
case 'ls': {
if (sessionResults.length === 0) {
return 'No backtest runs in current session. Run one with `/backtest run <strategy> --market <id>`.';
}
let output = `**Saved Backtest Runs** (${sessionResults.length})\n\n`;
for (const r of sessionResults) {
const m = r.result.metrics;
output += `\`${r.id}\` - Return: ${m.totalReturnPct.toFixed(2)}% | Trades: ${m.totalTrades} | Sharpe: ${m.sharpeRatio.toFixed(2)}\n`;
}
return output;
}
case 'export': {
if (sessionResults.length === 0) {
return 'No backtest results to export. Run one with `/backtest run <strategy> --market <id>`.';
}
const last = sessionResults[sessionResults.length - 1];
const m = last.result.metrics;
let csv = 'metric,value\n';
csv += `run_id,${last.id}\n`;
csv += `total_return_pct,${m.totalReturnPct.toFixed(4)}\n`;
csv += `annualized_return_pct,${m.annualizedReturnPct.toFixed(4)}\n`;
csv += `final_equity,${m.finalEquity.toFixed(2)}\n`;
csv += `total_trades,${m.totalTrades}\n`;
csv += `win_rate,${m.winRate.toFixed(2)}\n`;
csv += `profit_factor,${m.profitFactor.toFixed(4)}\n`;
csv += `avg_trade_pct,${m.avgTradePct.toFixed(4)}\n`;
csv += `avg_win_pct,${m.avgWinPct.toFixed(4)}\n`;
csv += `avg_loss_pct,${m.avgLossPct.toFixed(4)}\n`;
csv += `max_drawdown_pct,${m.maxDrawdownPct.toFixed(4)}\n`;
csv += `max_drawdown_days,${m.maxDrawdownDays.toFixed(0)}\n`;
csv += `sharpe_ratio,${m.sharpeRatio.toFixed(4)}\n`;
csv += `sortino_ratio,${m.sortinoRatio.toFixed(4)}\n`;
csv += `calmar_ratio,${m.calmarRatio.toFixed(4)}\n`;
csv += `total_commission,${m.totalCommission.toFixed(2)}\n`;
csv += `total_slippage,${m.totalSlippage.toFixed(2)}\n`;
if (last.result.trades.length > 0) {
csv += '\n--- trades ---\nside,size,price,pnl\n';
for (const t of last.result.trades) {
const pnl = t.pnl !== undefined ? t.pnl.toFixed(4) : '';
csv += `${t.side},${t.size},${t.price.toFixed(6)},${pnl}\n`;
}
}
return `**CSV Export** (\`${last.id}\`)\n\n\`\`\`csv\n${csv}\`\`\``;
}
case 'config': {
const cfg = (backtestMod as any).DEFAULT_CONFIG || {
initialCapital: 10000, commissionPct: 0.1, slippagePct: 0.05,
resolutionMs: 3600000, riskFreeRate: 5,
};
return `**Backtest Config**\n\nInitial Capital: $${(cfg as any).initialCapital?.toLocaleString() ?? '10,000'}\nCommission: ${(cfg as any).commissionPct ?? 0.1}%\nSlippage: ${(cfg as any).slippagePct ?? 0.05}%\nResolution: ${((cfg as any).resolutionMs ?? 3600000) / 60000} min bars\nRisk-free rate: ${(cfg as any).riskFreeRate ?? 5}% annual\n\nOverride with flags: --capital, --days`;
}
default:
return helpText();
}
} catch (err: any) {
if (cmd === 'help' || cmd === '') return helpText();
return `Error: ${err?.message || 'Failed to load backtest module'}\n\n${helpText()}`;
}
}
function helpText(): string {
return `**Backtest Commands**
/backtest run <strategy> --market <id> [--days <n>] [--capital <n>]
/backtest results - Show last results
/backtest stats - Alias for results
/backtest export - Export last results as CSV
/backtest monte-carlo [sims] - Monte Carlo simulation
/backtest compare <s1> <s2> - Compare strategies
/backtest list - List saved runs
/backtest config - Show default config
**Strategies:** momentum, reversion, divergence, mean-revert, trend-follow
**Metrics:** Sharpe, Sortino, Calmar, profit factor, max drawdown`;
}
export default {
name: 'backtest',
description: 'Test trading strategies on historical data with Monte Carlo simulation',
commands: ['/backtest', '/bt'],
handle: execute,
};
Related skills
FAQ
How does it guard against overfitting?
It supports walk-forward out-of-sample analysis and reports an overfitting ratio, plus Monte Carlo simulation to understand variance.
Does it model costs?
Yes, it lets you configure maker/taker fees and a realistic slippage model.