Batch and Streaming Patterns¶
How the strategy layer minimizes MCP round-trips and detects changes between scan cycles.
Parallel Indicator Reads¶
The strategy's scan.js reads all four indicators in parallel using Promise.all. Each indicator reader makes its own MCP tool calls, and all readers execute concurrently.
const [biasData, sessionData, obData, smtData] = await Promise.all([
biasReader.read(), // data_get_pine_tables + data_get_pine_labels
sessionReader.read(), // data_get_pine_labels + data_get_pine_lines
obReader.read(), // data_get_pine_boxes + data_get_pine_lines + data_get_pine_tables
smtReader.read(), // data_get_pine_labels
]);
This produces approximately 8 MCP tool calls per scan cycle, all issued concurrently. The total scan duration is bounded by the slowest individual call rather than the sum of all calls.
Typical Scan Timing¶
| Phase | Duration | Notes |
|---|---|---|
| MCP tool calls (parallel) | 800--1500ms | Depends on chart complexity and indicator count |
| Reader parsing | 5--20ms | String splitting, color matching, type conversion |
| Interpreter logic | 1--5ms | State derivation from parsed data |
| Engine scoring | <1ms | Confluence computation and decision |
| Total scan | ~1--2 seconds |
Poll Mode with Delta Detection¶
The poll.js module runs scan.js on a configurable interval (POLL_INTERVAL, default 10 seconds) and compares each scan's output against the previous one.
How Delta Detection Works¶
After each scan, poll.js performs a deep comparison of the new StrategyFlags against the previous scan's flags. Only fields that changed are reported.
// Simplified delta detection logic
function detectDelta(previous, current) {
const changes = [];
// Compare every leaf field
for (const path of allFieldPaths) {
const prev = getNestedValue(previous, path);
const curr = getNestedValue(current, path);
if (prev !== curr) {
changes.push({ path, from: prev, to: curr });
}
}
return changes;
}
Delta Output Example¶
When the session hunt locks during a poll cycle:
{
"scan_number": 15,
"timestamp": "2026-04-14T13:50:10.000Z",
"delta": [
{ "path": "session.hunt.lo_swept", "from": false, "to": true },
{ "path": "session.hunt.side", "from": null, "to": "HUNT_LOW" },
{ "path": "decision.setup_valid", "from": false, "to": true },
{ "path": "decision.confluence_score", "from": 0, "to": 7 },
{ "path": "decision.direction", "from": null, "to": "long" }
]
}
No-Change Heartbeat¶
When nothing has changed between scans, only a heartbeat is logged:
This keeps the log stream active (confirming the system is running) without flooding it with duplicate data.
batch_execute MCP Tool¶
The tradingview-mcp server provides a batch_execute tool that can combine multiple tool calls into a single MCP request. This reduces HTTP round-trip overhead when the MCP server is accessed over a network.
Usage¶
{
"tool": "batch_execute",
"params": {
"calls": [
{
"tool": "data_get_pine_tables",
"params": { "study_filter": "Bias AI" }
},
{
"tool": "data_get_pine_labels",
"params": { "study_filter": "Session Hunt AI", "verbose": true }
},
{
"tool": "data_get_pine_boxes",
"params": { "study_filter": "OB AI", "verbose": true }
},
{
"tool": "data_get_pine_lines",
"params": { "study_filter": "OB AI", "verbose": true }
}
]
}
}
Return Format¶
{
"results": [
{ "tool": "data_get_pine_tables", "result": { "tables": [...] } },
{ "tool": "data_get_pine_labels", "result": { "labels": [...] } },
{ "tool": "data_get_pine_boxes", "result": { "zones": [...] } },
{ "tool": "data_get_pine_lines", "result": { "horizontal_levels": [...] } }
]
}
All calls within the batch are executed by the MCP server and their results are returned together. The server may execute them sequentially or in parallel depending on implementation.
When to Use batch_execute¶
| Scenario | Recommendation |
|---|---|
| Local MCP server (same machine) | Promise.all with individual calls is fine -- network overhead is negligible |
| Remote MCP server (over network) | batch_execute reduces round-trips from ~8 to 1 |
| High-frequency polling (<5s interval) | batch_execute reduces connection churn |
The current strategy layer uses Promise.all with individual calls since the MCP server runs locally. Switching to batch_execute is a one-line change in scan.js if network deployment is needed.
Performance Optimization¶
Use study_filter to Reduce Payload¶
Without study_filter, Pine graphics tools return objects from all indicators on the chart. If the chart has 10 indicators, data_get_pine_labels returns labels from all 10.
With study_filter, only the target indicator's objects are returned. This reduces:
- Payload size: 10x fewer objects in a typical multi-indicator chart
- Parse time: Less data to iterate and filter in the reader
- Noise: No need to distinguish between indicators in application code
Always use study_filter. The strategy layer never calls Pine graphics tools without it.
Minimize Verbose Calls¶
The verbose: true parameter adds significant data to each object (colors, positions, sizes). Only use verbose when the reader needs the extra fields:
| Tool | Verbose Needed? | Reason |
|---|---|---|
data_get_pine_labels |
Sometimes | Verbose needed for textColor to detect signal direction in some indicators |
data_get_pine_lines |
Yes | style field needed for spent/unspent OB detection |
data_get_pine_tables |
No | Row text contains all needed data |
data_get_pine_boxes |
Yes | borderColor needed for bullish/bearish OB classification |
Scan Interval Tuning¶
The default POLL_INTERVAL of 10 seconds balances responsiveness with resource usage. Considerations for tuning:
| Interval | Tradeoff |
|---|---|
| 5 seconds | Faster signal detection. Higher CPU and MCP load. Good for active trading sessions. |
| 10 seconds (default) | Balanced. Catches most setups within one candle on a 5min chart. |
| 30 seconds | Low resource usage. Acceptable for monitoring-only mode where immediate execution is not needed. |
| 60 seconds | Minimum for meaningful monitoring. May miss fast-developing setups. |
On a 5-minute chart, indicator state changes at most once per candle close (every 5 minutes). A 10-second interval provides ~30 scans per candle, which is more than sufficient to catch any state change promptly.