Market Data Skills for AI Agents
A market data skill tells an AI agent which symbols, which calendar, what to cache, and what to do when a series comes back short. Here is the spec.
Market data skills for AI agents are packaged instruction sets, plus a small script surface, that let an agent fetch, normalize, cache, and read market data without an operator rewriting the plumbing for every task. A skill is not a data source and not a model. It is the written record of the decisions taken about a data source: which symbols count, which calendar applies, what gets cached, and what the agent does when a request returns half a series.
What is a market data skill for an AI agent?
An agent here means a language model with permission to run tools: read a file, run a script. Given an API key, an agent can already pull a price. What it cannot do on its own is make the same choice twice. Ask on Monday for a stock's average volume last month, ask again on Friday, and the two answers can differ on which minutes were counted and on what the agent did with a day that had no print.
A skill closes that gap. In current practice it is a folder: one markdown instruction file the agent loads when a task matches its description, with optional references/ and scripts/ directories beside it. The model brings the reasoning. The skill brings the conventions, including the ones an endpoint has no way to express.
Public examples move weekly, which makes the pin part of the practice. gauss314/skills at commit 5156f81 (June 14, 2026) is a readable current one, spanning a few dozen data sources and a handful of computational tools. Reference a tagged release or a commit, never the default branch. A skill file that changed under you is a number that changed under you.
Where does the trading session actually start?
A minute feed does not hand back a trading day. It hands back every bar the venues reported, extended hours included. The panel below folds one month of bars onto a single New York clock. For each half hour it shows the share of that name's June volume which printed inside the bucket. SPY is the S&P 500 tracker, KO is Coca-Cola.
The exact SQL behind every number
WITH bars AS (
SELECT ticker,
toStartOfInterval(toTimeZone(window_start, 'America/New_York'), INTERVAL 30 MINUTE) AS et_bucket,
volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'KO')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')
),
totals AS (
SELECT sumIf(volume, ticker = 'SPY') AS spy_total,
sumIf(volume, ticker = 'KO') AS ko_total
FROM bars
)
SELECT formatDateTime(b.et_bucket, '%H:%i') AS et_time,
round(100 * sumIf(b.volume, b.ticker = 'SPY') / any(t.spy_total), 2) AS spy_share_pct,
round(100 * sumIf(b.volume, b.ticker = 'KO') / any(t.ko_total), 2) AS ko_share_pct
FROM bars AS b
CROSS JOIN totals AS t
GROUP BY et_time
ORDER BY et_timeThe earliest bucket the feed carries opens at 04:00 ET, holding 0.23% of SPY's month and 0.15% of KO's. The bucket opening at 09:30, the first half hour of regular trading, holds 8.9% and 14.18%. The half hour opening at 15:30 holds 18.06% and 19.51%, and the one opening at 16:00, which contains the 4:00 pm closing cross, holds 13.52% and 6.54%. Across 32 buckets the shape is lopsided: the ones at the edges of regular trading carry many multiples of what the overnight ones carry.
A skill has to write that boundary down, since "today's bars" means one range to a chart and a different range to a volume calculation. After hours and premarket trading covers what the thin buckets are, and the closing auction explains the spike at the end.
Why the calendar comes before the numbers
Second decision: the calendar. Twelve months of SPY sessions below, alongside the count of sessions whose busiest minute landed before 15:00 ET, which is the fingerprint of a 1:00 pm early close.
The exact SQL behind every number
WITH sessions AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
argMax(formatDateTime(toTimeZone(window_start, 'America/New_York'), '%H:%i'), volume) AS busiest_minute
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2025-07-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')
GROUP BY session_date
)
SELECT formatDateTime(toStartOfMonth(session_date), '%Y-%m') AS month,
count() AS sessions,
toDayOfMonth(toLastDayOfMonth(min(session_date))) AS calendar_days,
countIf(busiest_minute < '15:00') AS short_sessions
FROM sessions
GROUP BY toStartOfMonth(session_date)
ORDER BY toStartOfMonth(session_date)2025-07 held 22 sessions inside 31 calendar days. 2026-06 held 21 inside 30. The short-session column picks out 2 in 2025-07, 1 in 2025-11, and 1 in 2025-12.
A twenty-day window is not four calendar weeks, and a month is not twenty-one sessions. An agent stepping through calendar dates will put a closed Friday in a denominator, and one stepping through the exchange calendar will not. Market holidays and early closes lists the days themselves. The same question sits underneath look-ahead bias in backtesting: a date that exists on a spreadsheet and a date on which a price existed are different objects.
What a partial series looks like
Third decision: absence. A minute bar exists when a trade printed in that minute. No trade, no row, and a query for a full day comes back with fewer rows than the day has minutes. Nine widely held names over June 2026:
The exact SQL behind every number
WITH daily AS (
SELECT ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
count() AS bars
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'QQQ', 'NVDA', 'AAPL', 'MSFT', 'XOM', 'JNJ', 'KO', 'PG')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')
GROUP BY ticker, session_date
)
SELECT ticker,
count() AS sessions_with_data,
round(quantileDeterministic(0.5)(bars, cityHash64(session_date)), 0) AS median_bars_per_session,
min(bars) AS quietest_session_bars
FROM daily
GROUP BY ticker
ORDER BY median_bars_per_session DESCNVDA carried data on 21 sessions and JNJ on 21, so nothing in this panel is a missing day. The gap sits inside the sessions: a median of 954 bars a session for the first name against 418 for the last, whose quietest June session carried 399 bars in total.
That is a partial series, and it is not a fault. The skill has to say what the agent does with one: return it with the bar count attached, or hold and report the shortfall. The failure worth engineering against is the quiet one, an agent that forward-fills the missing minutes and returns a clean-looking table with no marker on the invented rows. The same absence is why any average needs a stated denominator, which average daily volume works through.
One company, two tickers
Fourth decision: symbols. Three dual-class companies over June 2026, with each line's share of its pair's traded volume.
The exact SQL behind every number
WITH daily AS (
SELECT ticker,
multiIf(ticker IN ('GOOGL', 'GOOG'), 'Alphabet',
ticker IN ('FOXA', 'FOX'), 'Fox',
'News Corp') AS company,
toDate(toTimeZone(window_start, 'America/New_York')) AS session_date,
sum(volume) AS day_volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('GOOGL', 'GOOG', 'FOXA', 'FOX', 'NWSA', 'NWS')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-06-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')
GROUP BY ticker, company, session_date
),
per_ticker AS (
SELECT ticker,
company,
round(avg(day_volume) / 1e6, 2) AS avg_daily_volume_m
FROM daily
GROUP BY ticker, company
),
pair_totals AS (
SELECT company, sum(avg_daily_volume_m) AS pair_volume_m
FROM per_ticker
GROUP BY company
)
SELECT p.ticker AS ticker,
p.company AS company,
p.avg_daily_volume_m AS avg_daily_volume_m,
round(100 * p.avg_daily_volume_m / t.pair_volume_m, 1) AS share_of_pair_pct
FROM per_ticker AS p
INNER JOIN pair_totals AS t ON p.company = t.company
ORDER BY p.company, share_of_pair_pct DESCAlphabet trades as GOOGL and GOOG. The first averaged 30.15 million shares a session and 59.6% of the pair's volume, the second 40.4%. At News Corp the split runs 74.5% to NWSA against 25.5% to NWS.
Ask an agent about one of these companies and one of the two lines comes back. The skill decides which, and a careful one records the symbol it resolved next to the number it returns. The harder cases are worse: a symbol reassigned to a new company after a delisting, a ticker change part way through a history, a depositary receipt sitting beside a local listing. Symbol resolution is the step skipped most often and discovered latest.
Rate limits, caching, and the failure contract
A vendor endpoint has a call ceiling per minute and a page size, and those two numbers set the shape of the work. An agent asked for a year of minute bars across fifty names is planning a batch job, not making a request. A usable skill states the ceiling and the plan: chunk the range, then stop at a bounded retry count.
The cache key is where these things most often go wrong. Keying on symbol and date range alone stores the answer to a question nobody asked, since the session boundary and the price adjustment policy are part of what the number means. Put them in the key. Give datasets different lifetimes as well: a 2019 dividend record is immutable, yesterday's bars are not, and the front edge of any feed carries an ingest lag of a day or two.
Then the failure contract, which is most of what separates a skill from a thin wrapper. Four outcomes need to stay distinguishable: an empty result, a transport error, a truncated page, and a rate-limit rejection. Only one of them is a reason to repeat the same call unchanged. Write the policy into the instruction file, or the model will improvise a plausible one at the moment it matters.
A skill or a plain API call?
For a one-off question the raw call wins on every axis. The free stock market data API page covers that path, and so does the SQL API for stock market data, which has a property worth noticing here: the query is the artifact, and the definitions live inside the SELECT where a reader can check them.
A skill earns its place when the same task repeats and when several agents have to agree on definitions. Multi-agent trading systems sharpen the second case. Two agents holding different ideas of what a session is will produce two numbers and no way to reconcile them.
Where the abstraction leaks
Four places, roughly in the order they bite.
The defaults go invisible. A session defined once is inherited by every number downstream, including in a summary where the basis is never stated. Ask for the basis explicitly, and have the skill emit it beside the figure.
The file drifts. A skill is code and it moves. A figure produced under one version and quoted under another has no audit trail unless the version travels with the figure. That is the argument for pinning a commit rather than a branch.
The adjustment policy hides. Splits and dividends rewrite historical prices, and both the adjusted and the unadjusted series have honest uses. A skill that makes the choice silently will hand back a chart with a cliff in it the morning after a split.
None of it is edge. A skill removes plumbing mistakes and makes work repeatable. It does not tell anyone what a number means, and a faster path to a number is not a better number.
FAQ
What is an AI agent skill?
A skill is a folder of instructions a model loads when a task matches its description: one markdown file, often with scripts and reference documents beside it. The model brings the reasoning, and the skill brings the conventions that an API endpoint cannot express on its own.
What does a market data skill need to specify?
At minimum: the data source and its rate limits, the symbol resolution rule, the trading calendar and session boundary, the cache key with its lifetime, and the response to a failed or partial request. Anything left unspecified gets improvised by the model at run time.
Why pin a market data skill to a commit instead of the main branch?
The instruction file defines how a number is computed, so an edit to the file edits the number. Pinning a tagged release or a commit keeps a published figure reproducible. This category of tooling changes week to week, which makes a default branch a moving target.
What should happen when a market data request returns a partial series?
That is the skill's decision, and it belongs in writing. A defensible policy returns the series with its bar count and coverage attached and holds rather than interpolating. Missing minutes usually mean no trade printed in them, not a broken feed.
Does a market data skill give an AI agent a trading edge?
No. A skill standardizes how data is fetched and normalized, which removes a class of plumbing mistakes and makes results repeatable. The numbers it returns are the numbers anyone else can pull from the same source, and this page is educational rather than advice.
Every panel above is a stored query with its SQL attached. Open one and rerun it on the Strasmore terminal.