Domain skill
tradingview
Markdown synced from browser-harness domain skills.
- Host
- tradingview
- Files
- 1
Agent prompt
Use this skill
Copy this prompt into your coding agent to make it enable browser-harness domain skills and read this exact domain folder before automating.
Set up https://github.com/browser-use/browser-harness for me if it is not already installed. If setup is needed, read `install.md` first to install and connect it to my real browser. Then read `SKILL.md` for normal usage and always read `helpers.py` because that is where the browser-harness functions are. Enable domain skills if they are not already enabled by setting `BH_DOMAIN_SKILLS=1` for browser-harness. Use the `tradingview` domain skill from `agent-workspace/domain-skills/tradingview/`. Read every markdown file for this domain before inventing an approach: - agent-workspace/domain-skills/tradingview/scraping.md Use those domain-skill notes to complete my task for `tradingview` in my real browser. When you open a setup, verification, or task tab, activate it so I can see the active browser tab.
Skill contents
What the agent will read
Scraping & Data Extraction
scraping.md
- https://www.tradingview.com — charting platform with multiple internal REST APIs. Stock/crypto/forex screener and symbol search work without auth. Use httpget or raw urllib for all workflows except JS-rendered chart...
- Use the scanner API for bulk screener data — one POST, no browser, full column control.
- No auth, no Referer, no cookies required for the scanner. Responses arrive in 200ms.
- Critical: "d" is a plain positional array — index 0 = columns[0], index 1 = columns[1], etc. There are no keys in the row data itself.
Show full markdown
https://www.tradingview.com — charting platform with multiple internal REST APIs. Stock/crypto/forex screener and symbol search work without auth. Use http_get or raw urllib for all workflows except JS-rendered chart pages.
Do this first
Use the scanner API for bulk screener data — one POST, no browser, full column control.
import json, urllib.request
def tv_scan(payload, market="america"):
data = json.dumps(payload).encode()
req = urllib.request.Request(
f"https://scanner.tradingview.com/{market}/scan",
data=data,
headers={"Content-Type": "application/json", "User-Agent": "Mozilla/5.0"}
)
with urllib.request.urlopen(req, timeout=20) as r:
return json.loads(r.read())
No auth, no Referer, no cookies required for the scanner. Responses arrive in ~200ms.
Common workflows
Top stocks by market cap (screener)
import json, urllib.request
payload = {
"filter": [],
"options": {"lang": "en"},
"columns": ["name", "close", "change", "volume", "market_cap_basic"],
"sort": {"sortBy": "market_cap_basic", "sortOrder": "desc"},
"range": [0, 10] # [start, end] — half-open, so this returns rows 0–9
}
data = json.dumps(payload).encode()
req = urllib.request.Request(
"https://scanner.tradingview.com/america/scan",
data=data,
headers={"Content-Type": "application/json", "User-Agent": "Mozilla/5.0"}
)
with urllib.request.urlopen(req, timeout=20) as r:
resp = json.loads(r.read())
# resp["totalCount"] = 19549 (all US-listed instruments)
# resp["data"] is a list of {"s": "NASDAQ:NVDA", "d": [col0, col1, ...]}
# "d" values align positionally with "columns" in the payload
cols = payload["columns"]
for item in resp["data"]:
row = dict(zip(cols, item["d"]))
symbol = item["s"] # e.g. "NASDAQ:AAPL"
print(symbol, row["close"], row["change"], row["market_cap_basic"])
# NASDAQ:NVDA 201.68 1.68 4900823822021.0
# NASDAQ:AAPL 270.23 2.59 3967284528489.0
# ...
Critical: "d" is a plain positional array — index 0 = columns[0], index 1 = columns[1], etc. There are no keys in the row data itself.
Pagination
# Page 1: range [0, 20]
# Page 2: range [20, 40]
payload["range"] = [20, 40]
Filtering stocks
payload = {
"filter": [
{"left": "market_cap_basic", "operation": "greater", "right": 10_000_000_000},
{"left": "volume", "operation": "greater", "right": 5_000_000},
{"left": "change", "operation": "in_range", "right": [2, 10]},
{"left": "exchange", "operation": "equal", "right": "NASDAQ"},
{"left": "sector", "operation": "equal", "right": "Electronic Technology"},
],
"columns": ["name", "close", "change", "volume", "market_cap_basic",
"description", "sector", "industry"],
"sort": {"sortBy": "market_cap_basic", "sortOrder": "desc"},
"range": [0, 20]
}
Valid filter operations: greater, less, equal, in_range (right = [min, max]), match (substring on name).
Sector names use TradingView taxonomy (not GICS). Confirmed working values:
"Electronic Technology"— NVDA, AAPL, TSM"Technology Services"— MSFT, GOOGL, META"Finance","Health Technology","Consumer Non-Durables"
Full list of tested valid column names
# Price & volume
"name" # ticker (e.g. "AAPL")
"description" # full name ("Apple Inc.")
"close" # last price
"open", "high", "low"
"volume"
"change" # % change today
"change_abs" # absolute price change
"change|1M" # 1-month % change (also: |6M, |1Y)
"High.1M", "High.6M" # period high
"High.All", "Low.All" # all-time high/low
"price_52_week_high" # confirmed works
"price_52_week_low" # confirmed works
"premarket_change" # pre-market %
"postmarket_change" # after-hours %
"gap" # overnight gap %
"change_from_open_abs" # intraday move from open
"average_volume_10d_calc" # 10-day avg volume
"relative_volume_10d_calc" # relative volume vs 10-day avg
"relative_volume_intraday|5" # intraday relative vol (5m bars)
# Fundamentals
"market_cap_basic" # market cap in USD
"earnings_per_share_diluted_ttm" # EPS TTM
"price_earnings_ttm" # P/E TTM
"P/E" # P/E (snapshot)
"dividends_yield" # dividend yield %
"beta_1_year" # beta
"float_shares_outstanding" # float shares
# Technical ratings & indicators
"Recommend.All" # composite rating: -1 (strong sell) to +1 (strong buy)
"RSI" # RSI 14
"MACD.macd" # MACD line
# Classification
"sector", "industry", "country", "exchange"
"type" # "stock", "fund", "dr" (depository receipt), etc.
# NOTE: "52_week_high" / "52_week_low" are INVALID — use "price_52_week_high" / "price_52_week_low"
# NOTE: "EPS_diluted_net" is INVALID — use "earnings_per_share_diluted_ttm"
Bad columns return HTTP 400 with {"error": "Unknown field \"X\""}.
Other scanner markets
# market argument options (confirmed working):
# "america" — US equities (19,549 instruments)
# "crypto" — crypto across exchanges (56,455 instruments)
# "forex" — FX pairs (6,401 instruments)
# "futures" — futures (53,947 instruments)
# Crypto example
payload = {
"filter": [],
"columns": ["name", "close", "change", "volume", "market_cap_calc"],
"sort": {"sortBy": "market_cap_calc", "sortOrder": "desc"},
"range": [0, 10]
}
resp = tv_scan(payload, market="crypto")
# Returns BTC, ETH, etc. across Binance, Bybit, OKX...
Symbol search (requires Origin header)
import json, urllib.request
def symbol_search(query, exchange="", type_filter="", limit=50):
url = (
f"https://symbol-search.tradingview.com/symbol_search/v3/"
f"?text={query}&hl=1&exchange={exchange}&lang=en"
f"&search_type={type_filter or 'undefined'}&domain=production"
)
req = urllib.request.Request(url, headers={
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"Origin": "https://www.tradingview.com", # REQUIRED — 403 without this
})
with urllib.request.urlopen(req, timeout=15) as r:
return json.loads(r.read())
result = symbol_search("AAPL")
# result["symbols_remaining"] = 137
# result["symbols"] = list of up to 50 matches
# result["symbols"][0] keys:
# symbol, description, type, exchange, country, currency_code,
# cusip, isin, cik_code, logoid, provider_id, source_id,
# is_primary_listing, typespecs
Gotcha: symbol-search.tradingview.com requires Origin: https://www.tradingview.com. Referer alone is not enough. The scanner API does NOT need Origin or Referer.
Filter by exchange and type:
# Exact match on NASDAQ:AAPL
result = symbol_search("AAPL", exchange="NASDAQ", type_filter="stock")
# Returns 1 result — exact symbol only when exchange is specified
News headlines for a symbol
import json, urllib.request
def get_news(symbol, limit=20):
# symbol format: "NASDAQ:AAPL", "NYSE:TSLA"
url = (
f"https://news-headlines.tradingview.com/v2/view/headlines/symbol"
f"?symbol={symbol}&client=web&streaming=false&lang=en&limit={limit}"
)
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=15) as r:
data = json.loads(r.read())
return data["items"] # list of news items
items = get_news("NASDAQ:AAPL", limit=10)
# item keys: id, title, provider, sourceLogoId, published (unix ts),
# source, urgency, link, permission, relatedSymbols, storyPath
# example:
# items[0]["title"] = "Apple Clears Major Legal Hurdle..."
# items[0]["published"] = 1776472317 (unix timestamp)
# items[0]["link"] = "https://stocktwits.com/..."
# items[0]["relatedSymbols"] = [{"symbol": "NASDAQ:AAPL", "logoid": "apple"}]
No auth or special headers needed. Returns up to 200 items per request.
Published trading ideas feed
import json, urllib.request
def get_ideas(sort="trending", page=1, symbol=None):
# Valid sort values (others return 400):
# "trending", "recent", "latest_popular", "week_popular",
# "suggested", "recent_extended", "picked_time"
url = f"https://www.tradingview.com/api/v1/ideas/?lang=en&sort={sort}&page={page}"
if symbol:
url += f"&symbol={symbol}" # e.g. "NASDAQ:AAPL"
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=15) as r:
return json.loads(r.read())
data = get_ideas("trending")
# data["count"] = 1000 (always 1000 — soft cap)
# data["page_size"] = 20
# data["page_count"] = 50
# data["next"] = "https://www.tradingview.com/api/v1/ideas/?page=2"
# data["results"] = list of idea objects
idea = data["results"][0]
# idea keys: id, name, description, created_at, chart_url, views_count,
# likes_count, comments_count, is_video, is_education, is_hot,
# symbol (dict with name/exchange/type/interval/direction),
# user (dict with username/is_pro/badges), image (big/middle URLs)
# idea["symbol"]["direction"]: 1=long, 2=short, 0=neutral
# Filter by symbol:
aapl_ideas = get_ideas(symbol="NASDAQ:AAPL")
API summary table
| Endpoint | Auth | Headers needed | Speed |
|---|---|---|---|
scanner.tradingview.com/{market}/scan | None | None | ~200ms |
symbol-search.tradingview.com/symbol_search/v3/ | None | Origin: https://www.tradingview.com | ~150ms |
symbol-search.tradingview.com/symbol_search/ (v1) | None | Origin: https://www.tradingview.com | ~100ms |
news-headlines.tradingview.com/v2/view/headlines/symbol | None | None | ~400ms |
www.tradingview.com/api/v1/ideas/ | None | None | ~300ms |
data.tradingview.com/quotes/ | None | None | Dead — connection refused |
economic-calendar.tradingview.com/events | Yes | — | HTTP 403 |
Gotchas
Scanner range is half-open: [0, 10] returns rows 0–9 (10 rows total). [10, 20] for the next page.
Column order is critical: The "d" array in each result row is positional — it exactly mirrors your "columns" array. Always zip them: dict(zip(columns, item["d"])).
data.tradingview.com/quotes/ is dead: The URL https://data.tradingview.com/quotes/?symbols=NASDAQ:AAPL closes the connection without a response. Use the scanner API instead for real-time quotes.
Scanner needs no Referer: scanner.tradingview.com works with just User-Agent. The symbol-search subdomain checks Origin (CORS enforcement on the server side).
Symbol search highlights: The v3 endpoint wraps matched text in <em> tags (e.g. "<em>AAPL</em>"). Strip them: re.sub(r'</?em>', '', symbol["symbol"]).
Ideas sort validation: Only specific values work. "sort=popular" returns 400. Use "trending", "recent", "latest_popular", "week_popular", "suggested".
Ideas count cap: The API always reports count=1000 regardless of actual corpus size. With page_size=20, max pages is 50.
Scanner server is AWS CloudFront (X-Amz-Cf-Pop header) with a custom Server: tv — no Cloudflare. No anti-bot on the scanner subdomain. Main www.tradingview.com is a React SPA with window.initData = {} (empty — no embedded data). All data is loaded via API calls after hydration.
Rate limits: No 429s observed in testing. 5 concurrent scanner calls complete in ~1s. Symbol search returns symbols_remaining in the response (counts against some quota — varies 90–180 across calls but never blocks). Observed no blocking after 15 rapid calls in a row.
Sector names: Use TradingView's own taxonomy, not GICS. "Technology" does not exist — use "Electronic Technology" (hardware/semis) or "Technology Services" (software/internet).
When to use the browser
The charting UI (/chart/), symbol detail pages (/symbols/NASDAQ-AAPL/), and the ideas page (/ideas/) are React SPAs — their visible data comes from the APIs above, not embedded HTML. Use browser + JS extraction only if you need visual chart screenshots or data from auth-gated pages (watchlists, portfolio, paper trading).
# Only if you need a chart screenshot:
goto_url("https://www.tradingview.com/chart/?symbol=NASDAQ:AAPL")
wait_for_load()
wait(3) # chart renders asynchronously after readyState
capture_screenshot("/tmp/aapl_chart.png", full=False)