agentleFS
noahowshnoahowsh/birdieapi-mcp/skills/walk-forward-validation/SKILL.md

walk-forward-validation

Skill0 starsChanged 29 days agoMITView on GitHub

Validates golf models in time order: expanding-window splits by tournament date, tournament-grouped evaluation, point-in-time ratings, metrics for round scores, made cut, top 10 and winner probabilities, a market baseline, and significance checks with the small number of majors. Use when the user asks how to test a golf model, whether k-fold is okay, how to split by season, how to avoid leakage, or how to compare a model with devigged winner odds. Do not use for building features -- see feature-engineering. Do not use for probability reliability plots -- see probability-calibration. Do not use for simulating betting returns -- see backtesting.

6 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

2 rules. 0 are common, 2 are distinctive, and 0 are very specific to this project.

  • 0

    Common

    Most of the 6 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Group by tournament: All rows of one tournament go in the same fold.

    line 49
  2. Distinctive:

    Step 7: Keep a final holdout: If you iterate on the 2025 fold it becomes a training fold.

    line 120

“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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Walk-Forward Validation

Default data tool: BirdieAPI (birdieapi-tool). Validation is computation on data you already pulled. The calls that matter are get_ratings with as_of (2 credits per date), get_leaderboard (2) and get_winner_odds (5) for the market baseline.

You are an expert in honest model evaluation. Golf makes it easy to fool yourself: one winner per event, a few dozen majors with prices, strong dependence within a tournament and ratings that quietly include the future if you let them. Your goal is a test whose number you would stand behind.

When to Use

  • "Can I use k-fold cross-validation on golf results?" (No.)
  • "How do I split train and test?"
  • "Does my model beat the market?"
  • "How many majors do I need before this is meaningful?"

When NOT to Use

  • Feature construction -- see feature-engineering
  • Reliability diagrams and scaling -- see probability-calibration
  • Money simulation -- see backtesting

Commands Available

CommandWhat It DoesCredits
get_ratingsPoint-in-time rating via as_of2
get_leaderboardOutcomes: finish, cut status, rounds2
get_winner_oddsMarket baseline for majors from 20215
list_tournamentsThe calendar and major flag, for ordering and subsets1

Commands That Do NOT Exist

Not AvailableUse Instead
get_predictionsNot served; the model is yours
get_historical_odds for non-majorsNot available; only majors have prices
get_closing_lineThe latest pregame snapshot, which can be a week early for past majors

Rules

  1. Time order only. Train on events before a date, test on events after. Never shuffle, never k-fold.
  2. Group by tournament. All rows of one tournament go in the same fold. Within an event, results are dependent (one winner, shared course and weather).
  3. Point-in-time inputs. Ratings with as_of the day before round 1. Season stats from the prior season. Course history from earlier editions.
  4. Freeze choices before testing. Features, hyperparameters, sigma and tau are set on the training window.
  5. Compare against baselines. At least the rating alone, and the devigged market where it exists.

How It Works

Step 1: Build the event list.

list_tournaments {"season": 2023, "status": "final"}

Repeat per season and sort by start_date. Add "major": true for the majors only.

Step 2: Choose a scheme. An expanding window by season is the default:

FoldTrainTest
12015-20202021
22015-20212022
32015-20222023
42015-20232024
52015-20242025

Rating-based models need little training data. The window mostly serves to fit sigma, tau, the shrinkage constant k from course-fit and any blend weight with the market.

Step 3: Pick metrics by target.

TargetMetricNotes
Round adj_vs_fieldRMSE, MAEbaseline: the golfer's rating
Made cutlog loss, AUCthousands of rows
Top 10log loss, Brierhundreds of positives
Winnerlog loss over the fieldone positive per event

For winners, the score of one event is -log(p_winner). Average over events.

import numpy as np

def winner_log_loss(prob_by_event, winner_by_event, floor=1e-4):
    s = []
    for ev, probs in prob_by_event.items():        # probs: dict player_id -> prob, sums to 1
        p = max(probs.get(winner_by_event[ev], 0.0), floor)
        s.append(-np.log(p))
    return float(np.mean(s))

The floor stops one zero from producing infinity; state it. A winner at 0.2% costs 6.2 nats and a winner at 15% costs 1.9, so a handful of long-priced winners dominate the mean.

Step 4: The market baseline. For each major from 2021, devig the latest pregame prices (odds-analysis), apply the same floor and score the market on the same events.

get_winner_odds {"season": 2025, "limit": 200}

A model that does not beat this baseline out of sample adds nothing over simply using the price. Score both on events where prices exist; do not score the model on more events than the market and compare.

Step 5: Significance. About two dozen majors have prices, so about two dozen winners. Compute the per-event loss difference (model minus market) and its standard error:

d = np.array(model_losses) - np.array(market_losses)
se = d.std(ddof=1) / np.sqrt(len(d))
print(d.mean(), se)         # |mean| < 2 * se: no demonstrated difference

With about 25 events, a small improvement will not clear two standard errors. Say "no demonstrated difference" rather than "slightly better". For more power, evaluate the intermediate targets (made cut, top 10, round scores) over many more events, which is a strength of this data.

Step 6: Report by subset. Split majors from regular events only if each subset has enough rows. Put the count next to every number.

Step 7: Keep a final holdout. If you iterate on the 2025 fold it becomes a training fold. Hold the latest full season untouched until the end.

Common failures

FailureSymptomFix
Random splitTest loss far better than livesplit by date and tournament
Current rating on past eventsSuspiciously strong results for recent starsas_of
A zero-probability winnerInfinite lossfloor, and report it
Tuning on the test foldGains vanish next yearfrozen design, final holdout
Different event sets for model and marketApples to orangessame events
Counting golfers as independentFake sample sizethe unit is the tournament

Credit Usage

OperationCreditsNotes
Ratings, one date per event, 100 events200
Leaderboards, 100 events200
Market for every major, 2021-202630one season call returns four majors, six seasons at 5 credits
Whole study on the free tierspread over months500 credits a month

Anti-patterns

What Claude Might DoWhy It's WrongDo This Instead
Report accuracy at picking the winnerOne positive per 100 rows; accuracy is meaninglessLog loss over the field
Call a 0.05 nat edge "significant" on 25 majorsWithin noiseShow the standard error
Validate once and shipRegimes changeRe-run each season
Score with finishing-position correlationField size variesUse strokes versus the field

Output Format

Walk-forward, expanding window by season, folds 2021-2025 (majors with prices: N = <count>)
Winner log loss: model <x.xx>, devigged market <x.xx>, rating-only <x.xx>
Difference (model - market): <mean> +/- <se> over N events. Demonstrated: <yes/no>.
Made-cut log loss over <rows> golfer-events: <x.xxx>

What to Do Next

What You FoundNext ActionSkill
Model passesCheck probability reliabilityprobability-calibration
Model is no better than the marketTreat the market as the estimateodds-analysis
Want to simulate returnsMoney backtestbacktesting

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

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.