agentleFS
noahowshnoahowsh/acesapi-mcp/skills/feature-engineering/SKILL.md

feature-engineering

Skill1 starsChanged 28 days agoMITView on GitHub

Building leak-free features for tennis match prediction from AcesAPI data: pre-match Elo (overall and surface), rating gaps, rolling form, days of rest and recent load, head-to-head with a time guard, surface and format flags, and a symmetric pair layout. Use when the user is building a model, asks what features to use, or suspects leakage. Do not use for model fitting or tuning: see walk-forward-validation. Do not use for calibration: see probability-calibration. Do not use for quick lookups: see match-lookup.

14 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use mean-reversion and position-sizing.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • agiprolabsSkill

    mean-reversion

    Mean-reversion strategy tools including Hurst exponent, half-life estimation, z-score signals, ADF testing, and Ornstein-Uhlenbeck modeling

    Also in 3 projects that use this

  • Bhala-SrinivashSkill

    position-sizing

    Calculate optimal position sizes for NSE/BSE equity trades using fixed fractional, ATR-based, and Kelly criterion methods. Includes portfolio constraints and leverage adjustments.

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

1 rule. 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 12 edited versions we read keep these.

  • 0

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

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

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Paired layout: Keep both players' features and enforce symmetry: P(A beats B) + P(B beats A) = 1, by averaging the model's output across …

    line 56

“Common” is measured: kept by at least 6 in 10 of the 12 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

Feature Engineering

Default data tool: AcesAPI (acesapi-tool). Bulk results: get_matches by season (2 credits per 100 matches, use offset). Per-match pre-match ratings: get_match (2 each) or get_player_matches (5 per 50). Ages, hand, height: get_player (5). Ranking on a date: get_rankings with date (2). The cheapest design: pull seasons of results with get_matches and compute your own Elo and rolling features locally. About 90 credits per tour-season.

You turn match records into a table where each row is one match and every feature was knowable before the first ball. The single biggest risk is leakage, so every feature below states what it is allowed to see.

When to Use

  • "What features should I use to predict tennis matches?"
  • "How do I build a rating feature without leakage?"
  • "My model is 80 percent accurate, why?" (it is leaking)
  • "Build a dataset from 2015 to 2025"

When NOT to Use

  • Evaluating the model: see walk-forward-validation
  • Probability quality: see probability-calibration
  • Using a price as a feature: see odds-analysis first, and treat it separately (see below)

Commands Available

CommandWhat It DoesCredits
get_matchesSeason of results with sets, surface, round, seeds, winner; paginate with limit, offset2
get_player_matchesOne player's matches with rating_before, rating_after5
get_matchplayers[].rating.before and surface_before, stats, odds2
get_playerdate_of_birth, height_in, plays, country, debut year via the profile5
get_rankingsList in force on a date, for rank and points as of before the match2
get_head_to_headPrior meetings (only ever use meetings before the match date)3
get_tournamentDraw structure2

Commands That Do NOT Exist

Not AvailableUse Instead
get_featuresBuild locally from the calls above
get_serve_statsNot available: no serve or break-point features
get_pre_match_ratingsrating.before in get_match, rating_before in get_player_matches
get_injuriesNot available: proxy with long gaps and retirements

Row Design: One Match, Symmetric Pair

Each match record has player1 and player2. Do not treat position as meaningful. Two valid layouts:

  1. Difference layout. Pick the focal player at random per row (or alternate), compute feature_focal - feature_opponent, and the label is "focal won". Mirror the dataset (add the swapped row) so the model sees both orientations; evaluate on one orientation only.
  2. Paired layout. Keep both players' features and enforce symmetry: P(A beats B) + P(B beats A) = 1, by averaging the model's output across the swap.

A model that learns "player1 wins more often" has learned data ordering, not tennis.

Feature Families

Strength (pre-match only)

FeatureSourceRule
elo_gaprating.before for each playerNever after
surf_elo_gapsurface_beforeSame surface only; thin early in a career
elo_blend_gapshrink surface toward overall (see surface-elo)Fit shrinkage by walk-forward
rank_gap_loglog(rank2) - log(rank1) from the list in force before the matchRank is null outside the top 100 to 150; use an indicator plus a cap (e.g. 200)
points_gaplist points before the matchSame cap logic

Form and load

FeatureDefinitionRule
form_10sum of rating_after - rating_before over the last 10 completed matchesShift by one: exclude the current match
win_rate_surface_20wins in last 20 matches on this surfaceMin 5, else missing
games_share_10games won divided by games played, last 10A serve and return proxy
days_restdays since the previous matchCap at 60; flag returns after long gaps
sets_last_7d, games_last_7dsets or games played in the previous 7 daysFatigue proxy
deciding_sets_14dthree or five set matches in 14 daysSame

Context

FeatureSource
surface, indoormatch record (indoor may be null; encode as unknown)
best_of_5men's Grand Slam main draw; known in advance
round_ordermatch record
seed_gapseed1, seed2; null for unseeded, encode as 33 or a missing flag
age_gap, height_gap, hand_matchupget_player bio (compute age at match date)
tourney_tiermajor flag from list_tournaments, or Masters/1000 by name

Head to head with a time guard

Compute from get_head_to_head only meetings with match_date before the current match. Use wins_a - wins_b shrunk by n / (n + 10); set to zero when n < 3. Do not call get_head_to_head per row of a large dataset (3 credits each). Build the history from your own results table instead.

Rebuilding Elo Locally

If you compute ratings yourself from get_matches, you can reproduce the published method: start 1500, update both players with K = 250 / (matches + 5)^0.4 where matches is the player's own count of rated matches so far (separately for each surface rating), skip walkovers, defaults and retirements, and expected score 1 / (1 + 10^(-(Ra - Rb)/400)). Your numbers will differ from rating.before where your match pool differs from AcesAPI's (qualifying is included in theirs, from 2022). Validate on a sample: compare your pre-match value with rating_before from get_player_matches for one player, and expect agreement to within a few points when pools match.

def update(ra, rb, na, nb, a_won):
    ea = 1 / (1 + 10 ** (-(ra - rb) / 400))
    ka = 250 / (na + 5) ** 0.4
    kb = 250 / (nb + 5) ** 0.4
    s = 1.0 if a_won else 0.0
    return ra + ka * (s - ea), rb + kb * ((1 - s) - (1 - ea))

Process matches strictly in date order (starts_at), and write the pre-match rating into the row before applying the update.

Leakage Checklist

LeakWhyFix
rating.after, surface_afterContains this match's resultUse before
Score, sets, games1, stats.total_games of the target matchIt is the outcomeDrop from features
Rank from the list dated after the matchIncludes the resultTake get_rankings with date set to the day before
Rolling means that include the current rowSelf-referenceShift by 1 before windowing
Career record from get_player pulled todayIncludes later matchesRebuild from results as of the match date
Random train/test splitFuture matches train the pastTime split, see walk-forward-validation
Odds close as a feature, then reporting accuracyThe market already knowsReport it as a market-informed model; compare against the market baseline
Round and draw knowledgeFine, known in advanceKeep
Final-round survivorsWinners reach late roundsDo not condition on round reached by outcome

Sanity test: a pre-match tennis model that beats the closing line by a wide margin is leaking. Rating-only models usually land in the mid-60s for accuracy on tour-level matches (run the Elo baseline on your own data to confirm); treat 75 percent as a bug to explain.

Missing Data

  • Walkovers, retirements: drop from training and from rating updates, keep as a "retired recently" flag for the next match.
  • Qualifying draw matches: available from 2022; the rating updates include them, so models trained before and after 2022 see different coverage.
  • Rank null: indicator plus cap. Never fill with 0.
  • First matches of a career: few prior matches means the rating is unsettled; include matches_played_log as a feature so the model can discount.

Credit Usage

DatasetCallsCredits
One tour, one season of resultsabout 45 get_matches pagesabout 90
Ten seasons, one tourabout 450 pagesabout 900
Ratings from get_match per row1 per match2 per match: avoid, recompute locally
Player bios for 300 players300 get_player1,500: sample or only fetch finalists

State the credit estimate before pulling more than one season.

Anti-patterns

What Claude Might DoWhy It's WrongDo This Instead
Use rating.after as a featureLeakbefore
Let player1 mean somethingIt is record orderSymmetrize
Fill missing rank with 00 would be better than #1Indicator and cap
Add career win rate from today's profileIncludes the futureRebuild as-of
Evaluate on a shuffled splitLeakWalk forward
Add 80 features for 20,000 rowsOverfitStart with Elo gap, surface Elo gap, rest, form

What to Do Next

What You BuiltNext ActionSkill
A feature tableFit and evaluate in time orderwalk-forward-validation
PredictionsCheck probabilitiesprobability-calibration
A strategyTest against closing linesbacktesting

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.