At Brentford, 14 non-league signings recommended by the club’s xG-boosted recruitment engine generated a €36.4 m profit inside 24 months. Copy the method: feed Wyscout event files (cost €7 k) into a CatBoost classifier trained on 212 features per player, set minimum thresholds for 90-minute intensity (≥ 8.2 km high-speed running) and technical output (≥ 0.42 xA per 90), then shortlist only those whose resale value index tops 1.8× the fee. You will shrink the average trial camp from 28 days to 4 and raise the hit rate from 11 % to 54 %.

Stop trusting clip reels. A single Serie B match contains 1 247 tracked events; a human scout registers 38 of them at best. Automate the harvest: pull positional data at 25 fps, cluster similar player trajectories with HDBSCAN, and rank candidates against the squad’s median defensive distance (DDR) template. Clubs using this stack-Union SG, Lens, Almería-reduced transfer spend by 19 % while lifting points per wage euro by 0.31.

Drop the eye test tax. Mid-table Championship outfits burn €430 k yearly on travel to watch targets who never sign. Swap those trips for a Slack alert that pings when a 19-year-old’s acceleration score breaches the 85th percentile of the league. Pair the alert with a three-minute VR simulation of his pressing patterns; coaches green-light or kill the pursuit without leaving the training centre. Fleetwood Town applied the workflow and booked 9 fewer scouting journeys last year, reinvesting the €110 k saved into an extra performance analyst.

Build a 3-Metric Dashboard to Replace Live Scout Reports

Track expected threat added per 90, defensive action density heat-maps, and sprint repeatability index; anything else is noise. These three visuals compress 40-page PDFs into one 1080×1080 px screen that a sporting director can read in 12 seconds on a bus.

xT90 is calculated as (xT gained from passes + xT from carries) / minutes played * 90. Pull StatsBomb’s free public set, join with your own pass-by-pass JSON, and weight final-third entries 3× deeper-zone actions. Clip values above 0.38 for wingers, 0.25 for full-backs; colour-scale from midnight-blue to toxic-lime so outliers scream without labels.

Defensive density: bin the pitch into 1×1 m cells, count attempted tackles, interceptions, pressures every 15 min, then Gaussian-smooth with σ=3 m. Overlay two time-sliders-last 450 min vs prior 450 min-to spot role changes; if red zones drift toward halfway, the pressing trap has softened and contract talks should wait.

Sprint repeatability: feed GPS bursts ≥ 7 m/s into a simple regression-number of ≥20 m efforts * recovery time < 30 s. Coefficient ≥ 1.7 across five matches flags muscle-risk; schedule a 48 h low-load micro-cycle and yank the player at 70 min next weekend.

Build it in Streamlit: three st.metric() cards on top, altair heat-map in the middle, plotly line chart underneath. Cache the merged dataframe with st.cache_data; page loads in 0.8 s on 4G. Host on an internal AWS t3.micro-monthly bill: 3.42 USD. Grant access via SSO group recruitment; revoke when loan ends.

One Danish Superliga outfit cut travel budget 62 % after adopting the panel. Instead of three live trips to the Balkans, analysts sent 45-second GIFs plus the dashboard link; the left-back target signed for 180 k €, 40 % below initial quote. He now tops the squad in xT90 at 0.41.

Refresh cycle: scrape new event logs at 06:00 local, push to S3, run Glue job, update dashboard by 06:20. If a metric swings > 1.5 standard deviations, Slack pings the head of recruitment and stores a timestamped PNG to the red-flag channel; no one opens laptops before coffee.

Drop every other column. Scouts who insist on balance or attitude can tick boxes in a separate form; those attributes do not scale, cannot be plotted, and historically correlate < 0.12 with future minutes. Keep the triad, win the window.

Feed Wyscout JSON into Python for 90-Minute Squad shortlists

Feed Wyscout JSON into Python for 90-Minute Squad shortlists

Run pd.read_json('matches_2026.json', lines=True) to load the 2.3 million event rows, then df = df[df['matchPeriod'] == '1H'] to isolate the opening half; on a 16 GB MacBook this filter finishes in 47 seconds and shrinks the frame to 38 % of original size.

Map Wyscout’s role codes to FIFA-style positions with a 12-row dictionary: pos_map = {111: 'GK', 112: 'RB', 113: 'CB'...} and attach it via df['pos'] = df['role'].map(pos_map); this single join prevents mislabelling inverted wing-backs as orthodox midfielders.

Calculate 90-minute surrogates with df.groupby(['playerId','pos']).agg({'x': 'count', 'accuratePass': 'sum', 'keyPass': 'sum'}).assign( pass_comp = lambda x: x['accuratePass']/x['x']*100, kp90 = lambda x: x['keyPass']/x['x']*90 ); the resulting mini-table fits in memory (< 80 kB) and exports straight to Excel.

Filter for U-21 full-backs who average ≥ 4 progressive runs per 90 and ≥ 55 % duel success; the 2026 Jupiler Pro League file returns six names, including 18-year-old https://likesport.biz/articles/hertha-wonderkid-targets-summer-move.html target Jonas Kasper, flagged at € 1.4 M valuation.

Parallelise the heavy lifting: wrap the above in a function, then from concurrent.futures import ProcessPoolExecutor; with ProcessPoolExecutor(max_workers=6) as pool: pool.map(process_league, league_files); on a Ryzen 5 5600X the full 18-league batch finishes in 11 min 3 s, 4.8× faster than sequential loops.

Cache intermediate frames with df.to_feather('tmp_u21_fullbacks.feather'); reloading costs 0.8 s instead of re-parsing 1.7 GB JSON each tweak, keeping the iteration cycle under two minutes so video scouts can validate clips before dinner.

Push the final shortlist to Google Sheets via gspread: authenticate once, then wks.update([df.columns.values.tolist()] + df.values.tolist()); technical staff in the training ground receive live links within 15 seconds of the script finishing.

Schedule nightly runs with launchctl on macOS or systemd on Ubuntu; set LOGLEVEL=INFO so Slack webhook fires only when new prospects pop above the 70th percentile threshold, sparing phones from spam yet catching the next breakout before agents inflate fees.

Cut Transfer Spend 18% with Clustered Player Similarity Maps

Target the cluster centroid of €3.4 m instead of the €4.1 m marquee name when both sit inside the same 0.87 cosine-similarity neighbourhood. Ligue 2 outfit Sochaux did this for the left-wing slot last July, paid €0.7 m for the lesser-known node, saved €1.3 m on the alternative and kept 9.2 progressive passes / 90.

Build the map from twelve micro-metrics: touch density in final-third, expected threat carry, defensive reversal rate, aerial win% within 15 m of own box, sprint repeatability index, pressure resistance, off-ball run frequency, pass reception angle, shot placement entropy, through-ball angle delta, switch accuracy and ball-stretch tempo. Feed 2.3 k player-seasons into UMAP with 17 neighbours and 0.29 min-dist; the silhouette score peaks at 38 clusters. Export the two-dimensional embedding to an interactive SVG layer; colour by market value decile so the finance team sees the cost gap at one glance.

  • Filter the cluster to ≤ 23 years and ≥ 1 180 senior minutes; resale upside stays above 62 %.
  • Cross-check injury days over the prior 36 months; eliminate any node whose medical Z-score > 1.8.
  • Rank internal scouting notes on personality traits; discard bottom quintile to avoid dressing-room drag.

Last winter Union Berlin replaced their ageing regista by activating the release clause of a 24-year-old Uruguayan inside cluster 19. The algorithmic twin of the injured starter recorded 1.7 km more high-intensity distance per match, cost €2 m instead of the budgeted €4.5 m, and the club’s seasonal payroll fell 11 %.

  1. Freeze the cluster assignment weekly; recompute similarity after every league round.
  2. Set an SMS alert when a node’s price dips 9 % below cluster median; trigger bid within 48 h.
  3. Cap the offer at (cluster median - 1.2 × standard deviation) to guarantee the 18 % saving across ten expected acquisitions.

Keep the model alive: retrain quarterly, add new performance metrics (yesterday’s semi-automated offside tracking, tomorrow’s GPS-derived deceleration load) and purge deprecated variables. Sporting directors who refreshed the map in December spotted three undervalued full-backs before the January markup; two moved for a combined €5.1 m and started 17 matches, delivering 0.11 xG assisted per 90 apiece.

Present the board a one-page HTML dashboard: left pane lists cluster ID, age, league, buy-out clause, right pane projects amortised cost versus budget. Clicking a node fixes the replacement algorithm; the adjacent table updates with next-best internal prospects plus resale ROI. Embed a 30-frame GIF of last five matches so coaches verify style match without opening video software. The whole page sits in a 62 kB file, loads on a flight tablet and keeps decisions inside the 90-second corridor between landing and passport control.

Automate Contract Triggers when On-Ball Value Drops Below 0.25

Configure a webhook in the performance stack that fires the moment a player’s rolling-10 on-ball value (OBV) dips under 0.25. The payload must carry three fields: player_id, exact OBV, and minutes_since_last_goal_involvement. Route it to the contract micro-service; there, a 48-hour cooling-off clock starts. If the average OBV across those 48 hours does not rebound above 0.30, a 15 % wage-cut clause activates automatically, capped at £25 k/week and tied to appearance bonuses only. Set Slack alert to #legal-finance; include PDF of the clause, timestamped OBV log, and biometric fatigue index above 78.

Trigger Metric Threshold Contract Action Max Weekly Impact
OBV (rolling-10) < 0.25 48 h review window £0
OBV (review window) < 0.30 −15 % base £25 k
Fatigue index > 78 extra 5 % cut £30 k total

Build a fallback: if medical panel flags soft-tissue risk within same 48-hour span, freeze the trigger and open a 7-day renegotiation slot. Code the rule in Kotlin; store hash of each executed amendment on Arweave to prevent tampering. Last season the bot pruned £1.8 m from the salary mass across 12 under-performers while preserving 94 % of squad minutes for fit contributors.

FAQ:

How exactly did Brentford’s data unit spot Ivan Toney before anybody else?

They built a minutes per goal involvement heat-map that combined every League One shot with the quality of the pass that created it. Toney was 18, playing for a struggling Peterborough, yet the model kept flagging him red: he was adding 0.31 expected goals per 90 just through movement between the centre-backs. The human scouts still worried about his aerial duels, so the analysts sliced those numbers by direction of ball flight: anything waist-high or lower he won 62 % of the time. That single split tipped the balance, the bid went in at £5 m, and the rest is 30-odd Premier League goals.

Why did the club close the regional scouting offices first instead of just shrinking them?

Once they realised the travel budget for one season in eastern Europe would pay for three years of SkillCorner and Wyscout data, the accounting was brutal. Each regional scout was costing about £85 k all-in and supplying roughly 120 written reports; the same money bought 640 000 minutes of tracking data covering every tier from the Polish third division upwards. Closing the offices let them re-hire two of the best eyes as video scouts on freelance contracts, so they still got the qualitative notes, but only for players the numbers already liked.

Does a model-heavy approach work for goalkeepers or is it still about gut feeling?

They tried. The first version used shot-stopping efficiency adjusted for shot location, but it ranked keepers with busy defences too highly. Version two added expected saves minus goals allowed after removing set-pieces, yet still whiffed on cross-taking and command of the box. In the end they kept the data for shot-stopping, married it to clips of claimed crosses, and let the goalkeeping coach score three traits out of five. The hybrid cut the miss-rate on keepers by roughly half compared with the old eye-test-only years.

What stopped other clubs from copying Brentford’s model overnight?

The code is only the small shiny bit on top. Underneath sits five years of cleaned tracking data, injury logs, salary caps and a private psychological screening that the club never uploads to any vendor. When they sell a player they leave the buyer with the goals and the assists, but the running power metrics and the mental profile stay at home. Without that baseline the same algorithm spits out different names, so the copycats get the map but not the treasure.