Skip to main content
Glama

BotSpot: AI Trading, Backtests and Bots

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

query_csv
Read-onlyIdempotent

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. 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, :indicators and :market_inputs (every bar the run received, for full price charts) 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).

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "backtestId": "<backtestId>",
      "artifactType": "<artifactType>"
    },
    "name": "query_csv"
  }
}

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, memory_state.parquet, profile_yappi.csv (profiled runs), or a CSV/Parquet file your strategy wrote, by its label outputs/<path>. 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. Changed1 schema field changed
    • changedInput schema / properties / artifactType / description
      Previous value: -"The 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."New value: +"The tabular artifact type to query: trades.csv, stats.csv, indicators.csv, agent_detail.parquet, memory_events.parquet, memory_retrievals.parquet, memory_state.parquet, profile_yappi.csv (profiled runs), or a CSV/Parquet file your strategy wrote, by its label outputs/<path>. 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."
  2. First observed

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnly/idempotent/non-destructive, and the description still adds material behavior: automatic Parquet substitution, a 100-row raw-data cap with aggregations exempt, payloadTruncated signaling and how to remediate, long-text columns being shortened, and the ban on unchunked prompt/context selects. It also explains identifier semantics (valid backtest UUID vs. strategy/revision/conversation ids) and timestamp typing behavior, none of which the annotations convey.

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

Conciseness2/5

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

Section labels (SQL SCOPE, BATCHING, WORKFLOW, SAFETY, EXAMPLE QUERIES) help, but the text is heavily redundant: the TIMESTAMPS block appears twice, the "use get_backtest_logs not query_csv for runtime logs" warning is stated at least four times, and the artifact list is enumerated multiple times. The genuinely novel content (Parquet fallback, truncation, fill-vs-non-fill rows, LEFT(...) safety rule) is buried in repetition.

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?

For a 4-parameter tool with no output schema and a high degree of caller-side risk (wrong id, invented tables, unbounded text dumps), the description covers identifiers, table naming, timestamp typing, truncation semantics, batching, and worked examples. An agent has enough to both select and invoke it correctly, including the common trades.csv pitfall of non-fill event rows.

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?

Schema coverage is 100%, so the schema already carries per-parameter text; the description nevertheless adds real meaning around the two main parameters — which artifactType values exist, why backtestId must be the same run id across queries, and when to prefer batched `queries` over `sql`. It stops short of documenting the mutual exclusivity and maxItems=8 limits as sharply as the schema does, so it is additive rather than fully compensating.

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?

Opens with a specific verb+resource ("Execute a SQL query on a backtest artifact using DuckDB") and immediately bounds scope: only the selected artifact is table "data", read-only, one saved tabular artifact at a time. It explicitly differentiates from siblings by name — runtime logs go to get_backtest_logs, full-ledger/cross-artifact work goes to run_data_analysis — so an agent can route without opening schemas.

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?

Gives explicit when-to-use (bounded read-only SQL views and aggregates on one artifact), when-not-to-use (runtime logs, cross-artifact comparisons, full price charts), and a stated workflow prerequisite: call get_backtest_artifact first for csvSchema/nullCount, then query. It also names alternatives (run_data_analysis with declared owned datasets, get_backtest_logs with diagnosticPreset) and the condition that selects each.

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.