How to read Wayfinder Shells frontend state, create chart panes, and add TradingView annotations or overlays to the default live chart or agent-created workspace charts.
Scanned 9/3/2026
Install to Claude Code
npx -y skills add WayfinderFoundation/wayfinder-paths-sdk --skill using-visual-chart-annotations --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Using Visual Chart Annotations?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/wayfinderfoundation-using-visual-chart-annotations)More formats (shields.io, HTML) on the badges page.
---
name: using-visual-chart-annotations
description: How to read Wayfinder Shells frontend state, create chart panes, and add TradingView annotations or overlays to the default live chart or agent-created workspace charts.
metadata:
tags: wayfinder, shells, opencode, frontend, charts, annotations, overlays
---
## TL;DR
Read the current Shells chart id, then write chart changes through the chart workspace API. The same annotation tool works for the default live chart and for agent-created workspace charts.
**Typical flow (switch default market):**
```
1. visual_set_active_market(query="PENGU perp")
-> switches the live chart, order book, trades, and trade ticket together
2. chart_id = data["frontend_context"]["active_market_request"]["market_id"]
3. visual_add_workspace_chart_annotation(
chart_id=chart_id,
type="marker",
config={"time": "2026-05-21T06:00:00Z", "price": 0.035, "shape": "flag", "color": "#22c55e"}
)
```
**Typical flow (default chart):**
```
1. visual_get_frontend_context()
-> {"ok": true, "data": {"frontend_context": {"chart": {"id": "hl-perp-BTC", "market_id": "hl-perp-BTC", "market_type": "hl-perp"}}}}
2. chart_id = data["frontend_context"]["chart"]["id"]
3. visual_add_workspace_chart_annotation(
chart_id=chart_id,
type="horizontal_line",
config={"price": 73500, "color": "#ef4444", "label": "Support"}
)
4. The annotation appears on the user's default chart.
```
**Typical flow (agent-created visual pane):**
```
1. visual_search_chart_series(query="BTC ETH relative performance")
-> copy compatible `source` objects and each result's `shape.default_y`
2. visual_create_chart(
chart_id="btc-eth-relative",
title="BTC vs ETH",
kind="line",
lookback_days=90,
series=[
{"id": "btc", "label": "BTC", "source": <BTC source>, "x": "ts", "y": "price_usd"},
{"id": "eth", "label": "ETH", "source": <ETH source>, "x": "ts", "y": "price_usd"}
],
transforms=[{"type": "rebase", "base": 100}]
)
3. visual_set_active_chart(chart_id="btc-eth-relative")
4. visual_add_workspace_chart_annotation(
chart_id="btc-eth-relative",
type="text_label",
config={"time": "2026-05-21T06:00:00Z", "price": 120, "text": "Relative breakout"}
)
```
## MCP tools
| Tool | Args | Use |
|------|------|-----|
| `visual_get_frontend_context` | none | Read current default chart context and workspace |
| `visual_search_chart_series` | `query`, `kind?`, `venue?`, `market_type?`, `limit?` | Discover supported chart datasets and their column shapes |
| `visual_set_active_market` | `query?`, `market_id?`, `market_type?`, `chain_id?`, `clear_workspace?` | Switch the default chart/trading context to one tradable market |
| `visual_preview_series` | `series`, `kind?`, `transforms?`, `lookback_days?`, `limit?` | Dry-run resolve series before charting: y-range, unit, counts, sample points |
| `visual_create_chart` | `chart_id`, `title`, `kind`, `series`, `transforms?`, `overlays?`, `lookback_days?`, `limit?`, `layout?`, `context_market_id?`, `indicators?` | Validate, create, or replace a visual pane |
| `visual_set_active_chart` | `chart_id` | Focus an existing workspace chart |
| `visual_add_workspace_chart_annotation` | `chart_id`, `type`, `config`, `annotation_id?` | Add one TradingView annotation to a default or workspace chart |
| `visual_add_workspace_chart_overlay` | `chart_id`, `overlay` | Add a raw overlay, usually bulk `event_markers` |
| `visual_add_workspace_chart_series` | `chart_id`, `series` | Add or replace a data series on an existing workspace chart |
| `visual_set_chart_indicators` | `chart_id`, `indicators` | Replace the TradingView studies on a live or workspace chart (`[]` clears) |
| `visual_clear_chart_workspace` | none | Clear agent-created charts and default-chart annotations |
All gate on `is_opencode_instance()` and return `{"ok": false, "error": {"code": "not_opencode_instance"}}` when run outside Shells.
## Chart panes
Use `visual_set_active_market` when the user asks to show, switch to, or open one tradable market such as "show AAVE", "switch to PENGU perp", "open POL spot", or "show this Polymarket market". It is the one-call path that updates the default chart and the rest of the trading context.
Use `visual_create_chart` when the user asks for a new visual pane, comparison, APY/funding chart, table, or custom derived visualization, not when they only want to switch or annotate the live chart.
If `visual_create_chart` returns `ok: false`, do not tell the user the chart is ready. Read the error, pick a compatible source/kind, and retry.
| Chart kind | Use |
|------------|-----|
| `price_candle` | Primary market price chart. Use `{"type": "market_price", "market_id": "..."}` or a Hyperliquid perp `dataset_series` returned by search. |
| `line` | One or more time series, such as relative performance or APYs over time. |
| `bar` | Ranked or latest categorical values. |
| `table` | Tabular data. |
Supported source types:
- `market_price`: `{"type": "market_price", "market_id": "hl-perp-btc"}`
- `dataset_series`: returned by `visual_search_chart_series`; preferred for known backend datasets, including the current Delta Lab registry-backed series
- `delta_lab_asset`: `{"type": "delta_lab_asset", "symbol": "USDC", "series": "lending", "venue"?: "...", "basis"?: true}`. Legacy fallback only; use a returned `dataset_series` source when available.
- `inline`: `{"type": "inline", "points": [{...}]}`
Single-series time-series workspace charts render in TradingView. Multi-series
comparisons currently use the workspace line renderer; keep using `line` plus
`rebase` for relative performance charts.
For tradable perp charts, pass `context_market_id` so the shell switches the
order book, trades, and trade ticket while keeping the workspace chart active:
```json
{
"chart_id": "pengu-price",
"title": "PENGU Perp",
"kind": "price_candle",
"context_market_id": "hl-perp-pengu",
"series": [
{
"id": "pengu-price",
"label": "PENGU",
"source": {"type": "dataset_series", "dataset_id": "hyperliquid.perp.price", "params": {"coin": "PENGU", "interval": "1h"}},
"x": "ts",
"y": "price_usd"
}
]
}
```
Supported transforms: `filter`, `latest_by`, `top_n`, `rebase`, `pct_change`, `scale`, `multiply`, `ratio`, `spread`, `moving_average`. Prefer `rebase(base=100)` for relative performance across different units.
Transforms can live on the chart or on a single series. Copy any `default_transforms` from a search result into the series `transforms`, then add metric conversions. Use series-level transforms when only one dataset needs conversion. Chart-level transforms hit **every** series unless you scope them with `"series_ids": ["..."]`.
**Scaling a ratio**: put `scale` on the ratio transform itself — `{"type": "ratio", "left": "a", "right": "b", "scale": 1e6, "label_suffix": "(×10⁶)", "unit": "ratio"}` — which scales only the derived series. Never use a chart-level `scale` for this: it multiplies the sources *before* division, so it corrupts them and mathematically cancels out of the ratio.
Delta Lab APY/rate fields are decimal fractions. `0.12` means `12%`, not `0.12%`.
For Pendle/lending/Boros/yield APY or APR fields such as `implied_apy`, `underlying_apy`, `supply_apr`, `borrow_apr`, `fixed_rate_mark`, and `floating_rate_oracle`, convert to display percent:
```json
{"type": "scale", "factor": 100, "unit": "%", "label_suffix": "(%)"}
```
For annualizing hourly funding to display percent:
```json
{
"id": "btc-funding",
"label": "BTC Funding",
"source": {"type": "dataset_series", "dataset_id": "hyperliquid.perp.funding", "params": {"coin": "BTC"}},
"x": "ts",
"y": "funding_rate",
"unit": "%",
"axis": "right",
"transforms": [{"type": "scale", "factor": 876000, "unit": "%", "label_suffix": "(annualized %)"}]
}
```
Series can set `color` and `axis` (`left` or `right`). Keep related units on the same axis; use a right axis only when overlaying unrelated units.
## Dataset selection
Always search known datasets before inventing or fetching your own data.
0. If the user asks for a single tradable token/perp/spot/prediction market, use `visual_set_active_market` first. Do not search chart datasets or create a workspace chart for simple market switches.
1. Use `visual_search_chart_series` with the user intent/assets first. Do not pass `kind` by default; inspect returned `kind`, `shape.default_y`, `shape.columns`, and `shape.supported_chart_kinds` to decide whether to use the candidate.
2. Prefer a common source family across compared series. For example, use Hyperliquid perp prices for BTC and ETH together; do not mix Hyperliquid BTC with CoinGecko ETH unless there is no common source.
3. For asset price/performance requests, prefer Hyperliquid perp price series over spot/fallback price series unless the user explicitly asks for spot. Prefer registry-returned Delta Lab `dataset_series` sources for lending/yield/Boros/Pendle/funding research series, CoinGecko only as broad spot price fallback, and DeFiLlama for current ranked yield tables/bars.
4. Pass `kind` only to narrow a known data family (`funding`, `yield`, `price`) or a large result set. Do not pass chart kinds such as `line` as the first search because that hides useful candidate metadata.
5. Use Polymarket-specific tools/API for prediction markets. Do not route Polymarket discovery through chart-series search.
6. Use `inline` only when the registry does not expose the needed data. If using inline data, keep it small and describe the columns in the chart label or nearby message.
7. Set `lookback_days` on `visual_create_chart` when the user gives a time window. Use 90 for "3 months".
8. When a chart represents a tradable Hyperliquid perp, set
`context_market_id` to `hl-perp-<symbol-lowercase>` unless the tool result
already provides a more specific market id.
Registry results include:
- `source`: copy this into the chart series spec.
- `shape.default_y`: use this as the series `y` field unless a different column is intentional.
- `shape.columns`: read this before choosing transforms such as `latest_by`, `top_n`, `ratio`, or `spread`.
## Annotation types
| `type` | `config` |
|--------|----------|
| `horizontal_line` | `price`, `color?`, `label?` |
| `vertical_line` | `time` (unix sec), `color?`, `label?` |
| `marker` | `time`, `price?`, `shape?` (`arrow_up` / `arrow_down` / `flag` / `icon` / `emoji`), `color?` |
| `range` | `from_time?`, `to_time?`, `from_price`, `to_price`, `color?` |
| `text_label` | `time`, `price`, `text`, `color?` |
| `trend` | `from: {time, price}`, `to: {time, price}`, `color?`, `label?` |
For bulk catalyst markers on the live/default chart, prefer a single event marker
overlay:
```json
{
"type": "event_markers",
"id": "zec-catalysts",
"data": [
{
"time": "2026-05-21T06:00:00Z",
"price": 690,
"label": "ZEC hits $690",
"color": "#ef4444",
"shape": "flag"
}
]
}
```
Use `data` as the canonical event array. Legacy `markers` is accepted, but new
calls should not use it.
## TradingView indicators
`visual_set_chart_indicators(chart_id, indicators)` applies native TradingView
studies to the live/default chart or a workspace chart. **Replace semantics**:
the list you pass becomes the chart's full indicator set; `[]` clears. Current
indicators are readable at `chart_workspace.defaultIndicators[chart_id]`.
```json
[
{"name": "ema", "inputs": {"length": 21}},
{"name": "bollinger", "inputs": {"length": 20, "mult": 2}},
{"name": "rsi"}
]
```
| Alias | TradingView study | Pane | Params (defaults) |
|-------|-------------------|------|-------------------|
| `sma` | Moving Average | price overlay | `length` (9), `source` ("close") |
| `ema` | Moving Average Exponential | price overlay | `length` (9), `source` ("close") |
| `bollinger` | Bollinger Bands | price overlay | `length` (20), `mult` (2) |
| `supertrend` | SuperTrend | price overlay | `length` (10, ATR period), `factor` (3) |
| `vwap` | VWAP | price overlay | `anchor` ("Session"/"Week"/"Month"/"Quarter"/"Year"), `source` ("hlc3") |
| `rsi` | Relative Strength Index | own sub-pane | `length` (14) |
| `macd` | MACD | own sub-pane | `fast` (12), `slow` (26), `signal` (9) |
| `atr` | Average True Range | own sub-pane | `length` (14) |
| `stochastic` | Stochastic | own sub-pane | `k_length` (14), `k_smoothing` (1), `d_smoothing` (3) |
| `volume` | Volume | own sub-pane | `ma_length` (20), `show_ma` (false) |
Names are case-insensitive; the canonical study names are also accepted. Unknown
names return a 400 listing the supported set. Use the friendly param names from
the table — the backend translates them to the study's actual TradingView input
ids (several built-ins use positional `in_0`/`in_1` ids). Unrecognized input
keys pass through untouched, so exact TV input ids and advanced inputs
(`smoothingLine`, `maType`, …) also work. Omit `inputs` entirely for
TradingView defaults — prefer that unless the user asked for specific
parameters. Optional `forceOverlay` overrides the pane default; optional `id`
makes an entry stable across replaces. `visual_create_chart` accepts the same
list via `indicators`.
Indicators render only on TradingView-backed charts: the live market chart,
`price_candle` charts, and single-series time-series line charts. Bar, table,
and multi-series recharts panels ignore them.
## Previewing data before charting
`visual_preview_series(series, kind?, transforms?, lookback_days?, limit?)`
dry-run resolves the exact series/transforms payload you would pass to
`visual_create_chart` — nothing is saved. The compact response carries, per
series: `points`, `first_x`/`last_x`, `unit`, `y_first`/`y_last`/`y_min`/`y_max`,
and `sample_head`/`sample_tail` point arrays. Use it to catch unit and scaling
mistakes (decimal APYs, unreadably tiny ratios) before the chart exists. The
same automatic percent-scaling of known rate fields applied by
`visual_create_chart` is applied here, so the preview matches the render.
## Gotchas
- `marker` does not accept `label`. Use `text_label` for annotated points.
- Use ISO-8601 strings for `time` values when possible. The renderer also accepts unix seconds, but do not hand-compute timestamps.
- For default chart annotations, use the exact `frontend_context.chart.id`.
- For workspace charts, use the `chart_id` passed to `visual_create_chart`.
- Default chart annotations are stored in `chart_workspace.defaultAnnotations`; workspace chart annotations are stored in the chart's `overlays`.
- After writing default chart overlays, verify `chart_workspace.defaultAnnotations[chart_id]` contains the expected overlay and, for event markers, that its `data` array has the expected count.
- Chart workspace state is scoped to the current Shells instance, not the user vault.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!