@pencil-agent/nano-pencil 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +267 -267
  2. package/dist/build-meta.json +3 -3
  3. package/dist/core/export-html/AGENT.md +11 -11
  4. package/dist/core/export-html/template.css +971 -971
  5. package/dist/core/export-html/template.html +54 -54
  6. package/dist/core/model/custom-providers.js +1 -1
  7. package/dist/core/model-registry.js +5 -5
  8. package/dist/extensions/builtin/AGENT.md +115 -115
  9. package/dist/extensions/builtin/browser/AGENT.md +17 -17
  10. package/dist/extensions/builtin/browser/agent-workspace/agent_helpers.py +12 -12
  11. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/amazon/product-search.md +198 -198
  12. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/archive-org/scraping.md +341 -341
  13. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv/scraping.md +311 -311
  14. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv-bulk/scraping.md +333 -333
  15. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/atlas/overview.md +70 -70
  16. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/booking-com/scraping.md +578 -578
  17. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/capterra/scraping.md +440 -440
  18. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/centilebrain/generate-estimates.md +110 -110
  19. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coingecko/scraping.md +325 -325
  20. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coinmarketcap/scraping.md +463 -463
  21. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coursera/scraping.md +360 -360
  22. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/craigslist/scraping.md +390 -390
  23. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/crossref/scraping.md +568 -568
  24. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/dev-to/scraping.md +323 -323
  25. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/duckduckgo/scraping.md +349 -349
  26. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/ebay/scraping.md +435 -435
  27. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/etsy/scraping.md +506 -506
  28. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/eventbrite/scraping.md +363 -363
  29. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/expedia/automation.md +168 -168
  30. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/groups.md +236 -236
  31. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/pages.md +295 -295
  32. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/framer/editor.md +108 -108
  33. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/fred/scraping.md +493 -493
  34. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/g2/scraping.md +580 -580
  35. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/genius/scraping.md +511 -511
  36. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/repo-actions.md +65 -65
  37. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/scraping.md +184 -184
  38. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/glassdoor/scraping.md +543 -543
  39. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gmail/compose.md +122 -122
  40. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/goodreads/scraping.md +461 -461
  41. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gutenberg/scraping.md +383 -383
  42. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/hackernews/scraping.md +243 -243
  43. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/howlongtobeat/scraping.md +473 -473
  44. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/imdb/scraping.md +271 -271
  45. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/itch-io/scraping.md +436 -436
  46. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/job-boards/indeed-glassdoor.md +1021 -1021
  47. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/letterboxd/scraping.md +349 -349
  48. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/linkedin/invitation-manager.md +109 -109
  49. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/loom/folder-enumeration.md +170 -170
  50. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/macrotrends/scraping.md +537 -537
  51. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/article-hydration.md +120 -120
  52. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/scraping.md +414 -414
  53. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/metacritic/scraping.md +477 -477
  54. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/musicbrainz/scraping.md +478 -478
  55. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/nasa/scraping.md +339 -339
  56. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/news-aggregation/multi-source.md +205 -205
  57. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/open-library/scraping.md +472 -472
  58. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openalex/scraping.md +470 -470
  59. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openstreetmap/scraping.md +490 -490
  60. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/package-registries/npm-pypi.md +478 -478
  61. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/polymarket/scraping.md +234 -234
  62. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/producthunt/scraping.md +307 -307
  63. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/pubmed/scraping.md +421 -421
  64. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/quora/scraping.md +364 -364
  65. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rawg/scraping.md +352 -352
  66. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/reddit/scraping.md +124 -124
  67. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rest-countries/scraping.md +233 -233
  68. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/sec-edgar/scraping.md +361 -361
  69. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/README.md +36 -36
  70. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/embedded-apps.md +72 -72
  71. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/knowledge-base.md +109 -109
  72. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/polaris-inputs.md +137 -137
  73. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/soundcloud/scraping.md +362 -362
  74. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/spotify/scraping.md +339 -339
  75. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/stackoverflow/scraping.md +435 -435
  76. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/steam/scraping.md +575 -575
  77. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/substack/scraping.md +338 -338
  78. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/thetechgeeks/pricing.md +52 -52
  79. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tiktok/upload.md +107 -107
  80. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tradingview/scraping.md +309 -309
  81. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trello/boards-and-lists.md +88 -88
  82. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trustpilot/scraping.md +375 -375
  83. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/walmart/scraping.md +444 -444
  84. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wayback-machine/scraping.md +306 -306
  85. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/weather/scraping.md +398 -398
  86. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wellfound/scraping.md +596 -596
  87. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/world-bank/scraping.md +356 -356
  88. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/xiaohongshu/scraping.md +84 -84
  89. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/youtube/scraping.md +418 -418
  90. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/zillow/scraping.md +433 -433
  91. package/dist/extensions/builtin/browser/browser.md +73 -73
  92. package/dist/extensions/builtin/browser/install.md +142 -142
  93. package/dist/extensions/builtin/browser/interaction-skills/connection.md +48 -48
  94. package/dist/extensions/builtin/browser/interaction-skills/cookies.md +3 -3
  95. package/dist/extensions/builtin/browser/interaction-skills/cross-origin-iframes.md +3 -3
  96. package/dist/extensions/builtin/browser/interaction-skills/dialogs.md +64 -64
  97. package/dist/extensions/builtin/browser/interaction-skills/downloads.md +3 -3
  98. package/dist/extensions/builtin/browser/interaction-skills/drag-and-drop.md +3 -3
  99. package/dist/extensions/builtin/browser/interaction-skills/dropdowns.md +3 -3
  100. package/dist/extensions/builtin/browser/interaction-skills/iframes.md +3 -3
  101. package/dist/extensions/builtin/browser/interaction-skills/network-requests.md +3 -3
  102. package/dist/extensions/builtin/browser/interaction-skills/print-as-pdf.md +3 -3
  103. package/dist/extensions/builtin/browser/interaction-skills/profile-sync.md +90 -90
  104. package/dist/extensions/builtin/browser/interaction-skills/screenshots.md +17 -17
  105. package/dist/extensions/builtin/browser/interaction-skills/scrolling.md +3 -3
  106. package/dist/extensions/builtin/browser/interaction-skills/shadow-dom.md +3 -3
  107. package/dist/extensions/builtin/browser/interaction-skills/tabs.md +69 -69
  108. package/dist/extensions/builtin/browser/interaction-skills/uploads.md +1 -1
  109. package/dist/extensions/builtin/browser/interaction-skills/viewport.md +3 -3
  110. package/dist/extensions/builtin/browser/src/browser_harness/AGENT.md +15 -15
  111. package/dist/extensions/builtin/browser/src/browser_harness/__init__.py +8 -8
  112. package/dist/extensions/builtin/browser/src/browser_harness/_ipc.py +90 -90
  113. package/dist/extensions/builtin/browser/src/browser_harness/admin.py +722 -722
  114. package/dist/extensions/builtin/browser/src/browser_harness/daemon.py +328 -328
  115. package/dist/extensions/builtin/browser/src/browser_harness/helpers.py +396 -396
  116. package/dist/extensions/builtin/browser/src/browser_harness/run.py +103 -103
  117. package/dist/extensions/builtin/discipline/skills/brainstorming/SKILL.md +33 -33
  118. package/dist/extensions/builtin/discipline/skills/executing-plans/SKILL.md +25 -25
  119. package/dist/extensions/builtin/discipline/skills/finishing-development-branch/SKILL.md +25 -25
  120. package/dist/extensions/builtin/discipline/skills/receiving-code-review/SKILL.md +22 -22
  121. package/dist/extensions/builtin/discipline/skills/requesting-code-review/SKILL.md +31 -31
  122. package/dist/extensions/builtin/discipline/skills/systematic-debugging/SKILL.md +28 -28
  123. package/dist/extensions/builtin/discipline/skills/test-driven-development/SKILL.md +32 -32
  124. package/dist/extensions/builtin/discipline/skills/using-git-worktrees/SKILL.md +25 -25
  125. package/dist/extensions/builtin/discipline/skills/verification-before-completion/SKILL.md +27 -27
  126. package/dist/extensions/builtin/discipline/skills/writing-plans/SKILL.md +26 -26
  127. package/dist/extensions/builtin/goal/README.md +67 -67
  128. package/dist/extensions/builtin/grub/README.md +112 -112
  129. package/dist/extensions/builtin/link-world/agent-workspace/README.md +16 -16
  130. package/dist/extensions/builtin/link-world/internet-search/internet-search.md +65 -65
  131. package/dist/extensions/builtin/link-world/link-world-agent.md +82 -82
  132. package/dist/extensions/builtin/link-world/linkworld.md +313 -313
  133. package/dist/extensions/builtin/link-world/network-routing/network-routing.md +67 -67
  134. package/dist/extensions/builtin/loop/README.md +92 -92
  135. package/dist/extensions/builtin/mcp/figma-design.md +68 -68
  136. package/dist/extensions/builtin/mcp/mcp-management.md +85 -85
  137. package/dist/extensions/builtin/recap/AGENT.md +15 -15
  138. package/dist/extensions/builtin/sal/README.md +72 -72
  139. package/dist/extensions/builtin/security-audit/README.md +289 -289
  140. package/dist/extensions/builtin/team/AGENT.md +112 -112
  141. package/dist/extensions/builtin/team/TESTING.md +299 -299
  142. package/dist/extensions/builtin/token-save/README.md +56 -56
  143. package/dist/extensions/optional/AGENT.md +10 -10
  144. package/dist/modes/interactive/controllers/input-submit-controller.js +2 -2
  145. package/dist/modes/interactive/controllers/stream-render-controller.js +2 -2
  146. package/dist/modes/interactive/interactive-mode.js +19 -19
  147. package/dist/modes/interactive/theme/dark.json +85 -85
  148. package/dist/modes/interactive/theme/light.json +84 -84
  149. package/dist/modes/interactive/theme/theme-schema.json +335 -335
  150. package/dist/modes/interactive/theme/warm.json +81 -81
  151. package/dist/node_modules/@pencil-agent/ai/dist/cli.js +0 -0
  152. package/dist/node_modules/@pencil-agent/ai/dist/models.generated.js +1 -1
  153. package/docs/ACP/345/215/217/350/256/256/351/233/206/346/210/220/345/274/200/345/217/221/346/226/207/346/241/243.md +851 -0
  154. package/docs/SDK-TESTING.md +364 -0
  155. package/docs/codex-goal-command-impl.md +1055 -1055
  156. package/docs/codex-goal-vs-grub.md +500 -500
  157. package/docs/custom-provider.md +27 -27
  158. package/docs/extensions.md +27 -27
  159. package/docs/keybindings.md +27 -27
  160. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +250 -250
  161. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +122 -122
  162. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +1222 -1222
  163. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/256/236/347/216/260/346/212/245/345/221/212.md" +158 -158
  164. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/257/271/346/257/224/345/210/206/346/236/220.md" +128 -128
  165. package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +320 -320
  166. package/docs/loop-usage-examples.md +214 -214
  167. package/docs/mem-core/346/212/200/346/234/257/346/226/207/346/241/243.md +593 -0
  168. package/docs/models.md +27 -27
  169. package/docs/packages.md +27 -27
  170. package/docs/pi-design-philosophy.md +457 -457
  171. package/docs/planmode.md +1987 -1987
  172. package/docs/prompt-templates.md +27 -27
  173. package/docs/providers.md +27 -27
  174. package/docs/sdk.md +27 -27
  175. package/docs/skills.md +27 -27
  176. package/docs/startup-performance-optimization.md +301 -0
  177. package/docs/themes.md +27 -27
  178. package/docs/tui.md +27 -27
  179. package/docs//350/256/244/347/237/245/345/234/260/345/233/276.md +47 -0
  180. package/package.json +190 -190
  181. package/docs/cc-agent-design.md +0 -1297
  182. package/docs/cc-tui-design.md +0 -1333
  183. package/docs/nanoPencil-/345/255/246/344/271/240/350/256/241/345/210/222.md +0 -170
  184. package/docs/scan-report.md +0 -3820
  185. package/docs//345/257/271/346/240/207Claude-Code.md +0 -1775
  186. package/docs//351/230/277/351/207/214/345/267/264/345/267/264/350/264/242/346/212/245/345/210/206/346/236/220/344/271/246.md +0 -261
@@ -1,309 +1,309 @@
1
- # TradingView — Scraping & Data Extraction
2
-
3
- `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.
4
-
5
- ## Do this first
6
-
7
- **Use the scanner API for bulk screener data — one POST, no browser, full column control.**
8
-
9
- ```python
10
- import json, urllib.request
11
-
12
- def tv_scan(payload, market="america"):
13
- data = json.dumps(payload).encode()
14
- req = urllib.request.Request(
15
- f"https://scanner.tradingview.com/{market}/scan",
16
- data=data,
17
- headers={"Content-Type": "application/json", "User-Agent": "Mozilla/5.0"}
18
- )
19
- with urllib.request.urlopen(req, timeout=20) as r:
20
- return json.loads(r.read())
21
- ```
22
-
23
- **No auth, no Referer, no cookies required for the scanner.** Responses arrive in ~200ms.
24
-
25
- ## Common workflows
26
-
27
- ### Top stocks by market cap (screener)
28
-
29
- ```python
30
- import json, urllib.request
31
-
32
- payload = {
33
- "filter": [],
34
- "options": {"lang": "en"},
35
- "columns": ["name", "close", "change", "volume", "market_cap_basic"],
36
- "sort": {"sortBy": "market_cap_basic", "sortOrder": "desc"},
37
- "range": [0, 10] # [start, end] — half-open, so this returns rows 0–9
38
- }
39
-
40
- data = json.dumps(payload).encode()
41
- req = urllib.request.Request(
42
- "https://scanner.tradingview.com/america/scan",
43
- data=data,
44
- headers={"Content-Type": "application/json", "User-Agent": "Mozilla/5.0"}
45
- )
46
- with urllib.request.urlopen(req, timeout=20) as r:
47
- resp = json.loads(r.read())
48
-
49
- # resp["totalCount"] = 19549 (all US-listed instruments)
50
- # resp["data"] is a list of {"s": "NASDAQ:NVDA", "d": [col0, col1, ...]}
51
- # "d" values align positionally with "columns" in the payload
52
-
53
- cols = payload["columns"]
54
- for item in resp["data"]:
55
- row = dict(zip(cols, item["d"]))
56
- symbol = item["s"] # e.g. "NASDAQ:AAPL"
57
- print(symbol, row["close"], row["change"], row["market_cap_basic"])
58
- # NASDAQ:NVDA 201.68 1.68 4900823822021.0
59
- # NASDAQ:AAPL 270.23 2.59 3967284528489.0
60
- # ...
61
- ```
62
-
63
- **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.
64
-
65
- ### Pagination
66
-
67
- ```python
68
- # Page 1: range [0, 20]
69
- # Page 2: range [20, 40]
70
- payload["range"] = [20, 40]
71
- ```
72
-
73
- ### Filtering stocks
74
-
75
- ```python
76
- payload = {
77
- "filter": [
78
- {"left": "market_cap_basic", "operation": "greater", "right": 10_000_000_000},
79
- {"left": "volume", "operation": "greater", "right": 5_000_000},
80
- {"left": "change", "operation": "in_range", "right": [2, 10]},
81
- {"left": "exchange", "operation": "equal", "right": "NASDAQ"},
82
- {"left": "sector", "operation": "equal", "right": "Electronic Technology"},
83
- ],
84
- "columns": ["name", "close", "change", "volume", "market_cap_basic",
85
- "description", "sector", "industry"],
86
- "sort": {"sortBy": "market_cap_basic", "sortOrder": "desc"},
87
- "range": [0, 20]
88
- }
89
- ```
90
-
91
- Valid filter operations: `greater`, `less`, `equal`, `in_range` (right = [min, max]), `match` (substring on `name`).
92
-
93
- Sector names use TradingView taxonomy (not GICS). Confirmed working values:
94
- - `"Electronic Technology"` — NVDA, AAPL, TSM
95
- - `"Technology Services"` — MSFT, GOOGL, META
96
- - `"Finance"`, `"Health Technology"`, `"Consumer Non-Durables"`
97
-
98
- ### Full list of tested valid column names
99
-
100
- ```python
101
- # Price & volume
102
- "name" # ticker (e.g. "AAPL")
103
- "description" # full name ("Apple Inc.")
104
- "close" # last price
105
- "open", "high", "low"
106
- "volume"
107
- "change" # % change today
108
- "change_abs" # absolute price change
109
- "change|1M" # 1-month % change (also: |6M, |1Y)
110
- "High.1M", "High.6M" # period high
111
- "High.All", "Low.All" # all-time high/low
112
- "price_52_week_high" # confirmed works
113
- "price_52_week_low" # confirmed works
114
- "premarket_change" # pre-market %
115
- "postmarket_change" # after-hours %
116
- "gap" # overnight gap %
117
- "change_from_open_abs" # intraday move from open
118
- "average_volume_10d_calc" # 10-day avg volume
119
- "relative_volume_10d_calc" # relative volume vs 10-day avg
120
- "relative_volume_intraday|5" # intraday relative vol (5m bars)
121
-
122
- # Fundamentals
123
- "market_cap_basic" # market cap in USD
124
- "earnings_per_share_diluted_ttm" # EPS TTM
125
- "price_earnings_ttm" # P/E TTM
126
- "P/E" # P/E (snapshot)
127
- "dividends_yield" # dividend yield %
128
- "beta_1_year" # beta
129
- "float_shares_outstanding" # float shares
130
-
131
- # Technical ratings & indicators
132
- "Recommend.All" # composite rating: -1 (strong sell) to +1 (strong buy)
133
- "RSI" # RSI 14
134
- "MACD.macd" # MACD line
135
-
136
- # Classification
137
- "sector", "industry", "country", "exchange"
138
- "type" # "stock", "fund", "dr" (depository receipt), etc.
139
-
140
- # NOTE: "52_week_high" / "52_week_low" are INVALID — use "price_52_week_high" / "price_52_week_low"
141
- # NOTE: "EPS_diluted_net" is INVALID — use "earnings_per_share_diluted_ttm"
142
- ```
143
-
144
- Bad columns return HTTP 400 with `{"error": "Unknown field \"X\""}`.
145
-
146
- ### Other scanner markets
147
-
148
- ```python
149
- # market argument options (confirmed working):
150
- # "america" — US equities (19,549 instruments)
151
- # "crypto" — crypto across exchanges (56,455 instruments)
152
- # "forex" — FX pairs (6,401 instruments)
153
- # "futures" — futures (53,947 instruments)
154
-
155
- # Crypto example
156
- payload = {
157
- "filter": [],
158
- "columns": ["name", "close", "change", "volume", "market_cap_calc"],
159
- "sort": {"sortBy": "market_cap_calc", "sortOrder": "desc"},
160
- "range": [0, 10]
161
- }
162
- resp = tv_scan(payload, market="crypto")
163
- # Returns BTC, ETH, etc. across Binance, Bybit, OKX...
164
- ```
165
-
166
- ### Symbol search (requires Origin header)
167
-
168
- ```python
169
- import json, urllib.request
170
-
171
- def symbol_search(query, exchange="", type_filter="", limit=50):
172
- url = (
173
- f"https://symbol-search.tradingview.com/symbol_search/v3/"
174
- f"?text={query}&hl=1&exchange={exchange}&lang=en"
175
- f"&search_type={type_filter or 'undefined'}&domain=production"
176
- )
177
- req = urllib.request.Request(url, headers={
178
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
179
- "Origin": "https://www.tradingview.com", # REQUIRED — 403 without this
180
- })
181
- with urllib.request.urlopen(req, timeout=15) as r:
182
- return json.loads(r.read())
183
-
184
- result = symbol_search("AAPL")
185
- # result["symbols_remaining"] = 137
186
- # result["symbols"] = list of up to 50 matches
187
- # result["symbols"][0] keys:
188
- # symbol, description, type, exchange, country, currency_code,
189
- # cusip, isin, cik_code, logoid, provider_id, source_id,
190
- # is_primary_listing, typespecs
191
- ```
192
-
193
- **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.
194
-
195
- Filter by exchange and type:
196
-
197
- ```python
198
- # Exact match on NASDAQ:AAPL
199
- result = symbol_search("AAPL", exchange="NASDAQ", type_filter="stock")
200
- # Returns 1 result — exact symbol only when exchange is specified
201
- ```
202
-
203
- ### News headlines for a symbol
204
-
205
- ```python
206
- import json, urllib.request
207
-
208
- def get_news(symbol, limit=20):
209
- # symbol format: "NASDAQ:AAPL", "NYSE:TSLA"
210
- url = (
211
- f"https://news-headlines.tradingview.com/v2/view/headlines/symbol"
212
- f"?symbol={symbol}&client=web&streaming=false&lang=en&limit={limit}"
213
- )
214
- req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
215
- with urllib.request.urlopen(req, timeout=15) as r:
216
- data = json.loads(r.read())
217
- return data["items"] # list of news items
218
-
219
- items = get_news("NASDAQ:AAPL", limit=10)
220
- # item keys: id, title, provider, sourceLogoId, published (unix ts),
221
- # source, urgency, link, permission, relatedSymbols, storyPath
222
- # example:
223
- # items[0]["title"] = "Apple Clears Major Legal Hurdle..."
224
- # items[0]["published"] = 1776472317 (unix timestamp)
225
- # items[0]["link"] = "https://stocktwits.com/..."
226
- # items[0]["relatedSymbols"] = [{"symbol": "NASDAQ:AAPL", "logoid": "apple"}]
227
- ```
228
-
229
- No auth or special headers needed. Returns up to 200 items per request.
230
-
231
- ### Published trading ideas feed
232
-
233
- ```python
234
- import json, urllib.request
235
-
236
- def get_ideas(sort="trending", page=1, symbol=None):
237
- # Valid sort values (others return 400):
238
- # "trending", "recent", "latest_popular", "week_popular",
239
- # "suggested", "recent_extended", "picked_time"
240
- url = f"https://www.tradingview.com/api/v1/ideas/?lang=en&sort={sort}&page={page}"
241
- if symbol:
242
- url += f"&symbol={symbol}" # e.g. "NASDAQ:AAPL"
243
- req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
244
- with urllib.request.urlopen(req, timeout=15) as r:
245
- return json.loads(r.read())
246
-
247
- data = get_ideas("trending")
248
- # data["count"] = 1000 (always 1000 — soft cap)
249
- # data["page_size"] = 20
250
- # data["page_count"] = 50
251
- # data["next"] = "https://www.tradingview.com/api/v1/ideas/?page=2"
252
- # data["results"] = list of idea objects
253
-
254
- idea = data["results"][0]
255
- # idea keys: id, name, description, created_at, chart_url, views_count,
256
- # likes_count, comments_count, is_video, is_education, is_hot,
257
- # symbol (dict with name/exchange/type/interval/direction),
258
- # user (dict with username/is_pro/badges), image (big/middle URLs)
259
- # idea["symbol"]["direction"]: 1=long, 2=short, 0=neutral
260
-
261
- # Filter by symbol:
262
- aapl_ideas = get_ideas(symbol="NASDAQ:AAPL")
263
- ```
264
-
265
- ## API summary table
266
-
267
- | Endpoint | Auth | Headers needed | Speed |
268
- |---|---|---|---|
269
- | `scanner.tradingview.com/{market}/scan` | None | None | ~200ms |
270
- | `symbol-search.tradingview.com/symbol_search/v3/` | None | `Origin: https://www.tradingview.com` | ~150ms |
271
- | `symbol-search.tradingview.com/symbol_search/` (v1) | None | `Origin: https://www.tradingview.com` | ~100ms |
272
- | `news-headlines.tradingview.com/v2/view/headlines/symbol` | None | None | ~400ms |
273
- | `www.tradingview.com/api/v1/ideas/` | None | None | ~300ms |
274
- | `data.tradingview.com/quotes/` | None | None | **Dead** — connection refused |
275
- | `economic-calendar.tradingview.com/events` | Yes | — | HTTP 403 |
276
-
277
- ## Gotchas
278
-
279
- **Scanner `range` is half-open**: `[0, 10]` returns rows 0–9 (10 rows total). `[10, 20]` for the next page.
280
-
281
- **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"]))`.
282
-
283
- **`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.
284
-
285
- **Scanner needs no Referer**: `scanner.tradingview.com` works with just `User-Agent`. The symbol-search subdomain checks `Origin` (CORS enforcement on the server side).
286
-
287
- **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"])`.
288
-
289
- **Ideas sort validation**: Only specific values work. `"sort=popular"` returns 400. Use `"trending"`, `"recent"`, `"latest_popular"`, `"week_popular"`, `"suggested"`.
290
-
291
- **Ideas count cap**: The API always reports `count=1000` regardless of actual corpus size. With `page_size=20`, max pages is 50.
292
-
293
- **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.
294
-
295
- **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.
296
-
297
- **Sector names**: Use TradingView's own taxonomy, not GICS. "Technology" does not exist — use `"Electronic Technology"` (hardware/semis) or `"Technology Services"` (software/internet).
298
-
299
- ## When to use the browser
300
-
301
- 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).
302
-
303
- ```python
304
- # Only if you need a chart screenshot:
305
- goto_url("https://www.tradingview.com/chart/?symbol=NASDAQ:AAPL")
306
- wait_for_load()
307
- wait(3) # chart renders asynchronously after readyState
308
- capture_screenshot("/tmp/aapl_chart.png", full=False)
309
- ```
1
+ # TradingView — Scraping & Data Extraction
2
+
3
+ `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.
4
+
5
+ ## Do this first
6
+
7
+ **Use the scanner API for bulk screener data — one POST, no browser, full column control.**
8
+
9
+ ```python
10
+ import json, urllib.request
11
+
12
+ def tv_scan(payload, market="america"):
13
+ data = json.dumps(payload).encode()
14
+ req = urllib.request.Request(
15
+ f"https://scanner.tradingview.com/{market}/scan",
16
+ data=data,
17
+ headers={"Content-Type": "application/json", "User-Agent": "Mozilla/5.0"}
18
+ )
19
+ with urllib.request.urlopen(req, timeout=20) as r:
20
+ return json.loads(r.read())
21
+ ```
22
+
23
+ **No auth, no Referer, no cookies required for the scanner.** Responses arrive in ~200ms.
24
+
25
+ ## Common workflows
26
+
27
+ ### Top stocks by market cap (screener)
28
+
29
+ ```python
30
+ import json, urllib.request
31
+
32
+ payload = {
33
+ "filter": [],
34
+ "options": {"lang": "en"},
35
+ "columns": ["name", "close", "change", "volume", "market_cap_basic"],
36
+ "sort": {"sortBy": "market_cap_basic", "sortOrder": "desc"},
37
+ "range": [0, 10] # [start, end] — half-open, so this returns rows 0–9
38
+ }
39
+
40
+ data = json.dumps(payload).encode()
41
+ req = urllib.request.Request(
42
+ "https://scanner.tradingview.com/america/scan",
43
+ data=data,
44
+ headers={"Content-Type": "application/json", "User-Agent": "Mozilla/5.0"}
45
+ )
46
+ with urllib.request.urlopen(req, timeout=20) as r:
47
+ resp = json.loads(r.read())
48
+
49
+ # resp["totalCount"] = 19549 (all US-listed instruments)
50
+ # resp["data"] is a list of {"s": "NASDAQ:NVDA", "d": [col0, col1, ...]}
51
+ # "d" values align positionally with "columns" in the payload
52
+
53
+ cols = payload["columns"]
54
+ for item in resp["data"]:
55
+ row = dict(zip(cols, item["d"]))
56
+ symbol = item["s"] # e.g. "NASDAQ:AAPL"
57
+ print(symbol, row["close"], row["change"], row["market_cap_basic"])
58
+ # NASDAQ:NVDA 201.68 1.68 4900823822021.0
59
+ # NASDAQ:AAPL 270.23 2.59 3967284528489.0
60
+ # ...
61
+ ```
62
+
63
+ **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.
64
+
65
+ ### Pagination
66
+
67
+ ```python
68
+ # Page 1: range [0, 20]
69
+ # Page 2: range [20, 40]
70
+ payload["range"] = [20, 40]
71
+ ```
72
+
73
+ ### Filtering stocks
74
+
75
+ ```python
76
+ payload = {
77
+ "filter": [
78
+ {"left": "market_cap_basic", "operation": "greater", "right": 10_000_000_000},
79
+ {"left": "volume", "operation": "greater", "right": 5_000_000},
80
+ {"left": "change", "operation": "in_range", "right": [2, 10]},
81
+ {"left": "exchange", "operation": "equal", "right": "NASDAQ"},
82
+ {"left": "sector", "operation": "equal", "right": "Electronic Technology"},
83
+ ],
84
+ "columns": ["name", "close", "change", "volume", "market_cap_basic",
85
+ "description", "sector", "industry"],
86
+ "sort": {"sortBy": "market_cap_basic", "sortOrder": "desc"},
87
+ "range": [0, 20]
88
+ }
89
+ ```
90
+
91
+ Valid filter operations: `greater`, `less`, `equal`, `in_range` (right = [min, max]), `match` (substring on `name`).
92
+
93
+ Sector names use TradingView taxonomy (not GICS). Confirmed working values:
94
+ - `"Electronic Technology"` — NVDA, AAPL, TSM
95
+ - `"Technology Services"` — MSFT, GOOGL, META
96
+ - `"Finance"`, `"Health Technology"`, `"Consumer Non-Durables"`
97
+
98
+ ### Full list of tested valid column names
99
+
100
+ ```python
101
+ # Price & volume
102
+ "name" # ticker (e.g. "AAPL")
103
+ "description" # full name ("Apple Inc.")
104
+ "close" # last price
105
+ "open", "high", "low"
106
+ "volume"
107
+ "change" # % change today
108
+ "change_abs" # absolute price change
109
+ "change|1M" # 1-month % change (also: |6M, |1Y)
110
+ "High.1M", "High.6M" # period high
111
+ "High.All", "Low.All" # all-time high/low
112
+ "price_52_week_high" # confirmed works
113
+ "price_52_week_low" # confirmed works
114
+ "premarket_change" # pre-market %
115
+ "postmarket_change" # after-hours %
116
+ "gap" # overnight gap %
117
+ "change_from_open_abs" # intraday move from open
118
+ "average_volume_10d_calc" # 10-day avg volume
119
+ "relative_volume_10d_calc" # relative volume vs 10-day avg
120
+ "relative_volume_intraday|5" # intraday relative vol (5m bars)
121
+
122
+ # Fundamentals
123
+ "market_cap_basic" # market cap in USD
124
+ "earnings_per_share_diluted_ttm" # EPS TTM
125
+ "price_earnings_ttm" # P/E TTM
126
+ "P/E" # P/E (snapshot)
127
+ "dividends_yield" # dividend yield %
128
+ "beta_1_year" # beta
129
+ "float_shares_outstanding" # float shares
130
+
131
+ # Technical ratings & indicators
132
+ "Recommend.All" # composite rating: -1 (strong sell) to +1 (strong buy)
133
+ "RSI" # RSI 14
134
+ "MACD.macd" # MACD line
135
+
136
+ # Classification
137
+ "sector", "industry", "country", "exchange"
138
+ "type" # "stock", "fund", "dr" (depository receipt), etc.
139
+
140
+ # NOTE: "52_week_high" / "52_week_low" are INVALID — use "price_52_week_high" / "price_52_week_low"
141
+ # NOTE: "EPS_diluted_net" is INVALID — use "earnings_per_share_diluted_ttm"
142
+ ```
143
+
144
+ Bad columns return HTTP 400 with `{"error": "Unknown field \"X\""}`.
145
+
146
+ ### Other scanner markets
147
+
148
+ ```python
149
+ # market argument options (confirmed working):
150
+ # "america" — US equities (19,549 instruments)
151
+ # "crypto" — crypto across exchanges (56,455 instruments)
152
+ # "forex" — FX pairs (6,401 instruments)
153
+ # "futures" — futures (53,947 instruments)
154
+
155
+ # Crypto example
156
+ payload = {
157
+ "filter": [],
158
+ "columns": ["name", "close", "change", "volume", "market_cap_calc"],
159
+ "sort": {"sortBy": "market_cap_calc", "sortOrder": "desc"},
160
+ "range": [0, 10]
161
+ }
162
+ resp = tv_scan(payload, market="crypto")
163
+ # Returns BTC, ETH, etc. across Binance, Bybit, OKX...
164
+ ```
165
+
166
+ ### Symbol search (requires Origin header)
167
+
168
+ ```python
169
+ import json, urllib.request
170
+
171
+ def symbol_search(query, exchange="", type_filter="", limit=50):
172
+ url = (
173
+ f"https://symbol-search.tradingview.com/symbol_search/v3/"
174
+ f"?text={query}&hl=1&exchange={exchange}&lang=en"
175
+ f"&search_type={type_filter or 'undefined'}&domain=production"
176
+ )
177
+ req = urllib.request.Request(url, headers={
178
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
179
+ "Origin": "https://www.tradingview.com", # REQUIRED — 403 without this
180
+ })
181
+ with urllib.request.urlopen(req, timeout=15) as r:
182
+ return json.loads(r.read())
183
+
184
+ result = symbol_search("AAPL")
185
+ # result["symbols_remaining"] = 137
186
+ # result["symbols"] = list of up to 50 matches
187
+ # result["symbols"][0] keys:
188
+ # symbol, description, type, exchange, country, currency_code,
189
+ # cusip, isin, cik_code, logoid, provider_id, source_id,
190
+ # is_primary_listing, typespecs
191
+ ```
192
+
193
+ **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.
194
+
195
+ Filter by exchange and type:
196
+
197
+ ```python
198
+ # Exact match on NASDAQ:AAPL
199
+ result = symbol_search("AAPL", exchange="NASDAQ", type_filter="stock")
200
+ # Returns 1 result — exact symbol only when exchange is specified
201
+ ```
202
+
203
+ ### News headlines for a symbol
204
+
205
+ ```python
206
+ import json, urllib.request
207
+
208
+ def get_news(symbol, limit=20):
209
+ # symbol format: "NASDAQ:AAPL", "NYSE:TSLA"
210
+ url = (
211
+ f"https://news-headlines.tradingview.com/v2/view/headlines/symbol"
212
+ f"?symbol={symbol}&client=web&streaming=false&lang=en&limit={limit}"
213
+ )
214
+ req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
215
+ with urllib.request.urlopen(req, timeout=15) as r:
216
+ data = json.loads(r.read())
217
+ return data["items"] # list of news items
218
+
219
+ items = get_news("NASDAQ:AAPL", limit=10)
220
+ # item keys: id, title, provider, sourceLogoId, published (unix ts),
221
+ # source, urgency, link, permission, relatedSymbols, storyPath
222
+ # example:
223
+ # items[0]["title"] = "Apple Clears Major Legal Hurdle..."
224
+ # items[0]["published"] = 1776472317 (unix timestamp)
225
+ # items[0]["link"] = "https://stocktwits.com/..."
226
+ # items[0]["relatedSymbols"] = [{"symbol": "NASDAQ:AAPL", "logoid": "apple"}]
227
+ ```
228
+
229
+ No auth or special headers needed. Returns up to 200 items per request.
230
+
231
+ ### Published trading ideas feed
232
+
233
+ ```python
234
+ import json, urllib.request
235
+
236
+ def get_ideas(sort="trending", page=1, symbol=None):
237
+ # Valid sort values (others return 400):
238
+ # "trending", "recent", "latest_popular", "week_popular",
239
+ # "suggested", "recent_extended", "picked_time"
240
+ url = f"https://www.tradingview.com/api/v1/ideas/?lang=en&sort={sort}&page={page}"
241
+ if symbol:
242
+ url += f"&symbol={symbol}" # e.g. "NASDAQ:AAPL"
243
+ req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
244
+ with urllib.request.urlopen(req, timeout=15) as r:
245
+ return json.loads(r.read())
246
+
247
+ data = get_ideas("trending")
248
+ # data["count"] = 1000 (always 1000 — soft cap)
249
+ # data["page_size"] = 20
250
+ # data["page_count"] = 50
251
+ # data["next"] = "https://www.tradingview.com/api/v1/ideas/?page=2"
252
+ # data["results"] = list of idea objects
253
+
254
+ idea = data["results"][0]
255
+ # idea keys: id, name, description, created_at, chart_url, views_count,
256
+ # likes_count, comments_count, is_video, is_education, is_hot,
257
+ # symbol (dict with name/exchange/type/interval/direction),
258
+ # user (dict with username/is_pro/badges), image (big/middle URLs)
259
+ # idea["symbol"]["direction"]: 1=long, 2=short, 0=neutral
260
+
261
+ # Filter by symbol:
262
+ aapl_ideas = get_ideas(symbol="NASDAQ:AAPL")
263
+ ```
264
+
265
+ ## API summary table
266
+
267
+ | Endpoint | Auth | Headers needed | Speed |
268
+ |---|---|---|---|
269
+ | `scanner.tradingview.com/{market}/scan` | None | None | ~200ms |
270
+ | `symbol-search.tradingview.com/symbol_search/v3/` | None | `Origin: https://www.tradingview.com` | ~150ms |
271
+ | `symbol-search.tradingview.com/symbol_search/` (v1) | None | `Origin: https://www.tradingview.com` | ~100ms |
272
+ | `news-headlines.tradingview.com/v2/view/headlines/symbol` | None | None | ~400ms |
273
+ | `www.tradingview.com/api/v1/ideas/` | None | None | ~300ms |
274
+ | `data.tradingview.com/quotes/` | None | None | **Dead** — connection refused |
275
+ | `economic-calendar.tradingview.com/events` | Yes | — | HTTP 403 |
276
+
277
+ ## Gotchas
278
+
279
+ **Scanner `range` is half-open**: `[0, 10]` returns rows 0–9 (10 rows total). `[10, 20]` for the next page.
280
+
281
+ **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"]))`.
282
+
283
+ **`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.
284
+
285
+ **Scanner needs no Referer**: `scanner.tradingview.com` works with just `User-Agent`. The symbol-search subdomain checks `Origin` (CORS enforcement on the server side).
286
+
287
+ **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"])`.
288
+
289
+ **Ideas sort validation**: Only specific values work. `"sort=popular"` returns 400. Use `"trending"`, `"recent"`, `"latest_popular"`, `"week_popular"`, `"suggested"`.
290
+
291
+ **Ideas count cap**: The API always reports `count=1000` regardless of actual corpus size. With `page_size=20`, max pages is 50.
292
+
293
+ **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.
294
+
295
+ **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.
296
+
297
+ **Sector names**: Use TradingView's own taxonomy, not GICS. "Technology" does not exist — use `"Electronic Technology"` (hardware/semis) or `"Technology Services"` (software/internet).
298
+
299
+ ## When to use the browser
300
+
301
+ 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).
302
+
303
+ ```python
304
+ # Only if you need a chart screenshot:
305
+ goto_url("https://www.tradingview.com/chart/?symbol=NASDAQ:AAPL")
306
+ wait_for_load()
307
+ wait(3) # chart renders asynchronously after readyState
308
+ capture_screenshot("/tmp/aapl_chart.png", full=False)
309
+ ```