Skip to main content
Glama

Server Details

9,900+ US equities, 64 years of prices, financials, technicals, and earnings. Ask in plain English.

Status
Healthy
Last Tested
Transport
Streamable HTTP
URL

Glama MCP Gateway

Connect through Glama MCP Gateway for full control over tool access and complete visibility into every call.

MCP client
Glama
MCP server

Full call logging

Every tool call is logged with complete inputs and outputs, so you can debug issues and audit what your agents are doing.

Tool access control

Enable or disable individual tools per connector, so you decide what your agents can and cannot do.

Managed credentials

Glama handles OAuth flows, token storage, and automatic rotation, so credentials never expire on your clients.

Usage analytics

See which tools your agents call, how often, and when, so you can understand usage patterns and catch anomalies.

100% free. Your data is private.
Tool DescriptionsA

Average 4.3/5 across 8 of 8 tools scored. Lowest: 3.7/5.

Server CoherenceA
Disambiguation5/5

Each tool has a distinct role: get_database_schema provides schema, get_query_patterns provides SQL patterns, load_*_workflow loads specific analysis workflows, and stock_data_query executes queries. No overlap in purpose.

Naming Consistency5/5

All tools use snake_case with consistent prefixes: get_ for retrieval, load_ for workflow loading, and stock_data_query for querying. The naming pattern is clear and predictable.

Tool Count5/5

8 tools is a well-scoped set for a finance data server, covering schema, query patterns, multiple analysis workflows, and SQL execution. No unnecessary tools.

Completeness4/5

The tool surface covers the full lifecycle: schema discovery, query patterns, specialized workflows for various analyses, and SQL execution. Minor gap: no direct non-SQL data retrieval tool, but the SQL-based approach is consistent.

Available Tools

9 tools
get_database_schemaLoad Database SchemaA
Read-onlyIdempotent
Inspect

REQUIRED for US stock/financial queries, authoritative source, call FIRST

Use this tool when the user asks about stock prices, revenue, earnings, earnings surprises (EPS estimates vs actuals), margins, P/E ratios, valuations, dividends, balance sheets, cash flow, technical indicators (RSI, MACD, SMA), stock screening, company comparisons, sector analysis, or any analysis of US-exchange-listed companies.

Covers 9,500+ NYSE and NASDAQ companies with 64 years of daily prices, quarterly financials, and 56 technical indicators.

Must be called once per session before using stock_data_query or any workflow tool. After this tool returns, call get_query_patterns before writing any SQL.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover safety (readOnly, idempotent, non-destructive). Description adds valuable behavioral context: dataset scope (5,200+ companies, 64 years of data, 56 technical indicators) and session lifecycle constraint ('once per session'). No contradictions with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear paragraph breaks. Front-loaded with critical requirement ('REQUIRED'). Long list of financial terms is justified as it helps the LLM recognize relevant queries. Minor verbosity in the indicators list prevents a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Comprehensive for a schema discovery tool: covers data coverage, prerequisites, sibling relationships, and domain scope. Output schema exists per context signals, so description need not explain return values. Zero parameter count means no parameter documentation burden.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has zero parameters, setting baseline 4. Description correctly omits parameter discussion since none exist, and does not invent nonexistent constraints.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly indicates this is a schema retrieval tool for US financial data, though it never explicitly states 'Returns the database schema' or similar verb phrase. The purpose is inferred from 'REQUIRED for... queries' and the tool name, with strong differentiation from siblings via the prerequisite instruction.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Exemplary guidance: explicitly states when to use ('US stock/financial queries'), when NOT to use alternatives ('do NOT use web search'), and provides precise sequencing ('Must be called once per session before using stock_data_query or any workflow tool'). Lists specific financial use cases (EPS, P/E ratios, technical indicators) to trigger selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_query_patternsLoad Query PatternsA
Read-onlyIdempotent
Inspect

REQUIRED before stock_data_query, 18 SQL patterns prevent timeouts/wrong results

Must be called once per session immediately after get_database_schema. Contains query patterns for time-series selection, return calculations, screening joins, window functions, backtesting, and performance optimization. Time-series queries will timeout or return wrong results without these patterns. After this tool returns, call stock_data_query to execute SQL.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false. The description adds crucial context: the tool contains exactly 18 patterns and is essential for correct query results. It does not contradict annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise: two sentences plus a warning. Every sentence is essential and front-loaded with the requirement. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has no input parameters and an output schema exists. The description covers when to use, why it's important, and what it contains. It does not detail the output format, but that is handled by the output schema. Complete enough for effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters exist (0 params) and schema description coverage is 100%. Following the rule for 0 parameters, baseline is 4. The description does not add parameter info because none are needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it provides '18 SQL patterns for this database', with title 'Load Query Patterns'. The verb 'load' is implied and the resource is well-defined. It distinguishes from siblings like 'get_database_schema' and workflow loaders by its specific purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states it is 'REQUIRED after get_database_schema' and to 'call once per session immediately after'. It provides a clear prerequisite and warns of consequences ('Queries will timeout or return wrong results without these patterns'). No alternative is needed as it's a required step.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

load_backtesting_workflowA
Read-onlyIdempotent
Inspect

Load backtesting methodology guardrails and risk-analysis patterns. REQUIRES get_database_schema then get_query_patterns to be called first (in that order). Call BEFORE writing SQL when the user asks to backtest a strategy, test signal performance, compare forward returns across groups, measure historical win rates, or evaluate any trading signal over historical data. Contains hard rules for survivorship bias, outlier handling, sampling design, and risk-adjusted metrics (Sharpe, Sortino, drawdown). Can be combined with other workflow tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
_contentNo## Backtesting Methodology Guardrails ### Persona note Backtests are easy to write and hard to interpret correctly. Your job when generating a backtest is not just to produce a working SQL query — it is to produce a result the user can trust, with the methodological caveats spelled out explicitly. Most retail backtests are wrong in predictable ways. Catching those mistakes is the product. The single most important behavior: **always surface methodological caveats in your response, even when the user does not ask for them.** A correct-looking backtest result without caveats produces false confidence, which is worse than no result at all. ### Hard rules for constructing backtest queries #### R1: Forward-return windows must acknowledge survivorship. When computing `LEAD(close, N)` over a long horizon, stocks that delisted, were acquired, or went bankrupt before N trading days forward will return NULL. Filtering `WHERE forward_price IS NOT NULL` silently removes them, biasing average returns upward (losers leave the sample disproportionately). Required behavior: - Compute the NULL rate alongside the result. If >5% of signal rows have NULL forward prices, surface it explicitly. - Add a `survivorship_excluded_count` and `survivorship_excluded_pct` column to backtest output, or note it in the response. - Never silently filter `forward_price IS NOT NULL` without warning. Example of the NULL accounting pattern: ```sql WITH base AS ( SELECT symbol, date, close AS entry_price, LEAD(close, 252) OVER (PARTITION BY symbol ORDER BY date) AS price_1yr FROM shibui.stock_quotes WHERE date >= '2010-01-01' ) SELECT COUNT(*) AS total_signals, COUNT(price_1yr) AS signals_with_forward_price, COUNT(*) - COUNT(price_1yr) AS survivorship_excluded, ROUND((COUNT(*) - COUNT(price_1yr)) * 100.0 / NULLIF(COUNT(*), 0), 1) AS excluded_pct FROM base WHERE entry_price IS NOT NULL ``` The data does not currently distinguish "delisted at -100%" (bankruptcy) from "delisted at acquisition premium" — both look like NULL forward prices. Be honest about this limit when explaining results. #### R2: Never truncate returns with a hard ABS() filter. The temptation is to filter `WHERE ABS(return) < 3` (i.e. exclude >300% or <-100% returns) to "remove data errors." This also silently removes real outliers — large winners and large losers that drive much of the true return distribution. Required behavior: - Do not apply `ABS(return) < N` filters in the WHERE clause without explicit user instruction. - If outlier handling is needed for robustness, use **winsorization**: compute percentile cuts (e.g., 1st and 99th percentile) and cap outliers at those levels, rather than excluding them. - Always report both the raw mean and a winsorized mean if winsorizing. - Report the count and magnitude of extreme observations separately so the user can see what the tail looks like. Winsorization pattern: ```sql WITH returns AS ( SELECT symbol, return_pct FROM base_signals ), bounds AS ( SELECT PERCENTILE_CONT(0.01) WITHIN GROUP (ORDER BY return_pct) AS p01, PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY return_pct) AS p99 FROM returns ) SELECT ROUND(AVG(return_pct), 2) AS raw_mean, ROUND(AVG(GREATEST(LEAST(return_pct, b.p99), b.p01)), 2) AS winsorized_mean, ROUND(STDDEV(return_pct), 2) AS raw_stddev, COUNT(*) FILTER (WHERE return_pct > 500) AS extreme_winners_count, COUNT(*) FILTER (WHERE return_pct < -90) AS extreme_losers_count FROM returns CROSS JOIN bounds ``` #### R3: Single-date sampling produces noisy results. Sampling a signal on one calendar date per year (e.g., "Jan 15 each year") gives ~15 annual observations for a 15-year backtest. The result is sensitive to the chosen date because most technical indicators are autocorrelated over short windows. Required behavior: - For backtests with single-date annual sampling, note the date sensitivity in the response. - When feasible, run a multi-date version of the backtest (monthly or quarterly rebalances) and compare. If results differ substantially, the single-date result is noise; if they converge, the signal is more robust. - If running multi-date is too expensive, at minimum note: "This result is based on a single annual sampling date. Sampling on a different date could produce materially different results." Monthly rebalance pattern (denser signal, more robust): ```sql WITH monthly_signals AS ( SELECT symbol, date, close AS entry_price, indicator_value, LEAD(close, 21) OVER (PARTITION BY symbol ORDER BY date) AS price_1mo, ROW_NUMBER() OVER (PARTITION BY symbol, DATE_TRUNC('month', date) ORDER BY date) AS rn FROM shibui.stock_quotes sq INNER JOIN shibui.technical_indicators ti USING (symbol, date) WHERE date >= '2010-01-01' AND indicator_value IS NOT NULL ) SELECT * FROM monthly_signals WHERE rn = 1 LIMIT 200 ``` #### R4: Be honest when using indicator proxies. The user may ask for an indicator that isn't directly in the database (e.g., "Chaikin Money Flow", "Heikin-Ashi candles", "Ichimoku Cloud"). The available indicators are listed in the schema (see `technical_indicators` table). Substituting a related-but-different indicator without disclosure misleads the user. Required behavior: - If the user asks for an indicator not in the schema, do not substitute silently. - State explicitly: "Chaikin Money Flow is not in the database. The closest available proxy is `mfi_14` (Money Flow Index), which uses a related but distinct formula. Results for `mfi_14` may not generalize to CMF behavior." - Offer the user the choice: proceed with the proxy (with caveat), decline to run, or compute the indicator manually from price/volume if feasible. #### R5: Forward returns span calendar boundaries — label them honestly. A 252-trading-day forward return from January 15, 2010 ends approximately January 15, 2011. Labeling this as a "2010 return" is misleading — it's a forward-looking return spanning two calendar years. Required behavior: - Label backtest results as "signal year" rather than "return year", or use the entry-date and exit-date as explicit columns. - Note in the response: "Returns are forward-looking from the signal date. The '2010' row represents signals placed in early 2010 and held through early 2011." #### R6: Sample size matters more than win rate. A 65% win rate across 30 trades means almost nothing; a 55% win rate across 30,000 trades is meaningful. Backtest results with fewer than ~500 observations per group should be flagged as low-confidence. Required behavior: - Always include `COUNT(*)` per group in backtest output. - Flag groups with N < 500 explicitly: "The 2010 BUY group has only X observations — this row should not be over-interpreted." - For yearly breakdowns where N is naturally small per year, encourage the user to look at the aggregate result across all years before drawing conclusions from any single year. #### R7: Signal returns must be compared against the universe baseline. A signal group returning 12% is only meaningful if the universe returned less. Without a benchmark, the user cannot distinguish alpha (the signal's edge) from beta (the market moved). The database has no index data (no S&P 500, no SPY), so the benchmark is the universe's own average return — all stocks matching the base filters, ignoring the signal condition. This is a cleaner benchmark than an index because it controls for the exact universe definition (market-cap floor, date range, exchange). Required behavior: - Every backtest that reports a signal group return must also compute the full-universe average return for the same period and filters. - Report the spread (signal return minus universe return) alongside both figures. - If the spread is near zero or negative, say so plainly: "The signal did not outperform the universe average." Universe-benchmark pattern: ```sql WITH base AS ( SELECT sq.symbol, sq.date, sq.close AS entry_price, LEAD(sq.close, 252) OVER (PARTITION BY sq.symbol ORDER BY sq.date) AS price_1yr, ti.mfi_14 FROM shibui.stock_quotes sq INNER JOIN shibui.technical_indicators ti USING (symbol, date) WHERE sq.date >= '2010-01-01' AND sq.date <= '2023-01-01' ), returns AS ( SELECT symbol, date, mfi_14, CASE WHEN price_1yr IS NOT NULL THEN (price_1yr - entry_price) / NULLIF(entry_price, 0) * 100 END AS return_pct FROM base WHERE entry_price IS NOT NULL ) SELECT 'Signal (MFI >= 50)' AS group_label, COUNT(*) AS total_signals, COUNT(return_pct) AS with_forward_price, ROUND(AVG(return_pct), 2) AS avg_return FROM returns WHERE mfi_14 >= 50 UNION ALL SELECT 'Full universe' AS group_label, COUNT(*) AS total_signals, COUNT(return_pct) AS with_forward_price, ROUND(AVG(return_pct), 2) AS avg_return FROM returns ``` The "Full universe" row includes the signal group — this is intentional. The universe mean is the unconditional average. The difference (signal avg minus universe avg) is the signal's marginal contribution. #### R8: Check sector concentration of the signal group. A signal that appears profitable in aggregate may be overweight in one sector. If MFI >= 50 stocks are 60% tech in 2020-2021, the "alpha" is sector beta disguised as signal alpha. The `general_info` table has `gics_sector` (11 GICS sectors, ~5,800 of ~9,950 rows populated). Required behavior: - For any signal-based backtest, compute the sector breakdown of the signal group versus the full universe. - If any single sector accounts for more than 40% of the signal group (or is 2x its universe weight), flag it explicitly. - Note that ~4,150 symbols have NULL `gics_sector` (ETFs, preferred shares, closed-end funds). Report the NULL count but do not exclude these rows from the return calculation — only from the sector breakdown. Sector-concentration pattern: ```sql WITH base AS ( SELECT sq.symbol, sq.date, ti.mfi_14 FROM shibui.stock_quotes sq INNER JOIN shibui.technical_indicators ti USING (symbol, date) WHERE sq.date >= '2020-01-01' AND sq.date <= '2022-01-01' AND ti.mfi_14 IS NOT NULL ), signal_symbols AS ( SELECT DISTINCT symbol FROM base WHERE mfi_14 >= 50 ), universe_symbols AS ( SELECT DISTINCT symbol FROM base ) SELECT g.gics_sector, COUNT(*) FILTER (WHERE ss.symbol IS NOT NULL) AS signal_count, COUNT(*) AS universe_count, ROUND(COUNT(*) FILTER (WHERE ss.symbol IS NOT NULL) * 100.0 / NULLIF(SUM(COUNT(*) FILTER (WHERE ss.symbol IS NOT NULL)) OVER (), 0), 1) AS signal_pct, ROUND(COUNT(*) * 100.0 / NULLIF(SUM(COUNT(*)) OVER (), 0), 1) AS universe_pct FROM universe_symbols us INNER JOIN shibui.general_info g ON us.symbol = g.symbol LEFT JOIN signal_symbols ss ON us.symbol = ss.symbol WHERE g.gics_sector IS NOT NULL GROUP BY g.gics_sector ORDER BY signal_pct DESC LIMIT 20 ``` If `signal_pct` for any sector is substantially higher than `universe_pct`, the signal is sector-concentrated. Note this in the response and suggest re-running the backtest sector-neutral (equal-weighting sectors or excluding the dominant sector) to see if the signal survives. #### R9: Flag multiple-testing bias when several thresholds are compared. If the user tests MFI >= 40, 45, 50, 55, 60 and picks the best result, the winning threshold is biased upward. With five independent tests at the 5% significance level, the probability of at least one false positive is ~23%. This is the classic data-mining / p-hacking problem and applies equally to threshold sweeps, indicator selection, and holding-period optimization. Required behavior: - If the conversation includes multiple backtest variants (different thresholds, indicators, or holding periods), explicitly note that the best-performing variant benefits from selection bias. - State: "The best result out of N variants is expected to look better than its true forward performance. Out-of-sample validation or walk-forward testing (see Risk & validation patterns) is needed before treating this result as reliable." - Never present the best-of-N result as the expected forward performance without this caveat. - When feasible, suggest Bonferroni-style framing: "With N tests, the significance bar is higher — a result that looks marginal at the single-test level is likely noise." ### Caveats to include in the response (always, not optional) When presenting backtest results to the user, the response must include a "Caveats" section. The exact wording depends on the specific query, but the section must address each of the following that applies: 1. **Survivorship**: What percentage of signals had NULL forward prices, and what direction does that bias results? 2. **Outliers**: Are extreme returns being filtered, capped, or included raw? If filtered or capped, how does that affect the mean? 3. **Sampling design**: Single-date or multi-date? What does that imply for robustness? 4. **Indicator validity**: Is the indicator used the one the user asked for, or a proxy? What's the difference? 5. **Sample size**: Are any group sizes too small to draw conclusions? 6. **Calendar conventions**: Are returns labeled by signal date or exit date? Are weekends/holidays handled correctly? 7. **Transaction costs and slippage**: The backtest does not model trading costs, bid-ask spread, or market impact. Real-world returns would be lower, especially for strategies with high turnover. 8. **Look-ahead bias**: Is any data used in the signal that wasn't available at signal time? (Usually not, with our point-in-time data, but verify when fundamental signals are involved — restated fundamentals would be look-ahead.) 9. **Benchmark comparison**: Does the signal outperform the universe average? How large is the spread? A positive signal return with a near-zero or negative spread is not alpha (R7). 10. **Sector concentration**: Is the signal group overweight in any sector relative to the universe? If so, the result may be driven by sector performance rather than the signal itself (R8). 11. **Multiple testing**: Were multiple variants tested in this conversation? If so, the best result is biased upward by selection and should not be taken at face value without out-of-sample validation (R9). The response should be honest without being so long that the user stops reading. Aim for: result table, 2-3 sentences of headline interpretation, then a "Caveats" section of 3-5 bullets covering the issues most relevant to this specific backtest. ### Anti-patterns to avoid - **Don't** present a backtest as conclusive evidence. The honest framing is "in this sample, with these assumptions, the result was X." Forward-testing or out-of-sample validation is needed before any signal should be acted on. - **Don't** compare two strategies on average return alone. Compare on risk-adjusted basis (Sharpe-style: mean / stddev), win rate, max drawdown, and worst-year. A strategy with higher mean and much higher variance is not strictly better. - **Don't** ignore the universe-definition question. "All US stocks" vs "S&P 500 constituents" vs "market cap > $1B" produces very different backtest results for the same signal. Be explicit about the universe and acknowledge that the result is conditional on it. Compare signal returns against the universe average (R7) and check for sector concentration (R8). - **Don't** confuse "the signal correlates with positive returns" with "the signal causes positive returns" or "buying on the signal is a good strategy." Many signals correlate with returns because they correlate with broader factors (size, momentum, value, volatility) that drive returns. A proper backtest would benchmark against those factors or use factor-neutral construction. When multiple thresholds or variants are tested, the best result is subject to data-mining bias (R9). - **Don't** present signal returns without the universe baseline. A 12% signal return means nothing if the universe returned 14%. Always compute and show the spread (R7). ### Risk & validation patterns These patterns implement the risk-adjusted comparison and validation steps referenced above. Each integrates survivorship accounting (R1) and winsorization (R2) rather than silently filtering NULLs. #### Risk-adjusted metrics (Sharpe and Sortino) Sharpe measures return per unit of total volatility; Sortino uses only downside volatility, which matters more for the skewed return distributions common in backtests. Always report both alongside raw and winsorized means. ```sql WITH base AS ( SELECT sq.symbol, sq.date, sq.close AS entry_price, LEAD(sq.close, 252) OVER (PARTITION BY sq.symbol ORDER BY sq.date) AS price_1yr FROM shibui.stock_quotes sq WHERE sq.date >= '2010-01-01' ), returns AS ( SELECT entry_price, price_1yr, CASE WHEN price_1yr IS NOT NULL THEN (price_1yr - entry_price) / NULLIF(entry_price, 0) * 100 END AS return_pct FROM base WHERE entry_price IS NOT NULL ), bounds AS ( SELECT PERCENTILE_CONT(0.01) WITHIN GROUP (ORDER BY return_pct) AS p01, PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY return_pct) AS p99 FROM returns WHERE return_pct IS NOT NULL ) SELECT COUNT(*) AS total_signals, COUNT(return_pct) AS with_forward_price, COUNT(*) - COUNT(return_pct) AS survivorship_excluded, ROUND((COUNT(*) - COUNT(return_pct)) * 100.0 / NULLIF(COUNT(*), 0), 1) AS excluded_pct, ROUND(AVG(return_pct), 2) AS raw_mean, ROUND(AVG(GREATEST(LEAST(return_pct, b.p99), b.p01)), 2) AS winsorized_mean, ROUND(STDDEV(return_pct), 2) AS raw_stddev, ROUND(AVG(return_pct) / NULLIF(STDDEV(return_pct), 0), 3) AS sharpe, ROUND(AVG(return_pct) / NULLIF( STDDEV(CASE WHEN return_pct < 0 THEN return_pct END), 0 ), 3) AS sortino, ROUND(PERCENTILE_CONT(0.05) WITHIN GROUP (ORDER BY return_pct), 2) AS p05, ROUND(PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY return_pct), 2) AS p25, ROUND(PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY return_pct), 2) AS p75, ROUND(PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY return_pct), 2) AS p95 FROM returns CROSS JOIN bounds b ``` When comparing two strategies, compute Sharpe and Sortino for each group. A strategy with higher mean return but lower Sharpe is taking on proportionally more risk — the higher return may not compensate. #### Maximum drawdown Drawdown measures the worst peak-to-trough decline in cumulative returns. Use with R3's monthly rebalance pattern to track strategy performance across time and surface regime-dependent behavior. ```sql WITH monthly_signals AS ( SELECT sq.symbol, sq.date, sq.close AS entry_price, LEAD(sq.close, 21) OVER (PARTITION BY sq.symbol ORDER BY sq.date) AS price_1mo, ROW_NUMBER() OVER ( PARTITION BY sq.symbol, DATE_TRUNC('month', sq.date) ORDER BY sq.date ) AS rn FROM shibui.stock_quotes sq WHERE sq.date >= '2010-01-01' ), period_returns AS ( SELECT DATE_TRUNC('month', date) AS month, AVG((price_1mo - entry_price) / NULLIF(entry_price, 0) * 100) FILTER (WHERE price_1mo IS NOT NULL) AS avg_return, COUNT(*) AS signals, COUNT(*) - COUNT(price_1mo) AS survivorship_excluded FROM monthly_signals WHERE rn = 1 AND entry_price IS NOT NULL GROUP BY DATE_TRUNC('month', date) ), with_peak AS ( SELECT month, avg_return, signals, survivorship_excluded, SUM(avg_return) OVER (ORDER BY month) AS cumulative, MAX(SUM(avg_return) OVER (ORDER BY month)) OVER ( ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS peak FROM period_returns ) SELECT month, ROUND(avg_return, 2) AS period_return, ROUND(cumulative, 2) AS cumulative_return, ROUND(cumulative - peak, 2) AS drawdown, signals, survivorship_excluded FROM with_peak ORDER BY month LIMIT 200 ``` Report the maximum drawdown (most negative value) and the month it occurred. Strategies with similar average returns but very different max drawdowns have very different risk profiles. #### Walk-forward validation Walk-forward tests a strategy across sequential non-overlapping windows. If results are consistent across windows, the signal is more robust. If one window drives most of the aggregate return, the strategy may be overfitted to that market regime. ```sql WITH windows AS ( SELECT gs::date AS window_start, (gs + INTERVAL '3 years')::date AS window_end FROM generate_series( '2010-01-01'::date, '2022-01-01'::date, '3 years'::interval ) AS t(gs) ), base AS ( SELECT sq.symbol, sq.date, sq.close AS entry_price, LEAD(sq.close, 252) OVER (PARTITION BY sq.symbol ORDER BY sq.date) AS price_1yr FROM shibui.stock_quotes sq WHERE sq.date >= '2010-01-01' ), windowed AS ( SELECT w.window_start, w.window_end, b.entry_price, b.price_1yr, CASE WHEN b.price_1yr IS NOT NULL THEN (b.price_1yr - b.entry_price) / NULLIF(b.entry_price, 0) * 100 END AS return_pct FROM base b INNER JOIN windows w ON b.date >= w.window_start AND b.date < w.window_end WHERE b.entry_price IS NOT NULL ) SELECT window_start, window_end, COUNT(*) AS total_signals, COUNT(return_pct) AS with_forward_price, COUNT(*) - COUNT(return_pct) AS survivorship_excluded, ROUND(AVG(return_pct), 2) AS avg_return, ROUND(STDDEV(return_pct), 2) AS stddev, ROUND(AVG(return_pct) / NULLIF(STDDEV(return_pct), 0), 3) AS sharpe FROM windowed GROUP BY window_start, window_end ORDER BY window_start LIMIT 200 ``` If Sharpe varies widely across windows (e.g., positive in one, negative in another), the aggregate result is misleading. Report per-window results alongside the aggregate. ### Output format for backtest responses Structure backtest responses as: 1. **Headline result** (1-2 sentences): the most important takeaway, stated plainly. "MFI ≥ 50 produced an average 1-year return of X% vs Y% for MFI < 50, over Z signals across 2010-2024." 2. **Result table**: the grouped statistics, with sample sizes always visible. 3. **Caveats** (3-5 bullets): the methodological issues most relevant to this specific backtest. Be specific — "survivorship bias likely inflates returns by ~X%" is more useful than "results may be biased." 4. **Suggested next step**: if the result is encouraging, what would validate it? Out-of-sample test, different universe, different sampling date, factor-neutral construction, etc. Treat the backtest as the first step of validation, not the last.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, consistent with loading guardrails. Description adds behavioral context about hard rules for survivorship bias, outlier handling, etc., beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is verbose with extensive examples and rules, but well-structured with bullet points and sections. Front-loaded with core action and requirements. Some verbosity is justified by complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values are covered. Description provides complete information on what the tool loads, including detailed rules and examples, ensuring the agent can use it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Single parameter '_content' has 0% schema description coverage, but its default value contains the entire content, making it self-explanatory. Description does not add further semantics, but baseline is acceptable given the parameter's nature.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states 'Load backtesting methodology guardrails and risk-analysis patterns' and specifies when to call it, clearly distinguishing from siblings by noting required preceding tools and combinability with other workflow tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly requires get_database_schema and get_query_patterns to be called first, and lists exact user requests that trigger this tool, providing clear when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

load_comparison_workflowPeer Comparison WorkflowA
Read-onlyIdempotent
Inspect

Load comparative analysis workflow with advanced query patterns. REQUIRES get_database_schema then get_query_patterns to be called first (in that order). Call BEFORE writing SQL for complex queries when the user asks to compare companies, "X vs Y", peer benchmarking, sector ranking, or relative valuation. Can be combined with other workflow tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
_contentNo## Comparative Analysis Workflow ### Persona You are an analyst producing peer comparison reports. You focus on apples-to-apples comparisons - same sector, similar size, comparable business models. You highlight where a company stands out (positively or negatively) relative to peers. ### Workflow 1. **Validate comparability**: Check that companies are in the same or adjacent GICS sector/industry. If not, note that the comparison is cross-sector and metrics may not be directly comparable. 2. **Size context**: Note market cap differences. A $10B company vs a $500B company will naturally differ in growth rates, margins, and multiples. 3. **Snapshot comparison**: Pull latest valuation + fundamentals overview fields for all symbols. Compare P/E, EV/EBITDA, margins, return_on_equity. 4. **Trend comparison**: Compare quarterly revenue and earnings trajectories from fundamentals. Are they converging or diverging? 5. **Price performance**: Use P2 pattern for returns over same period. 6. **Relative strengths**: Identify what each company does better. Avoid declaring a "winner" - different investors value different attributes. ### Output Format - **Comparison Overview**: Brief context on why these companies are being compared (same industry, competitors, etc.) - **Snapshot Table**: Side-by-side metrics (inline markdown) - **Key Differentiators**: 2-3 sentences on what distinguishes each - **Trend Context**: Are the gaps widening or narrowing? - **Caveats**: Size differences, data staleness, sector mismatches ### Advanced Query Patterns #### C1: Side-by-side snapshot (N companies) ```sql WITH latest_val AS ( SELECT symbol, market_cap, pe_ratio, price_to_book, peg_ratio, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days' ), latest_q AS ( SELECT symbol, profit_margin, return_on_equity, revenue_growth_yoy, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_quarterly WHERE date >= CURRENT_DATE - INTERVAL '6 months' ), latest_dd AS ( SELECT symbol, ev_ebitda, dividend_yield, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_derived_daily WHERE date >= CURRENT_DATE - INTERVAL '7 days' ) SELECT g.symbol, g.name, g.gics_industry, ROUND(v.market_cap / 1e9, 1) AS market_cap_bln, ROUND(v.pe_ratio, 2) AS pe, ROUND(dd.ev_ebitda, 2) AS ev_ebitda, ROUND(f.profit_margin * 100, 1) AS margin_pct, ROUND(f.return_on_equity * 100, 1) AS roe_pct, ROUND(f.revenue_growth_yoy * 100, 1) AS rev_growth_pct, ROUND(dd.dividend_yield * 100, 2) AS div_yield_pct, os.percent_insiders, os.short_percent_float FROM shibui.general_info g INNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1 LEFT JOIN latest_q f ON g.symbol = f.symbol AND f.rn = 1 LEFT JOIN latest_dd dd ON g.symbol = dd.symbol AND dd.rn = 1 LEFT JOIN shibui.ownership_stats os ON g.symbol = os.symbol WHERE g.symbol IN ('AAPL.NASDAQ', 'MSFT.NASDAQ', 'GOOGL.NASDAQ') ORDER BY v.market_cap DESC LIMIT 10 ``` #### C2: Revenue and margin trend comparison (last 8 quarters) ```sql SELECT f.symbol, f.date, f.revenue, ROUND(f.gross_profit / NULLIF(f.revenue, 0) * 100, 1) AS gross_margin_pct, ROUND(f.net_income / NULLIF(f.revenue, 0) * 100, 1) AS net_margin_pct, ROUND(f.operating_income / NULLIF(f.revenue, 0) * 100, 1) AS op_margin_pct FROM shibui.fundamentals_quarterly f WHERE f.symbol IN ('AAPL.NASDAQ', 'MSFT.NASDAQ') AND f.date >= CURRENT_DATE - INTERVAL '2 years' AND f.revenue IS NOT NULL ORDER BY f.symbol, f.date DESC LIMIT 20 ``` #### C3: Price performance comparison (multiple timeframes) ```sql WITH prices AS ( SELECT symbol, date, close, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn, FIRST_VALUE(close) OVER (PARTITION BY symbol ORDER BY date ASC) AS start_90d FROM shibui.stock_quotes WHERE symbol IN ('AAPL.NASDAQ', 'MSFT.NASDAQ', 'GOOGL.NASDAQ') AND date >= CURRENT_DATE - INTERVAL '90 days' ), prices_1y AS ( SELECT symbol, FIRST_VALUE(close) OVER (PARTITION BY symbol ORDER BY date ASC) AS start_1y, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.stock_quotes WHERE symbol IN ('AAPL.NASDAQ', 'MSFT.NASDAQ', 'GOOGL.NASDAQ') AND date >= CURRENT_DATE - INTERVAL '1 year' ) SELECT p.symbol, ROUND(p.close, 2) AS current_price, ROUND((p.close - p.start_90d) / NULLIF(p.start_90d, 0) * 100, 1) AS return_90d_pct, ROUND((p.close - y.start_1y) / NULLIF(y.start_1y, 0) * 100, 1) AS return_1y_pct FROM prices p LEFT JOIN prices_1y y ON p.symbol = y.symbol AND y.rn = 1 WHERE p.rn = 1 ORDER BY return_90d_pct DESC LIMIT 10 ```

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnly/idempotent safety profile; description adds critical behavioral context that this 'has no schema' (requires external schema loading) and exposes composability with other workflows. Captures dependency graph information not present in annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four efficient sentences with zero redundancy: (1) purpose definition, (2) hard prerequisite, (3) usage conditions and timing, (4) composability note. Front-loaded with action verb and appropriately sized for the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the existence of an output schema (return values need not be described) and rich annotations, the description adequately covers the tool's role in the workflow chain. Only gap is the lack of explicit parameter documentation given 0% schema coverage, though the default value compensates practically.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage for the _content parameter. Description mentions 'advanced query patterns' which semantically maps to the SQL examples in the parameter's default value, providing minimal grounding. However, it fails to explicitly document that _content contains the workflow definition/instructions or explain the parameter's role in the tool's operation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description uses specific verb 'Load' with resource 'comparative analysis workflow' and clearly distinguishes from siblings by enumerating specific use cases: 'compare companies', 'X vs Y', 'peer benchmarking', 'sector ranking', or 'relative valuation'. Immediately contrasts with load_earnings_workflow, load_fundamental_workflow, etc.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit prerequisite chain ('REQUIRES get_database_schema to be called first'), temporal ordering ('Call BEFORE writing SQL'), specific trigger conditions (comparison queries), and sibling interaction pattern ('Can be combined with other workflow tools'). Covers when-to-use and prerequisites comprehensively.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

load_earnings_workflowEarnings Analysis WorkflowA
Read-onlyIdempotent
Inspect

Load earnings analysis workflow with advanced query patterns. REQUIRES get_database_schema then get_query_patterns to be called first (in that order). Call BEFORE writing SQL for complex queries when the user asks about earnings results, EPS surprises, beat/miss history, earnings estimates, quarterly reporting, or earnings season analysis. Can be combined with other workflow tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
_contentNo## Earnings Analysis Workflow ### Persona You are an earnings analyst who tracks quarterly results and identifies patterns in surprises. You contextualize EPS numbers with revenue trends and margin shifts - an EPS beat from cost cuts is different from one driven by revenue growth. ### Key Data Notes - `earnings_quarterly`: has eps_actual, eps_estimate, eps_difference, surprise_percent, before_after_market ('AfterMarket'|'BeforeMarket') - Dates in earnings_quarterly are period-end dates (month-end), not announcement dates. Use `report_date` for actual announcement date. - Pair earnings data with `fundamentals_quarterly` for full context (revenue, margins, one-time items) - join on (symbol, date) - Forward EPS estimates are in `analyst_estimates` table (eps_estimate_current_year/quarter, next_year/quarter) ### Workflow 1. **Recent result**: Latest quarter EPS actual vs estimate + surprise % 2. **Trend**: Last 4-8 quarters of beats/misses (consistency matters) 3. **Revenue context**: Did revenue also beat? Revenue miss + EPS beat = cost-cutting, not organic growth 4. **Margin trajectory**: Are margins expanding or compressing? 5. **Forward estimates**: eps_estimate_current_quarter and next_quarter from analyst_estimates table (if available) 6. **Sector context**: How did peers perform in the same quarter? ### Output Format - **Latest Quarter**: EPS actual vs estimate, surprise %, revenue - **Track Record**: Table of last 4-8 quarters with beat/miss - **Quality Assessment**: Revenue-driven vs cost-driven performance - **Forward Look**: Current estimates (if available) and trend context ### Advanced Query Patterns #### E1: Earnings history with revenue context ```sql WITH eps AS ( SELECT symbol, date, report_date, before_after_market, eps_actual, eps_estimate, surprise_percent, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.earnings_quarterly WHERE symbol = 'AAPL.NASDAQ' AND date >= CURRENT_DATE - INTERVAL '2 years' AND eps_actual IS NOT NULL ) SELECT e.date, e.report_date, e.before_after_market, ROUND(e.eps_actual, 2) AS actual, ROUND(e.eps_estimate, 2) AS estimate, ROUND(e.surprise_percent, 1) AS surprise_pct, CASE WHEN e.surprise_percent > 0 THEN 'Beat' ELSE 'Miss' END AS result, f.revenue, ROUND(f.net_income / NULLIF(f.revenue, 0) * 100, 1) AS net_margin_pct FROM eps e LEFT JOIN shibui.fundamentals_quarterly f ON e.symbol = f.symbol AND e.date = f.date WHERE e.rn <= 8 ORDER BY e.date DESC LIMIT 8 ``` #### E2: Sector earnings season summary (latest quarter) ```sql WITH latest_eps AS ( SELECT e.symbol, e.date, e.eps_actual, e.eps_estimate, e.surprise_percent, ROW_NUMBER() OVER (PARTITION BY e.symbol ORDER BY e.date DESC) AS rn FROM shibui.earnings_quarterly e WHERE e.date >= CURRENT_DATE - INTERVAL '4 months' AND e.eps_actual IS NOT NULL AND e.eps_estimate IS NOT NULL ) SELECT g.gics_sector, COUNT(*) AS reported, COUNT(*) FILTER (WHERE le.surprise_percent > 0) AS beats, COUNT(*) FILTER (WHERE le.surprise_percent < 0) AS misses, ROUND(AVG(le.surprise_percent), 1) AS avg_surprise_pct, ROUND(MEDIAN(le.surprise_percent), 1) AS median_surprise_pct FROM latest_eps le INNER JOIN shibui.general_info g ON le.symbol = g.symbol WHERE le.rn = 1 AND g.type = 'Common Stock' GROUP BY g.gics_sector ORDER BY avg_surprise_pct DESC LIMIT 15 ``` #### E3: Consecutive beat streak leaders ```sql WITH recent_eps AS ( SELECT symbol, date, surprise_percent, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.earnings_quarterly WHERE date >= CURRENT_DATE - INTERVAL '2 years' AND eps_actual IS NOT NULL AND eps_estimate IS NOT NULL ), streaks AS ( SELECT symbol, MIN(CASE WHEN surprise_percent <= 0 THEN rn END) AS first_miss_rn FROM recent_eps WHERE rn <= 8 GROUP BY symbol ), latest_val AS ( SELECT symbol, market_cap, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days' ) SELECT g.symbol, g.name, g.gics_sector, COALESCE(s.first_miss_rn - 1, 8) AS consecutive_beats, ROUND(v.market_cap / 1e6, 0) AS market_cap_mln FROM streaks s INNER JOIN shibui.general_info g ON s.symbol = g.symbol INNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1 WHERE COALESCE(s.first_miss_rn - 1, 8) >= 4 AND g.type = 'Common Stock' AND v.market_cap > 500e6 ORDER BY consecutive_beats DESC, v.market_cap DESC LIMIT 20 ```

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnly/idempotent/destructiveHint=false. The description adds critical behavioral context: 'this tool has no schema' (explains external dependency), 'Can be combined with other workflow tools' (interaction pattern), and implies it provides methodology rather than raw data. Could better clarify that it returns/guides the SQL workflow content.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, zero waste. Front-loaded with purpose, followed by hard requirement (REQUIRES), usage timing (BEFORE), trigger examples, and combinability. Strategic capitalization draws attention to critical sequencing constraints without verbose explanation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complex workflow nature and presence of output schema, the description appropriately focuses on usage prerequisites and domain scope (earnings-specific). It correctly notes the schema dependency gap. Could improve by stating that the tool populates the analytical context with SQL patterns/workflows, but the content is largely complete for agent selection.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% (the _content parameter lacks description). The description compensates partially by explaining what the workflow contains ('advanced query patterns', SQL examples implicitly), but never mentions the _content parameter or explains that the workflow definition is passed/loaded via this parameter. Baseline 3 for high schema coverage not met; description explains concept but not parameter mechanics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it 'Load[s] earnings analysis workflow with advanced query patterns' — specific verb (load) and resource (earnings workflow). It distinguishes from siblings (load_comparison_workflow, load_technical_workflow, etc.) by specifying 'earnings' and listing earnings-specific use cases (EPS surprises, beat/miss history). Minor deduction because 'load' could ambiguously mean 'execute' vs 'retrieve instructions', though context clarifies.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Excellent guidance: explicit prerequisite ('REQUIRES get_database_schema to be called first'), clear timing ('Call BEFORE writing SQL'), specific trigger conditions ('when the user asks about earnings results, EPS surprises...'), and combinability note ('Can be combined with other workflow tools'). Provides complete decision framework for the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

load_fundamental_workflowFundamental Analysis WorkflowA
Read-onlyIdempotent
Inspect

Load fundamental analysis workflow with advanced query patterns. REQUIRES get_database_schema then get_query_patterns to be called first (in that order). Call BEFORE writing SQL for complex queries when the user asks about company valuation, financial health, investment quality, earnings trends, profitability, "is X a good buy", or any deep-dive company analysis. Can be combined with other workflow tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
_contentNo## Fundamental Analysis Workflow ### Persona You are a senior equity research analyst. Your analysis should be structured, evidence-based, and balanced. Lead with conclusions, support with data, and always note limitations and risks. ### Workflow (follow in order) 1. **Identify**: Look up symbol via general_info. Note gics_sector, gics_industry_group, gics_industry, full_time_employees, ipo_date. 2. **Current Snapshot**: Pull latest valuation (daily) + latest fundamentals_quarterly for pe_ratio, profit_margin, return_on_equity, market_cap. 3. **Financial Trends** (last 8 quarters from `fundamentals_quarterly`): - Revenue + margin trajectory - Balance sheet health: current_ratio, debt_to_equity, cash_and_equivalents - Cash flow: operating_cash_flow, free_cash_flow, capex intensity, stock_based_compensation as % of revenue - Profitability: return_on_invested_capital from fundamentals_derived_quarterly 4. **Earnings Quality**: - EPS surprise history (earnings_quarterly, last 4-8 quarters) - Revenue growth vs earnings growth (divergence = red flag) - SBC relative to net income (>50% = dilution concern) 5. **Peer Comparison**: - Find 3-5 peers in same gics_industry (or gics_industry_group) with similar market cap - Compare: P/E, profit margin, return_on_equity, revenue growth, FCF yield - Use sector benchmark pattern (P9) for context 6. **Valuation Assessment**: - P/E vs peers and sector average (from valuation table) - EV/EBITDA vs peers (from fundamentals_derived_daily) - PEG ratio if available (peg_ratio from valuation) - Note: this data cannot produce a DCF - no forward estimates beyond 1-year EPS. Be honest about this limitation. ### Output Format Structure your response as: - **Summary** (2-3 sentences: bull case, bear case, overall lean) - **Key Metrics** (inline markdown table) - **Financial Trends** (what direction are revenues, margins, cash flow heading) - **Peer Context** (where does this company sit vs competitors) - **Risks & Limitations** (data gaps, staleness, what you can't assess) ### Advanced Query Patterns #### F1: Quarterly financial trend with YoY and QoQ growth ```sql WITH quarterly AS ( SELECT symbol, date, revenue, gross_profit, net_income, operating_income, research_and_development, ebitda, LAG(revenue, 1) OVER (PARTITION BY symbol ORDER BY date) AS prev_q_rev, LAG(revenue, 4) OVER (PARTITION BY symbol ORDER BY date) AS yoy_rev FROM shibui.fundamentals_quarterly WHERE symbol = 'AAPL.NASDAQ' AND date >= CURRENT_DATE - INTERVAL '3 years' AND revenue IS NOT NULL ) SELECT date, revenue, gross_profit, net_income, ROUND(gross_profit / NULLIF(revenue, 0) * 100, 1) AS gross_margin_pct, ROUND(net_income / NULLIF(revenue, 0) * 100, 1) AS net_margin_pct, ROUND(research_and_development / NULLIF(revenue, 0) * 100, 1) AS rd_pct, ROUND((revenue - prev_q_rev) / NULLIF(prev_q_rev, 0) * 100, 1) AS qoq_pct, ROUND((revenue - yoy_rev) / NULLIF(yoy_rev, 0) * 100, 1) AS yoy_pct FROM quarterly WHERE prev_q_rev IS NOT NULL ORDER BY date DESC LIMIT 12 ``` #### F2: Cash flow quality assessment ```sql WITH q AS ( SELECT symbol, date, operating_cash_flow, capex, free_cash_flow, stock_based_compensation, ABS(dividends_paid) AS dividends, net_income, revenue, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_quarterly WHERE symbol = 'AAPL.NASDAQ' AND date >= CURRENT_DATE - INTERVAL '3 years' ) SELECT date, operating_cash_flow, free_cash_flow, capex, stock_based_compensation, dividends, net_income, ROUND(operating_cash_flow / NULLIF(net_income, 0), 2) AS cf_to_earnings_ratio, ROUND(stock_based_compensation / NULLIF(revenue, 0) * 100, 1) AS sbc_pct_of_revenue, ROUND(ABS(capex) / NULLIF(operating_cash_flow, 0) * 100, 1) AS capex_intensity_pct FROM q WHERE rn <= 8 ORDER BY date DESC LIMIT 8 ``` #### F3: Peer comparison (same GICS industry, similar size) ```sql WITH target AS ( SELECT g.gics_industry, v.market_cap FROM shibui.general_info g INNER JOIN (SELECT symbol, market_cap, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days') v ON g.symbol = v.symbol AND v.rn = 1 WHERE g.symbol = 'AAPL.NASDAQ' ), latest_q AS ( SELECT symbol, return_on_equity, profit_margin, revenue_growth_yoy, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_quarterly WHERE date >= CURRENT_DATE - INTERVAL '6 months' ), latest_val AS ( SELECT symbol, market_cap, pe_ratio, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days' ), latest_dd AS ( SELECT symbol, ev_ebitda, dividend_yield, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_derived_daily WHERE date >= CURRENT_DATE - INTERVAL '7 days' ) SELECT g.symbol, g.name, g.gics_industry, ROUND(v.market_cap / 1e9, 1) AS market_cap_bln, ROUND(v.pe_ratio, 2) AS pe, ROUND(f.profit_margin * 100, 1) AS margin_pct, ROUND(f.return_on_equity * 100, 1) AS roe_pct, ROUND(f.revenue_growth_yoy * 100, 1) AS rev_growth_pct, ROUND(dd.ev_ebitda, 2) AS ev_ebitda FROM shibui.general_info g INNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1 INNER JOIN latest_q f ON g.symbol = f.symbol AND f.rn = 1 LEFT JOIN latest_dd dd ON g.symbol = dd.symbol AND dd.rn = 1 CROSS JOIN target t WHERE g.gics_industry = t.gics_industry AND g.type = 'Common Stock' AND v.market_cap > t.market_cap * 0.1 ORDER BY v.market_cap DESC LIMIT 15 ``` #### F4: Balance sheet health over time ```sql WITH bs AS ( SELECT symbol, date, current_assets, current_liabilities, current_ratio, total_assets, total_liabilities, equity, debt, cash_and_equivalents, debt_to_equity, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_quarterly WHERE symbol = 'AAPL.NASDAQ' AND date >= CURRENT_DATE - INTERVAL '3 years' ) SELECT date, ROUND(current_ratio, 2) AS current_ratio, ROUND(debt_to_equity, 2) AS debt_to_equity, cash_and_equivalents, debt, equity FROM bs WHERE rn <= 8 ORDER BY date DESC LIMIT 8 ```

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds critical dependency context beyond annotations: discloses that the tool lacks database schema ('this tool has no schema'), explaining the hard prerequisite. Annotations cover safety (readOnly/idempotent), while description adds functional context about query patterns and workflow structure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Perfectly structured and front-loaded: 4 sentences covering purpose, prerequisite, trigger conditions, and combinability. No redundancy, no tautology, every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Complete for a workflow tool: covers prerequisites, usage timing, and sibling relationships. Output schema exists (per context signals), so return values need not be explained. Could slightly improve by explicitly stating it returns workflow instructions, but adequate given complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage for the single `_content` parameter. Description partially compensates by characterizing the content ('advanced query patterns', 'workflow'), but does not explicitly explain that `_content` contains the workflow template or how the parameter should be used. Baseline 3 appropriate given some semantic hints provided.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Excellent specificity: verb 'Load' + resource 'fundamental analysis workflow' + distinguishes from siblings (technical, earnings, comparison, screening workflows via explicit 'fundamental' scope). Mentions 'advanced query patterns' to differentiate content type.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Exceptional guidance: explicit prerequisite ('REQUIRES get_database_schema to be called first'), explicit trigger conditions ('when the user asks about company valuation...'), ordering constraint ('Call BEFORE writing SQL'), and combination guidance ('Can be combined with other workflow tools').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

load_screening_workflowStock Screening WorkflowA
Read-onlyIdempotent
Inspect

Load quantitative screening workflow with advanced query patterns. REQUIRES get_database_schema then get_query_patterns to be called first (in that order). Call BEFORE writing SQL for complex queries when the user asks to find, screen, rank, or filter stocks by multiple criteria — "find stocks that...", "top N by...", sector-wide ranking, or any multi-factor selection. Can be combined with other workflow tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
_contentNo## Quantitative Screening Workflow ### Persona You are a quantitative analyst building stock screens. You think carefully about universe definition, filter interactions, survivorship bias, and result quality. You always explain what the screen found AND what it excluded. ### Workflow 1. **Define universe**: Start with base filters: - `g.type = 'Common Stock'` (excludes ADRs, CEFs, REITs, MLPs) - Optionally add: `g.country_iso = 'US'` for US-only - Optionally add market cap filter via `valuation` (latest date) - Always use `general_info` as the base table (9,952 rows, 1:1 with `symbols`) 2. **Apply filters incrementally**: Each filter reduces the universe. Note the reduction at each step. Warn if <10 results remain. 3. **Check NULL exclusion**: Filters on nullable columns silently exclude NULLs. Key columns: - `pe_ratio` in valuation: ~9% NULL on latest date (unprofitable companies excluded) - `dividend_yield` in fundamentals_derived_daily: 0 for non-payers, NULL only when no data (~3%) - Overview fields in fundamentals_quarterly: significantly nullable (current_ratio ~76% populated, return_on_equity ~90%, profit_margin ~91%, piotroski_f_score ~89%) - Financial statement columns: variably NULL Tell the user what percentage of the universe was excluded by NULLs. 4. **Apply ranking/sorting**: Use ORDER BY with the primary criterion. 5. **Validate results**: Check for ADRs, data anomalies, stale data. ### Common Screening Pitfalls - **Survivorship bias**: The database includes delisted companies with historical data. Screens on current metrics naturally exclude failed companies. Note this limitation. - **ADR inflation**: Foreign ADRs on NYSE/NASDAQ can inflate yield, FCF, and margin metrics. Filter with `g.type = 'Common Stock'` to exclude them. - **NULL interaction**: `WHERE pe_ratio < 15 AND current_ratio > 1.5` silently drops ~9% (no P/E) + ~24% (no current_ratio). Multiple nullable filters compound — check data availability at each step. - **Micro-cap noise**: Stocks with market_cap < $100M often have unreliable financial data and low liquidity. Default to market_cap > $500M unless user specifies otherwise. ### Output Format - **Screen Summary**: What you searched for, universe size, result count - **Results Table**: Inline markdown, sorted by primary criterion - **Exclusion Note**: What filters removed and approximate % excluded - **Caveats**: Any data quality concerns with the results ### Advanced Query Patterns #### S1: Multi-factor value screen ```sql WITH latest_val AS ( SELECT symbol, pe_ratio, price_to_book, market_cap, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days' ), latest_q AS ( SELECT symbol, return_on_equity, profit_margin, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_quarterly WHERE date >= CURRENT_DATE - INTERVAL '6 months' ), latest_dd AS ( SELECT symbol, dividend_yield, ev_ebitda, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_derived_daily WHERE date >= CURRENT_DATE - INTERVAL '7 days' ) SELECT g.symbol, g.name, g.gics_sector, g.gics_industry, ROUND(v.market_cap / 1e6, 0) AS market_cap_mln, ROUND(v.pe_ratio, 2) AS pe, ROUND(dd.ev_ebitda, 2) AS ev_ebitda, ROUND(v.price_to_book, 2) AS pb, ROUND(f.return_on_equity * 100, 1) AS roe_pct, ROUND(f.profit_margin * 100, 1) AS margin_pct, ROUND(dd.dividend_yield * 100, 2) AS div_yield_pct FROM shibui.general_info g INNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1 INNER JOIN latest_q f ON g.symbol = f.symbol AND f.rn = 1 LEFT JOIN latest_dd dd ON g.symbol = dd.symbol AND dd.rn = 1 WHERE g.type = 'Common Stock' AND v.market_cap > 500e6 AND v.pe_ratio BETWEEN 5 AND 20 AND f.return_on_equity > 0.12 AND f.profit_margin > 0.08 ORDER BY v.pe_ratio ASC LIMIT 30 ``` #### S2: Growth screen (revenue acceleration + earnings beats) ```sql WITH rev_growth AS ( SELECT symbol, date, revenue, LAG(revenue, 4) OVER (PARTITION BY symbol ORDER BY date) AS rev_yoy, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_quarterly WHERE date >= CURRENT_DATE - INTERVAL '2 years' AND revenue IS NOT NULL AND revenue > 0 ), latest_rev AS ( SELECT symbol, ROUND((revenue - rev_yoy) / NULLIF(rev_yoy, 0) * 100, 1) AS yoy_growth_pct FROM rev_growth WHERE rn = 1 AND rev_yoy IS NOT NULL ), recent_beats AS ( SELECT symbol, COUNT(*) FILTER (WHERE surprise_percent > 0) AS beats, COUNT(*) AS quarters FROM shibui.earnings_quarterly WHERE date >= CURRENT_DATE - INTERVAL '1 year' AND eps_actual IS NOT NULL AND eps_estimate IS NOT NULL GROUP BY symbol ), latest_val AS ( SELECT symbol, pe_ratio, market_cap, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days' ) SELECT g.symbol, g.name, g.gics_sector, ROUND(v.market_cap / 1e6, 0) AS market_cap_mln, lr.yoy_growth_pct, rb.beats || '/' || rb.quarters AS beat_rate, ROUND(v.pe_ratio, 2) AS pe FROM latest_rev lr INNER JOIN shibui.general_info g ON lr.symbol = g.symbol INNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1 LEFT JOIN recent_beats rb ON g.symbol = rb.symbol WHERE g.type = 'Common Stock' AND v.market_cap > 5e8 AND lr.yoy_growth_pct > 15 AND rb.beats >= 3 ORDER BY lr.yoy_growth_pct DESC LIMIT 30 ``` **Shortcut**: `fundamentals_quarterly` has pre-computed `revenue_growth_yoy` and `eps_growth_yoy` (decimal fractions: 0.15 = 15%). Use these instead of manual LAG() when YoY growth is the only metric needed. #### S3: Short squeeze candidates ```sql WITH latest_val AS ( SELECT symbol, pe_ratio, market_cap, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days' ) SELECT g.symbol, g.name, g.gics_sector, ROUND(v.market_cap / 1e6, 0) AS market_cap_mln, ROUND(os.short_percent_float * 100, 2) AS short_pct_float, ROUND(os.percent_institutions, 1) AS inst_pct, ROUND(v.pe_ratio, 2) AS pe FROM shibui.general_info g INNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1 INNER JOIN shibui.ownership_stats os ON g.symbol = os.symbol WHERE g.type = 'Common Stock' AND os.short_percent_float > 0.15 AND v.market_cap BETWEEN 3e8 AND 1e10 ORDER BY os.short_percent_float DESC LIMIT 20 ``` #### S4: Dividend quality screen (yield + coverage + growth) ```sql WITH latest_q AS ( SELECT symbol, date, free_cash_flow, dividends_paid, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_quarterly WHERE date >= CURRENT_DATE - INTERVAL '6 months' AND dividends_paid IS NOT NULL AND dividends_paid != 0 ), latest_dd AS ( SELECT symbol, dividend_yield, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.fundamentals_derived_daily WHERE date >= CURRENT_DATE - INTERVAL '7 days' ), latest_val AS ( SELECT symbol, pe_ratio, market_cap, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days' ) SELECT g.symbol, g.name, g.gics_sector, ROUND(v.market_cap / 1e6, 0) AS market_cap_mln, ROUND(dd.dividend_yield * 100, 2) AS yield_pct, ROUND(f.free_cash_flow / NULLIF(ABS(f.dividends_paid), 0), 2) AS fcf_coverage, ROUND(v.pe_ratio, 2) AS pe FROM shibui.general_info g INNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1 INNER JOIN latest_q f ON g.symbol = f.symbol AND f.rn = 1 INNER JOIN latest_dd dd ON g.symbol = dd.symbol AND dd.rn = 1 WHERE g.type = 'Common Stock' AND g.country_iso = 'US' AND dd.dividend_yield > 0.02 AND f.free_cash_flow / NULLIF(ABS(f.dividends_paid), 0) > 1.2 AND v.market_cap > 500e6 ORDER BY dd.dividend_yield DESC LIMIT 20 ```

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover read-only/idempotent safety. Description adds crucial behavioral context: explains prerequisite chain (why get_database_schema is needed), warns 'this tool has no schema', and clarifies its role in the SQL generation workflow. Could add what the output contains (SQL templates, pitfalls).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Perfectly structured: action statement, prerequisite warning, usage triggers with examples, and integration note. Four sentences with zero redundancy, front-loaded with the most critical information (what it does and requires).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Strong coverage given complexity: explains when/why to use, prerequisites, and sibling relationships. Output schema exists so return values need not be described. Minor gap: does not summarize what the loaded workflow contains (e.g., 'returns SQL templates and screening guidelines').

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% (the _content parameter lacks a description field). Description mentions 'advanced query patterns' vaguely but fails to explicitly document that _content contains the workflow document, that it has a default value with embedded SQL templates, or what valid inputs might be. Insufficient compensation for zero schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clear specific verb ('Load') + resource ('quantitative screening workflow'). Explicitly distinguishes from sibling workflow tools by specifying 'screening' context and noting it 'Can be combined with other workflow tools', placing it within the suite of load_*_workflow tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Excellent explicit guidance: states prerequisite 'REQUIRES get_database_schema to be called first', specifies exact trigger phrases ('find stocks that...', 'top N by...'), and defines temporal relationship ('Call BEFORE writing SQL'). Also notes combinatorial usage with other tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

load_technical_workflowTechnical Analysis WorkflowA
Read-onlyIdempotent
Inspect

Load technical analysis workflow with indicator interpretation guide and advanced query patterns. REQUIRES get_database_schema then get_query_patterns to be called first (in that order). Call BEFORE writing SQL for complex queries when the user asks about RSI, MACD, moving averages, support/resistance, overbought/oversold, chart patterns, momentum, trend analysis, or entry/exit signals. Can be combined with other workflow tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
_contentNo## Technical Analysis Workflow ### Persona You are a technical analyst specializing in momentum and trend- following strategies. You interpret indicator combinations, not single signals. You always note the limitations of backward- looking indicators and never claim predictive certainty. ### Indicator Interpretation Guide **Trend indicators** (direction of the trend): - `sma_200`: Long-term trend. Price > SMA-200 = bullish. Below = bearish. - `ema_50` / `ema_20`: Medium/short-term trend. - Golden cross: ema_50 crosses above sma_200. Death cross: below. - **Stage 2 (Weinstein)**: Price above rising SMA-200. Identify: `close > sma_200` AND SMA-200 trending up (compare to prior day via LAG). **Momentum indicators** (strength and speed): - `rsi_14`: >70 overbought, <30 oversold, 40-60 neutral. Divergence (price up, RSI down) = weakening momentum. - `macd` / `macdsignal` / `macdhist`: MACD > signal = bullish. Histogram expanding = momentum increasing. Contracting = fading. - `stoch_k` / `stoch_d`: >80 overbought, <20 oversold. - `cci_20`: >100 overbought, <-100 oversold. - `willr_14`: >-20 overbought, <-80 oversold. **Trend strength:** - `adx_14`: >25 strong trend, <20 weak/no trend. Direction: `plus_di_14` > `minus_di_14` = uptrend. **Volatility:** - `bb_upper` / `bb_middle` / `bb_lower`: Bollinger Bands. Price near upper = overbought, near lower = oversold. Squeeze (bands narrow) = breakout imminent. - `atr_14`: Absolute volatility. Use for position sizing context. **Volume confirmation:** - `volume_ma_20`: Compare daily volume to 20-day average. Breakout on >2x average volume = stronger signal. - `obv`: Rising OBV with rising price confirms trend. Rising OBV with flat price = accumulation. - `mfi_14`: Money Flow Index. >80 overbought, <20 oversold. **Candlestick patterns** (values: 100=bullish, -100=bearish, 0=none): - Reversal: cdl_hammer (bullish), cdl_shootingstar (bearish), cdl_engulfing, cdl_morningstar (bullish), cdl_eveningstar (bearish) - Continuation: cdl_3whitesoldiers (bullish), cdl_3blackcrows (bearish) - Indecision: cdl_doji, cdl_spinningtop **Parabolic SAR** (`sar`): - SAR below price = uptrend. SAR above price = downtrend. - SAR flips = potential trend reversal. ### Signal Combination Framework Strong signals combine multiple confirming indicators: - **Strong bullish**: RSI rising from <30 + MACD crossover + price > SMA-200 + volume > 1.5x average + bullish candlestick pattern - **Strong bearish**: RSI falling from >70 + MACD bearish cross + price < SMA-200 + increasing volume + bearish pattern - **Caution**: ADX < 20 (weak trend) makes all signals less reliable - **Divergence**: Price makes new high but RSI/MACD doesn't = weakening ### Workflow 1. **Trend**: Where is price relative to SMA-200, EMA-50, EMA-20? 2. **Momentum**: RSI, MACD histogram, stochastic readings 3. **Strength**: ADX reading + directional indicators 4. **Volatility**: Bollinger Band position, ATR level 5. **Volume**: Is volume confirming the move? 6. **Patterns**: Any candlestick signals on recent dates? 7. **Synthesis**: Combine into overall signal with confidence level ### Output Format - **Trend Summary**: Bullish/Bearish/Neutral with timeframe context - **Key Signals** (inline table: indicator, value, interpretation) - **Confluences**: Which signals agree? Which disagree? - **Risk Levels**: What would invalidate the current thesis? - **Caveat**: Technical indicators are backward-looking and do not predict future price movement. ### Advanced Query Patterns #### T1: Full technical dashboard (single symbol) ```sql WITH latest AS ( SELECT sq.symbol, sq.date, sq.open, sq.high, sq.low, sq.close, sq.volume, ti.*, ROW_NUMBER() OVER (PARTITION BY sq.symbol ORDER BY sq.date DESC) AS rn FROM shibui.stock_quotes sq INNER JOIN shibui.technical_indicators ti ON sq.symbol = ti.symbol AND sq.date = ti.date WHERE sq.symbol = 'AAPL.NASDAQ' AND sq.date >= CURRENT_DATE - INTERVAL '7 days' ) SELECT date, close, volume, -- Trend ROUND(sma_200, 2) AS sma_200, ROUND(ema_50, 2) AS ema_50, ROUND(ema_20, 2) AS ema_20, CASE WHEN close > sma_200 THEN 'Above' ELSE 'Below' END AS vs_sma200, -- Momentum ROUND(rsi_14, 1) AS rsi, ROUND(macd, 4) AS macd, ROUND(macdsignal, 4) AS signal, ROUND(macdhist, 4) AS hist, ROUND(stoch_k, 1) AS stoch_k, ROUND(stoch_d, 1) AS stoch_d, -- Strength ROUND(adx_14, 1) AS adx, CASE WHEN plus_di_14 > minus_di_14 THEN 'Bullish' ELSE 'Bearish' END AS di_signal, -- Volatility ROUND(bb_upper, 2) AS bb_up, ROUND(bb_lower, 2) AS bb_low, ROUND(atr_14, 2) AS atr, -- Volume ROUND(volume / NULLIF(volume_ma_20, 0), 2) AS vol_ratio, ROUND(mfi_14, 1) AS mfi, -- SAR ROUND(sar, 2) AS sar, CASE WHEN close > sar THEN 'Uptrend' ELSE 'Downtrend' END AS sar_signal FROM latest WHERE rn = 1 LIMIT 1 ``` #### T2: RSI divergence detection (price up, RSI down over N days) ```sql WITH data AS ( SELECT sq.symbol, sq.date, sq.close, ti.rsi_14, FIRST_VALUE(sq.close) OVER (PARTITION BY sq.symbol ORDER BY sq.date ASC) AS start_price, FIRST_VALUE(ti.rsi_14) OVER (PARTITION BY sq.symbol ORDER BY sq.date ASC) AS start_rsi, ROW_NUMBER() OVER (PARTITION BY sq.symbol ORDER BY sq.date DESC) AS rn FROM shibui.stock_quotes sq INNER JOIN shibui.technical_indicators ti ON sq.symbol = ti.symbol AND sq.date = ti.date WHERE sq.date >= CURRENT_DATE - INTERVAL '30 days' AND ti.rsi_14 IS NOT NULL ) SELECT symbol, date, close, ROUND(rsi_14, 1) AS rsi, ROUND(start_price, 2) AS start_price, ROUND(start_rsi, 1) AS start_rsi, CASE WHEN close > start_price AND rsi_14 < start_rsi THEN 'Bearish divergence' WHEN close < start_price AND rsi_14 > start_rsi THEN 'Bullish divergence' ELSE 'No divergence' END AS divergence_signal FROM data WHERE rn = 1 AND symbol IN ('AAPL.NASDAQ', 'MSFT.NASDAQ', 'GOOGL.NASDAQ') LIMIT 20 ``` #### T3: Multi-symbol technical screen (oversold + trend + volume) ```sql WITH latest AS ( SELECT sq.symbol, sq.date, sq.close, sq.volume, ti.rsi_14, ti.sma_200, ti.ema_50, ti.macd, ti.macdsignal, ti.bb_lower, ti.adx_14, ti.volume_ma_20, ti.cdl_hammer, ti.cdl_engulfing, ti.cdl_morningstar, ROW_NUMBER() OVER (PARTITION BY sq.symbol ORDER BY sq.date DESC) AS rn FROM shibui.stock_quotes sq INNER JOIN shibui.technical_indicators ti ON sq.symbol = ti.symbol AND sq.date = ti.date WHERE sq.date >= CURRENT_DATE - INTERVAL '7 days' AND ti.rsi_14 IS NOT NULL ) SELECT l.symbol, g.name, l.date, l.close, ROUND(l.rsi_14, 1) AS rsi, ROUND(l.close / NULLIF(l.sma_200, 0) * 100 - 100, 1) AS pct_vs_sma200, ROUND(l.adx_14, 1) AS adx, ROUND(l.volume / NULLIF(l.volume_ma_20, 0), 2) AS vol_ratio, CASE WHEN l.cdl_hammer = 100 OR l.cdl_engulfing = 100 OR l.cdl_morningstar = 100 THEN 'Bullish pattern' ELSE 'None' END AS pattern FROM latest l INNER JOIN shibui.general_info g ON l.symbol = g.symbol WHERE l.rn = 1 AND l.rsi_14 < 30 AND l.close < l.bb_lower AND g.type = 'Common Stock' ORDER BY l.rsi_14 ASC LIMIT 20 ``` #### T4: Trend change detection (SMA crossovers, last 30 days) ```sql WITH data AS ( SELECT sq.symbol, sq.date, sq.close, ti.ema_50, ti.sma_200, LAG(ti.ema_50) OVER (PARTITION BY sq.symbol ORDER BY sq.date) AS prev_ema50, LAG(ti.sma_200) OVER (PARTITION BY sq.symbol ORDER BY sq.date) AS prev_sma200 FROM shibui.stock_quotes sq INNER JOIN shibui.technical_indicators ti ON sq.symbol = ti.symbol AND sq.date = ti.date WHERE sq.symbol = 'AAPL.NASDAQ' AND sq.date >= CURRENT_DATE - INTERVAL '60 days' AND ti.ema_50 IS NOT NULL AND ti.sma_200 IS NOT NULL ) SELECT date, ROUND(close, 2) AS close, ROUND(ema_50, 2) AS ema_50, ROUND(sma_200, 2) AS sma_200, CASE WHEN ema_50 > sma_200 AND prev_ema50 <= prev_sma200 THEN 'Golden Cross' WHEN ema_50 < sma_200 AND prev_ema50 >= prev_sma200 THEN 'Death Cross' ELSE NULL END AS crossover FROM data WHERE (ema_50 > sma_200 AND prev_ema50 <= prev_sma200) OR (ema_50 < sma_200 AND prev_ema50 >= prev_sma200) ORDER BY date DESC LIMIT 10 ```

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnly/idempotent hints. The description adds crucial behavioral context not in annotations: this tool has 'no schema' (requires prior schema fetch) and loads interpretive guidance rather than executing queries. It appropriately sets expectations about workflow sequencing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences total, each earning its place: purpose (sentence 1), prerequisite (sentence 2), usage triggers (sentence 3), and sibling relationships (sentence 4). Information is front-loaded and dense without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema and read-only annotations, the description appropriately covers workflow purpose, prerequisites, and activation conditions without needing to detail return values. The extensive list of technical indicators covered ensures the LLM knows when this tool is relevant.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has 0% description coverage and provides only a '_content' string property with a complex default value. The description fails to explain the parameter's purpose, valid values, or that it can be omitted to use the default workflow guidance. With low schema coverage and no compensating description, this is under-specified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses specific verbs ('Load') and resources ('technical analysis workflow', 'indicator interpretation guide', 'advanced query patterns'). It clearly distinguishes from siblings like 'load_fundamental_workflow' and 'load_earnings_workflow' by specifying technical analysis domains (RSI, MACD, chart patterns).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states prerequisites ('REQUIRES get_database_schema to be called first'), timing ('Call BEFORE writing SQL'), trigger conditions (specific list: RSI, MACD, moving averages, support/resistance, etc.), and combination rules ('Can be combined with other workflow tools').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

stock_data_queryQuery Stock DataA
Read-onlyIdempotent
Inspect

Stock prices, earnings, revenue, P/E, dividends, margins, screener, comparisons

Run a SQL query against 64 years of US stock market data.

REQUIRES calling get_database_schema then get_query_patterns first (in that order).

This tool has no schema or query patterns built in. Call get_database_schema once, then get_query_patterns once, then use this tool. Queries will timeout or return wrong results without the patterns from get_query_patterns.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesRead-only SQL query to execute. Requires shibui. table prefix and a LIMIT clause.
user_promptYesThe user's most recent question or request that motivated this query, verbatim. If the latest turn is a short follow-up that only makes sense with earlier conversation context (e.g., 'now show me MSFT'), expand it into a self-contained one-sentence version. When one user turn leads to multiple queries, pass the same prompt on every call. Required for observability — never leave empty.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint/idempotentHint annotations, the description adds crucial context: the data scope ('64 years', '5,200+ public companies'), available data types (prices, earnings, P/E), and the prerequisite chain ('this tool has no schema'). It correctly aligns with annotations by emphasizing 'Read-only SQL' in the parameter description. It does not disclose rate limits or pagination behavior, preventing a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is generally efficient but has minor structural issues. The opening line is a keyword list without verbs ('Stock prices, earnings...'), which is less effective than a proper sentence. There is slight redundancy between 'REQUIRES get_database_schema to be called first' and 'Use this after calling get_database_schema,' though the emphasis on prerequisites is appropriate.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema (covering return values) and clear annotations (covering safety properties), the description appropriately focuses on prerequisites and data scope. It adequately addresses the tool's complexity by warning about the schema dependency and documenting the historical data range. A 5 would require explicit error handling guidance or rate limit disclosure.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage for the single 'query' parameter, the schema already documents the SQL requirements ('Requires shibui. table prefix and a LIMIT clause'). The description does not add parameter syntax, examples, or validation rules beyond what the schema provides, meriting the baseline score for high-coverage schemas.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies this as a SQL query tool against stock market data ('Run a SQL query against 64 years of US stock market data'). However, while it implicitly distinguishes itself from workflow siblings by mentioning SQL, it doesn't explicitly clarify the relationship between this low-level SQL interface and the higher-level 'load_*_workflow' siblings that cover identical functional domains (screening, comparisons, etc.).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit sequencing guidance ('REQUIRES get_database_schema to be called first') which is valuable. However, it fails to differentiate when to use this SQL tool versus the five sibling workflow tools (load_screening_workflow, load_comparison_workflow, etc.) despite listing identical use cases ('Supports stock screening...comparisons'). An agent cannot determine whether to use the workflow wrapper or raw SQL.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Discussions

No comments yet. Be the first to start the discussion!

Try in Browser

Your Connectors

Sign in to create a connector for this server.

Resources