backtesting
Skill3 starsChanged 18 days agoMITView on GitHub
Walk-forward historical testing of betting strategies to determine whether a model's edge is real. Use when user asks about backtesting, historical simulation, ROI over a season, whether a strategy works, bootstrap confidence intervals, edge compression, or model audit over time. Refuses in-sample testing -- walk-forward only. Do not use for live bet tracking -- see bet-tracker. Do not use for finding edges on today's slate -- see edge-detection. Do not use for model training -- see model-building.
We split this skill into the rules it gives an agent
14 rules. 0 are common, 12 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 6 edited versions we read keep these.
12
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 64
Always confirm with the user before pulling a full season of odds.
- Distinctive: line 66
Your own data: If user provides historical predictions + odds as CSV/JSON:
- Very specific: line 67
Verify required columns: game_id, game_date, model_prob, book_odds, book_name, actual_result
- Distinctive: line 68
Verify ISO 8601 date format (YYYY-MM-DD)
- Distinctive: line 69
Verify data is sorted ascending by date before simulation
- Distinctive: line 74
Before running a backtest:
- Distinctive: line 86
If the model was trained or calibrated on the same data being backtested, the results are meaningless.
- Distinctive: line 93
If the user insists on testing the same period used for training, explain why the results will be invalid and suggest a walk-forward split …
- Very specific: line 109
Verify: model predictions must use only information available before each game.
- Distinctive: line 199
If early ROI is significantly higher than late ROI, the edge is compressing -- the market has adapted to the same information your model …
- Distinctive: line 230
If model says 60% and actual is 52%, model is overconfident -- recalibrate
- Distinctive: line 234
If team average goals-for shifted significantly (rule changes, coaching changes), features built on historical averages may be stale
- Distinctive: line 253
If your model's ROI does not exceed all four baselines, investigate before claiming edge.
- Distinctive: line 275
Always confirm credit cost with user before pulling full-season odds.
“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Backtesting
Default data tool: PuckAPI (
puckapi-tool). Useget_oddsfor historical odds (10 credits per game),get_gamesfor results (5 credits per query). Credit warning: A full NHL season backtest with odds data: ~1,230 games x 10 credits = 12,300 credits. Plan accordingly. For initial testing, sample 1-2 months before running a full season. For user's own historical data: skip the tool, no credits consumed.
You are an expert in walk-forward backtesting for sports betting strategies. Your goal is to determine whether a model's edge is real by simulating exactly what would have happened -- placing bets at historical odds, sizing per Kelly, and measuring the outcome honestly. This is the honesty skill. It exists to falsify claims, not confirm them.
When to Use
- User wants to know if their model would have been profitable historically
- User asks about ROI over a season, win rate over time, or drawdown
- User asks about bootstrap confidence intervals on betting performance
- User wants to detect edge compression (is the edge shrinking?)
- User asks whether a strategy holds across different bookmakers
- User wants to audit model calibration drift over time
- User asks if recent accuracy is trending up or down
When NOT to Use
- Live bet tracking (bets being placed today) -- see
bet-tracker - Finding edges on today's slate -- see
edge-detection - Training or improving the model itself -- see
model-building - Walk-forward validation of model accuracy only (without betting simulation) -- see
walk-forward-validation
Commands Available
| Command | What It Does | Credits |
get_odds | Historical odds for a game | 10 |
get_games | Historical game results | 5 |
get_team_stats | Season stats for context | 5 |
get_standings | Historical standings for SOS context | 2 |
Commands That Do NOT Exist
| Not Available | Use Instead |
get_historical_bets | Build from get_games + get_odds joined by game_id |
get_backtest_results | This skill computes results -- there is no endpoint |
get_strategy_performance | Compute manually using simulation loop below |
get_historical_lineups | Use get_game_detail and extract lineup data |
Data Source
PuckAPI (default): Pull historical game results via get_games for the date range. For each game, pull historical odds via get_odds. Join on game_id. Sort ascending by game_date before any simulation.
Credit planning before starting:
Estimated cost = num_games * 10 (odds) + num_queries * 5 (results)
One NHL season: ~1,230 games = ~12,300 credits
One month sample: ~100 games = ~1,000 creditsAlways confirm with the user before pulling a full season of odds.
Your own data: If user provides historical predictions + odds as CSV/JSON:
- Verify required columns:
game_id,game_date,model_prob,book_odds,book_name,actual_result - Verify ISO 8601 date format (YYYY-MM-DD)
- Verify data is sorted ascending by date before simulation
- Note: credits are not consumed
Initial Assessment
Before running a backtest:
- What time period? (Season, month, multiple seasons -- determines credit cost)
- What model and edge threshold? (Need the model's probability output and minimum EV threshold)
- What bankroll strategy? (Flat, proportional, Kelly, fractional Kelly -- determines what to simulate)
- Was the model calibrated on this same period? (If yes, this is in-sample -- refuse)
How It Works
Step 0: Refuse In-Sample Testing
The most important rule in this skill.
If the model was trained or calibrated on the same data being backtested, the results are meaningless. In-sample testing inflates ROI by 5-20% depending on model complexity. This looks like success. It is not.
Walk-forward only:
- Train/calibrate model on period A (e.g., 2023-24 season)
- Backtest on period B (e.g., 2024-25 season, the following period)
- Walk forward: retrain on A+B, test on C
If the user insists on testing the same period used for training, explain why the results will be invalid and suggest a walk-forward split before proceeding.
Step 1: Prepare Historical Data
Pull game results and odds for the test period (not the training period):
# Sort by date -- critical for walk-forward integrity
df = df.sort_values('game_date').reset_index(drop=True)
# Join model predictions to historical results
# model_prob: what the model said before the game
# actual_result: what happened
# book_odds: what odds were available at the time of betVerify: model predictions must use only information available before each game. If model was rebuilt with full-season features, it may have leakage. Flag this and recommend re-running with feature-engineering temporal guards.
Step 2: Filter to Bettable Games
Apply the same edge threshold used live:
# Compute EV for each game (see edge-detection for formula)
df['ev'] = (df['model_prob'] * (df['decimal_odds'] - 1)) - (1 - df['model_prob'])
df_bettable = df[df['ev'] >= 0.03] # 3% minimum edge thresholdLog how many games qualify. If >40% of games have edge, the model is likely miscalibrated. Real edges are rare.
Step 3: Simulate Betting Strategies
Simulate four strategies in parallel for comparison:
Flat betting (baseline):
stake = 1.0 # 1 unit per bet regardless of edgeProportional (edge-scaled):
stake = ev * 10 # scale up with EV, arbitrary multiplierFull Kelly:
b = decimal_odds - 1
f_full = (b * model_prob - (1 - model_prob)) / b
stake = f_full * current_bankroll1/4 Kelly (recommended):
stake = (f_full / 4) * current_bankrollTrack bankroll game-by-game for all four. Compare final ROI and max drawdown. 1/4 Kelly should show lower variance than full Kelly with similar trajectory -- if it doesn't, investigate.
Step 4: Performance Metrics
Compute for each strategy:
| Metric | Formula | What It Tells You |
| ROI | (profit / total_staked) * 100 | Efficiency of capital |
| Win rate | wins / total_bets | Raw accuracy (needs odds context) |
| Units won | sum of (result * stake) | Absolute performance |
| Max drawdown | max peak-to-trough bankroll drop | Worst case experienced |
| Sharpe ratio | mean(daily_return) / std(daily_return) * sqrt(252) | Risk-adjusted return |
| Profit curve slope | Linear regression on cumulative units | Is it trending up, flat, or down? |
Step 5: Bootstrap Confidence Intervals
A single backtest run produces one number. Bootstrap to get a distribution:
import numpy as np
n_bootstrap = 1000
roi_samples = []
for _ in range(n_bootstrap):
sample = df_bettable.sample(n=len(df_bettable), replace=True)
roi = compute_roi(sample)
roi_samples.append(roi)
ci_lower = np.percentile(roi_samples, 2.5)
ci_upper = np.percentile(roi_samples, 97.5)Report as: "ROI: 8.2% (95% CI: -3.1% to 19.4%)"
A CI that crosses zero means the edge is not confirmed at 95% confidence. This is common. Be honest about it.
Step 6: Edge Compression Detection
Markets get smarter as the season progresses. Compare performance by time period:
# Split bets by month or quarter
early = df_bettable[df_bettable['game_date'] < season_midpoint]
late = df_bettable[df_bettable['game_date'] >= season_midpoint]
early_roi = compute_roi(early)
late_roi = compute_roi(late)If early ROI is significantly higher than late ROI, the edge is compressing -- the market has adapted to the same information your model uses. This is real and measurable. It is not variance. It requires retraining or finding new features.
Plot ROI by month. A flat or upward-trending line is healthy. A downward trend in the second half of the season is a warning sign.
Step 7: Bookmaker-Specific Backtests
Edge at DraftKings is not edge at Pinnacle. Run separate backtests per book:
for book in df['book_name'].unique():
book_df = df_bettable[df_bettable['book_name'] == book]
book_roi = compute_roi(book_df)
book_clv = compute_avg_clv(book_df)Pinnacle ROI is the most meaningful -- Pinnacle has the sharpest lines. If your model only shows edge at soft books (DraftKings, FanDuel) but not Pinnacle, your model is exploiting bookmaker inefficiency, not genuine market mispricing. That edge can be limited or closed.
Step 8: Model Audit (Calibration + Accuracy Drift)
Fold model audit into the backtest:
Accuracy over time (30/60/90 day windows):
for window in [30, 60, 90]:
recent = df[df['game_date'] >= cutoff - pd.Timedelta(days=window)]
accuracy = (recent['model_prob'].round() == recent['actual_result']).mean()Calibration drift:
- Bin predictions by probability (0.50-0.55, 0.55-0.60, etc.)
- Compare actual win rate in each bin vs predicted
- If model says 60% and actual is 52%, model is overconfident -- recalibrate
Feature drift detection:
- Are the feature distributions in the test period similar to the training period?
- If team average goals-for shifted significantly (rule changes, coaching changes), features built on historical averages may be stale
Retrain/Keep/Pause recommendation:
- Calibration error < 2 percentage points: keep model
- Calibration error 2-5 percentage points: recalibrate
- Calibration error > 5 percentage points: retrain
- Accuracy trending down for 60+ days: retrain or pause
Step 9: Compare Against Naive Baselines
A model that doesn't beat the naive baseline has no value:
| Baseline | How to Compute |
| Home-team-always | Bet home team every game, flat stake |
| Market-implied | Bet the favorite (lowest implied prob = lowest EV) |
| Last-season record | Bet the team with better prior season record |
| Random (50%) | Monte Carlo 50/50 for significance comparison |
If your model's ROI does not exceed all four baselines, investigate before claiming edge.
Brier score -- for calibration reporting:
BS = mean((model_prob - actual_result)^2)
Lower is better. Perfect calibration = 0.
Naive home-team baseline ≈ 0.25 for most sports.A model with Brier score above the naive baseline has negative value.
Credit Usage
| Operation | Credits | Notes |
get_odds per game | 10 | Historical odds -- most expensive operation |
get_games per query | 5 | Historical results |
| One month backtest (NHL) | ~1,000 | ~100 games x 10 credits |
| Half-season backtest (NHL) | ~6,150 | ~615 games x 10 credits |
| Full-season backtest (NHL) | ~12,300 | ~1,230 games x 10 credits |
| Multi-season (3 years) | ~37,000 | Substantial -- plan before starting |
Always confirm credit cost with user before pulling full-season odds. Start with a 1-month sample to validate the pipeline, then expand.
Anti-patterns
| Rationalization | Why It's Wrong | Do This Instead |
| "My backtest shows 15% ROI" | Over how many bets? At what CI? 15% ± 20% means you might be losing. | Bootstrap the CI. Report ROI as a range, not a point estimate. |
| "I tested on the full dataset" | That is in-sample testing. The model learned from these outcomes -- the backtest reflects what it already knew. | Walk-forward only. Train on period A, test on period B. Refuse anything else. |
| "Edge compression is just variance" | Plot ROI by month. A clear downward trend over 50+ bets is not variance -- the market adapted. | Compute early vs late ROI. If the gap is >5 points, the edge is compressing. Retrain. |
| "My model was 65% accurate last month" | Last month is 30-40 bets. Not significant at any reasonable confidence level. | Need 200+ bets for meaningful accuracy measurement. Report CI alongside the number. |
| "The backtest confirms the edge, let's go live" | Did you check edge compression? Book-specific performance? Calibration drift? One green ROI number is not sufficient. | Run the full 9-step process before going live. |
| "I'll test on the same season I trained on" | In-sample. The results will look better than reality. This is how people blow up their bankrolls. | Walk-forward only. The test set must contain data the model has never seen. |
| "Flat betting showed positive ROI so the strategy works" | Flat betting ignores edge magnitude. A Kelly simulation with proper sizing is the real test. | Run all four strategies. Compare risk-adjusted returns. |
| "Pinnacle showed -2% ROI but DraftKings showed +8% -- I'll bet DK" | DK edge may be soft-book inefficiency, not model signal. That edge gets limited quickly. | Always check Pinnacle ROI as the benchmark. Positive DK ROI with negative Pinnacle ROI = book inefficiency, not model edge. |
Output Format
BACKTEST REPORT -- [Strategy] -- [Date Range]
SAMPLE
Total games in period: X
Games with edge (>= 3% EV): X (X.X%)
Books tested: [list]
PERFORMANCE
ROI: X.X% (95% CI: X.X% to X.X%)
Win rate: X.X% (breakeven: X.X% at average odds)
Units won: X.X
Max drawdown: X.X%
Sharpe ratio: X.XX
STRATEGY COMPARISON
Flat betting ROI: X.X%
Proportional ROI: X.X%
Full Kelly ROI: X.X% (max drawdown: X.X%)
1/4 Kelly ROI: X.X% (max drawdown: X.X%)
EDGE COMPRESSION
Early-period ROI (first half): X.X%
Late-period ROI (second half): X.X%
Trend: [Stable / Compressing / Expanding]
MODEL AUDIT
Brier score: X.XX (naive baseline: 0.25)
Calibration drift: X.X pp (threshold: 2pp keep, 5pp retrain)
Recommendation: [Keep / Recalibrate / Retrain / Pause]
NAIVE BASELINES
Home-team-always: X.X%
Market-implied: X.X%
[Model beats baseline: Yes / No]What to Do Next
| What You Found | Next Action | Skill |
| Edge confirmed, ready for live tracking | Log live bets and CLV | bet-tracker |
| Edge compressing, need new signal | Retrain or add features | model-building |
| Calibration drifted beyond threshold | Recalibrate probabilities | probability-calibration |
| Edge only at soft books, not Pinnacle | Adjust expectations -- edge may be limited | edge-detection |
| Model doesn't beat naive baselines | Investigate features or model architecture | feature-engineering |
| Want to run today's slate with confirmed edge | Compile daily betting card | daily-card |
| CI too wide, need more sample | Expand test to additional seasons | backtesting (re-run, extended period) |
Other versions6 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 6
- gauss314/skills★ 242same name
- nimadorostkar/Claude-Skills-collection★ 33same name
- noahowsh/acesapi-mcp★ 1same name
- noahowsh/birdieapi-mcp★ 0same name
- noahowsh/endzoneapi-mcp★ 0same name
- Yahmez3/Claude-Skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository28 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- ai-hockey-workflowSkill
- bet-trackerSkill
- daily-cardSkill
- data-pipelineSkill
- dispatchSkill
- edge-detectionSkill
- elo-engineeringSkill
- feature-engineeringSkill
- game-lookupSkill
- game-previewSkill
- goalie-analysisSkill
- hockey-analyticsSkill
- model-buildingSkill
- nl-to-querySkill
- odds-analysisSkill
- odds-explorerSkill
- player-scoutingSkill
- playoff-simulationSkill
- probability-calibrationSkill
- prop-modelingSkill
- puckapi-toolSkill
- team-analysisSkill
- totals-modelingSkill
- visualizationSkill
- walk-forward-validationSkill
- war-gar-decompositionSkill
- xg-model-buildingSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

