Skip to main content
Glama

BotSpot: AI Trading, Backtests and Bots

Query backtest data with SQL (auto-uses Parquet when available)

query_csv
Read-only

Execute a SQL query on a backtest artifact using DuckDB. backtestId must be the actual backtest run id already discovered from list_backtests or used with get_backtest_artifact; never pass a strategy id, aiStrategyId, revisionId, browser URL revisionId, conversation id, or placeholder such as . Use the same backtestId for every artifact query in a single-run answer. Do not mix stats/trades/agent_detail/log evidence from different backtest ids unless the user asked to compare multiple runs, requested all-runs analysis, or the current id is inaccessible and you say so. Before querying trades.csv, indicators.csv, stats.csv, agent_detail.parquet, or agent memory Parquet artifacts for completed-run analysis, call get_backtest_artifact for that artifact first; list_backtests(includeArtifacts=true) is useful discovery, but it is not a substitute for the explicit artifact availability/schema step. Running backtests often have no tabular artifacts yet. Runtime logs use get_backtest_logs, not query_csv. SQL SCOPE: Only the selected artifact is available as table data. Do not invent tables or read_parquet/read_csv paths for another artifact. Query artifacts separately or use run_data_analysis with declared owned datasets for cross-artifact comparisons. TIMESTAMPS: Use the actual csvSchema types. TIMESTAMP_NS is timezone-naive; TIMESTAMPTZ is timezone-aware. Match boundary literals to the column type. Cross-timezone comparisons require verified timezone provenance and explicit normalization, not guessed UTC or stripped offsets. IMPORTANT: This tool automatically uses the Parquet version of artifacts when available for much faster queries. BATCHING: queries: [{name, sql}, ...] can inspect several bounded views of one tabular artifact. Runtime logs are available through get_backtest_logs with query/queries/diagnosticPreset/contextLines. When you report results to the user, tell them whether Parquet or CSV format was used (shown in the response). Use query_csv for bounded read-only SQL views and aggregates from one saved tabular artifact. For full-ledger calculations, indicators or charts, run_data_analysis can read owned backtest::artifact:trades, :stats and :indicators datasets through declared internalData capabilities. Python can compare multiple owned backtests, preserving each run and date window. Use this for queryable CSV/Parquet artifacts: trades.csv, indicators.csv, stats.csv, agent_detail.parquet, memory_events.parquet, memory_retrievals.parquet, memory_state.parquet. For trades.csv/trades.parquet, remember the file can contain non-fill event rows such as submitted orders, canceled orders, position summaries, cash events, and readiness rows. For user questions like 'what trades', 'all trades', or trade reasons, a broad schema/context query is not enough: run a second query with a WHERE filter for filled status or nonzero filled_quantity, and base the filled-trade list only on that filtered result. Keep cash events and non-fill events separate. For runtime backtest diagnostics, get_backtest_logs is the log evidence tool and supports batched regex searches with context. Do not try to query runtime logs as artifacts. File Search (RAG) is NOT reliable for aggregations because it only returns partial chunks. query_csv gives you EXACT results for COUNT, SUM, AVG, GROUP BY, etc. WORKFLOW: First call get_backtest_artifact to learn the column names (csvSchema) and check nullCount for each column. Then call THIS TOOL (query_csv) to actually get the data values. Use 'data' as the table name. SAFETY: query results are bounded. Long text cells can be shortened and rows can be omitted with payloadTruncated=true; if that happens, rerun a narrower query with fewer columns, lower LIMIT, WHERE filters, or LEFT(long_column, N). For agent_detail.parquet or agent_detail.csv, start with aggregate queries plus at least one ordered timeline/excerpt query with LIMIT. When using batched queries, name them agent_detail_counts and agent_detail_timeline so the evidence is unambiguous. Aggregate counts alone are not enough to answer what the agent was doing. Never raw-select full prompt/context/call text columns such as effective_system_prompt, base_system_prompt, runtime_context_text, context_text, memory_state_text, event_payload_json, event_text, summary, or final_text without LEFT(..., N). Aggregating a long text column does not make it safe: do not use MAX(summary), MAX(final_text), ANY_VALUE(summary), or ANY_VALUE(final_text) unless the aggregate is wrapped in LEFT(..., N). TIMESTAMPS: Timestamp fields are returned as ISO 8601 strings (e.g., '2024-01-15T09:30:45.123456') in UTC. Check nullCount in csvSchema - some columns may have NULLs for certain row types. For indicators.csv: the 'timestamp' column (Unix epoch DOUBLE) may be NULL for price/OHLC data (e.g., MNQ) - use epoch(datetime) instead for those rows. EXAMPLE QUERIES:

  • Total trades: SELECT COUNT(*) FROM data

  • Filled trades only: SELECT * FROM data WHERE lower(coalesce(status, '')) = 'filled' OR coalesce(filled_quantity, 0) <> 0 ORDER BY time LIMIT 100

  • Win rate: SELECT COUNT(*) as total, SUM(CASE WHEN profit > 0 THEN 1 ELSE 0 END) as winners FROM data

  • By symbol, only after schema confirms symbol: SELECT symbol, COUNT(*) FROM data GROUP BY symbol

  • Indicator values: SELECT * FROM data LIMIT 50

  • AI agent event counts: queries=[{name:'agent_detail_counts',sql:'SELECT event_kind, tool_name, COUNT(*) AS events FROM data GROUP BY event_kind, tool_name ORDER BY events DESC'}]

  • AI agent timeline: queries=[{name:'agent_detail_timeline',sql:'SELECT call_index, event_index, event_kind, tool_name, LEFT(event_text, 500) AS event_text, call_input_tokens, call_output_tokens, call_cached_input_tokens, call_thinking_tokens, call_latency_ms FROM data ORDER BY call_index, event_index LIMIT 50'}]

  • AI agent call summaries: SELECT call_index, MIN(timestamp) AS first_ts, MAX(timestamp) AS last_ts, MAX(agent_name) AS agent_name, LEFT(MAX(summary), 500) AS summary_excerpt, LEFT(MAX(final_text), 500) AS final_text_excerpt, MAX(call_input_tokens) AS input_tokens, MAX(call_output_tokens) AS output_tokens, MAX(call_latency_ms) AS latency_ms FROM data GROUP BY call_index ORDER BY call_index LIMIT 50

  • AI agent model usage: SELECT COUNT(DISTINCT call_index) AS calls, MAX(call_input_tokens) AS max_input_tokens, MAX(call_output_tokens) AS max_output_tokens, MAX(call_latency_ms) AS max_latency_ms FROM data

  • Price data (when timestamp is NULL): SELECT * FROM data WHERE name = 'MNQ' AND epoch(datetime) BETWEEN start AND end Results are limited to 100 rows for raw data queries (aggregations are not limited).

Input Schema

NameRequiredDescriptionDefault
sqlNoSingle read-only SQL over the selected artifact as table "data". Example: SELECT COUNT(*) FROM data. Inspect its schema before referencing other columns. Pass either sql or queries, not both.
queriesNoBatch several named read-only SQL queries against the same tabular artifact in one tool call. Strongly prefer this for trades, stats, agent_detail, indicators, and other tabular evidence when diagnosis needs multiple evidence angles. Runtime logs use get_backtest_logs with query/queries/diagnosticPreset. For completed-backtest improvement/tuning advice, a successful query_csv result should be followed by get_backtest_logs unless that log evidence was already checked.
backtestIdYesActual backtest run UUID. Use the same backtest id previously used for get_backtest_artifact. Do not use a strategy id, aiStrategyId, revisionId, browser URL revisionId, conversation id, or a placeholder such as <unknown>.
artifactTypeYesThe tabular artifact type to query: trades.csv, stats.csv, indicators.csv, agent_detail.parquet, memory_events.parquet, memory_retrievals.parquet, or memory_state.parquet. Runtime logs are not query_csv artifacts; use get_backtest_logs. For "improve/tune/make this backtest perform better" questions, query_csv on stats/trades/agent_detail is not enough; after tabular queries, call get_backtest_logs for errors, order/fill/position activity, and strategy/runtime lines before final advice.

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

A4.7/5.0
Behavior5/5

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

The readOnlyHint annotation already marks this as safe, but the description adds substantial behavioral context beyond annotations: results are bounded, raw queries are limited to 100 rows, long text cells can be shortened, rows can be omitted with payloadTruncated=true, aggregations are not row-limited, and Parquet format may be used automatically. It also discloses important data-shape quirks such as non-fill event rows in trades.csv and NULL timestamp columns in indicators.csv.

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

Conciseness3/5

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

The definition is front-loaded with purpose and usage constraints, and its section-like structure makes a complex tool navigable. However, it is extremely long and repetitive, with multiple repeats about get_backtest_logs, get_backtest_artifact, timestamp handling, and long text columns, so many sentences do not earn their place as tightly as they could.

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 tool's complexity, the absence of an output schema, and the rich input schema, the description is complete enough for correct invocation. It covers prerequisites, alternatives, SQL scoping, timestamp semantics, truncation behavior, artifact-specific caveats, batching, and examples, leaving little ambiguity for an agent.

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?

With 100% schema description coverage, the schema already documents the four parameters, and the description largely reinforces those constraints while adding practical semantics such as using "data" as the table name, passing either sql or queries but not both, naming batched queries meaningfully, and not passing strategy/revision/conversation IDs as backtestId. The rich examples and timestamp-type guidance add value beyond the schema, though some parameter details are duplicated from the schema.

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 a specific verb and resource: execute a read-only SQL query on a backtest artifact using DuckDB, with automatic Parquet usage when available. It clearly distinguishes this tool from siblings such as get_backtest_artifact, get_backtest_logs, run_data_analysis, and list_backtests, so an agent can identify when this tool is the right one.

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 gives explicit usage rules: call get_backtest_artifact first to inspect schema, use get_backtest_logs for runtime logs, use run_data_analysis for full-ledger or cross-artifact calculations, and avoid mixing evidence from different backtest IDs unless comparing runs. It also explains prerequisites for completed-run artifacts and the limitation that running backtests may have no tabular artifacts yet.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.