TradingView
- TradingView是专业金融图表与社交交易平台,集图表分析、全球市场数据、策略研究和社交交流于一体 ,支持股票、外汇、加密货币、期货等多种资产。

- 它提供专业级的技术指标和绘图工具,还能用 Pine Script 自定义策略,并可连接部分券商账户直接交易;同时,用户还能在社区分享和浏览交易想法,像投资者的“社交网络”一样交流经验。

- 平台支持网页、桌面和手机多端使用,新手能轻松上手,资深交易者也能深度挖掘。


- TradingView.widget (https://www.tradingview.com/widget/)是 TradingView 官方提供的小部件,它自带了 TradingView 的行情数据源。不需要用户自己接入交易所或外汇的 API。
-
TradingView 会自动拉取并展示它支持的市场(外汇、股票、加密货币、期货等)的实时/延迟数据。
- 股票类 → 一般免费用户看到的是 延时数据(通常 15 分钟延迟)。
- 外汇/加密货币 → 大部分是 实时数据,因为这些数据源 TradingView 可以免费分发。
- 如果需要 全市场实时数据(比如 NASDAQ/NYSE 实时行情),TradingView 通常会要求开通 付费订阅 或交易所授权。
- 常见的 TradingView symbol:
| 类别 | 示例 Symbol | 含义 |
|---|---|---|
| 外汇 Forex | FX:EURUSD | 欧元 / 美元 |
FX:USDJPY | 美元 / 日元 | |
FX:GBPUSD | 英镑 / 美元 | |
FX:AUDUSD | 澳元 / 美元 | |
FX:USDCAD | 美元 / 加元 | |
| 美股 US Stocks | NASDAQ:AAPL | 苹果公司 |
NASDAQ:MSFT | 微软 | |
NASDAQ:TSLA | 特斯拉 | |
NYSE:IBM | IBM | |
NYSE:JPM | 摩根大通 | |
| 加密货币 Crypto | BINANCE:BTCUSDT | 比特币/USDT(币安) |
BINANCE:ETHUSDT | 以太坊/USDT(币安) | |
COINBASE:BTCUSD | 比特币/美元(Coinbase) | |
KRAKEN:ETHUSD | 以太坊/美元(Kraken) | |
| 指数 Index | SP:SPX | 标普500指数 |
DJ:DJI | 道琼斯工业指数 | |
NASDAQ:NDX | 纳斯达克100 | |
INDEX:NIKKEI | 日经225 | |
INDEX:HSI | 恒生指数 | |
| 大宗商品 Commodities | TVC:GOLD | 黄金现货 |
TVC:SILVER | 白银现货 | |
TVC:USOIL | WTI 原油 | |
TVC:UKOIL | 布伦特原油 |
- 官方提供了使用代码示例:

加载关键代码
- 因为 TradingView 的脚本不是 React/Next.js 内置的模块,而是一个外部的全局库。我们得按照它的特性来“引入 → 等待加载 → 使用”,否则会报错或者无法正常渲染。
-
Next.js 的 SSR(服务端渲染)环境没有
window- Next.js 默认会在服务端也尝试运行组件。
- 服务端没有 DOM 和
window,所以window.TradingView根本不可能存在。 - 如果不加
"use client"或不在useEffect里调用,会直接在服务端渲染时报错。
-
React 生命周期的节奏
- React 在组件挂载前执行 JSX → DOM 元素还没生成。
- 你需要等到组件“挂载”之后,容器
<div id="tradingview_widget">已经出现在 DOM 里,再去告诉 TradingView 把图表插进去。 useEffect正好就是“等 DOM 渲染完再执行副作用”的钩子。
- 负责 动态加载 TradingView 提供的外部脚本(tv.js) 的代码:
useEffect(() => {
// Load TradingView widget script
const script = document.createElement("script") // 创建一个新的 <script>元素
script.src = "https://s3.tradingview.com/tv.js" // 设置 script 的加载地址,这个库会往window挂载一个全局window.TradingView对象
script.async = true // 告诉浏览器异步加载脚本,不会阻塞页面渲染
script.onload = () => setIsLoaded(true) // 当脚本加载完成时触发 onload 回调
document.body.appendChild(script) // 把 <script> 元素插入到 <body> 里,浏览器开始去下载并执行它
return () => { // 清理函数,会在组件卸载时执行。作用是把之前插入的`<script> 从 DOM 中移除,避免内存泄漏或重复加载。
document.body.removeChild(script)
}
}, []) // 依赖数组 `[]` 是空的 → 代表这个 effect **只在挂载时运行一次**,卸载时运行 cleanup。
- 在组件挂载时动态引入 TradingView 的外部库,并在加载完成后更新状态;当组件卸载时,再把这个 script 元素移除。
使用代码 这个脚本是如何被使用的?
- 脚本加载后,
window.TradingView出现。 - 里面有一个构造函数
TradingView.widget(...),这是 TradingView 提供的 API,用来在页面上生成交互式行情图表。
- 在脚本
onload事件触发时,组件里执行了setIsLoaded(true)。 - 这就会触发 第二个 useEffect。
new window.TradingView.widget({...})会在页面上渲染一个 TradingView 图表。- 其中的
container_id: "tradingview_widget"指定了图表要挂载到哪个 DOM 节点。这个 id 对应的 JSX 里写的<div id="tradingview_widget">...</div>。
useEffect(() => {
if (isLoaded && containerRef.current) { // 当脚本已经加载完成 (isLoaded 为 true) 且容器 div 已经存在时,才初始化 TradingView 的图表小部件
new window.TradingView.widget({
autosize: true, // 让图表根据容器大小自动伸缩
symbol: "FX:EURUSD", // 设置初始显示的交易品种,这里是外汇 EUR/USD
interval: "D", // 图表的时间间隔,这里是 "D" → 日线
timezone: "Etc/UTC", // 设置时区,这里用 UTC
theme: "dark", // 图表主题,"dark" → 黑色背景
style: "1", // 图表样式编号,"1" 表示蜡烛图(TradingView 内置的样式类型)
locale: "en", // 图表语言,这里是英文
toolbar_bg: "#131722", // 工具栏背景颜色
enable_publishing: false, // 是否允许用户发布分享自己的图表,false 表示关闭
hide_top_toolbar: false, // 是否隐藏顶部工具栏,false 表示显示
hide_legend: false, // 是否隐藏图例,false 表示显示
save_image: false, // 是否允许保存图表为图片,false 表示不允许
container_id: "tradingview_widget", // 指定挂载图表的容器元素 id
})
}
}, [isLoaded]) // 依赖项是 isLoaded → 只有当脚本加载状态改变时才会运行
- TradingView 的代码会往这个
<div>里插入一个 iframe,里面就是 TradingView 提供的实时行情图表。 - 最终用户就能在页面上看到一个可以缩放、切换时间周期、加指标的互动图表。
多市场切换的 TradingView Demo
demo运行与效果
# 如果你还没有项目,可以先新建一个:
npx create-next-app@latest tradingview-demo
cd tradingview-demo
app/tradingview/page.tsx # 用于放置TradingViewSwitcher代码
npm run dev
- 然后访问: http://localhost:3000/tradingview,多市场切换的 TradingView Demo,用 Tabs 切换 symbol,每次切换都会加载对应的图表:

代码
"use client"
import { useEffect, useRef, useState } from "react"
declare global {
interface Window {
TradingView: any
}
}
export default function TradingViewPage() {
const containerRef = useRef<HTMLDivElement>(null)
const [isLoaded, setIsLoaded] = useState(false)
const [symbol, setSymbol] = useState("FX:EURUSD") // 当前选择的交易对
// 加载 TradingView 脚本
useEffect(() => {
const script = document.createElement("script")
script.src = "https://s3.tradingview.com/tv.js"
script.async = true
script.onload = () => setIsLoaded(true)
document.body.appendChild(script)
return () => {
document.body.removeChild(script)
}
}, [])
// 初始化 / 更新 TradingView 图表
useEffect(() => {
if (isLoaded && containerRef.current) {
containerRef.current.innerHTML = "" // 清空旧图表,避免重叠
new window.TradingView.widget({
autosize: true,
symbol: symbol,
interval: "D",
timezone: "Etc/UTC",
theme: "dark",
style: "1",
locale: "en",
container_id: "tradingview_container",
})
}
}, [isLoaded, symbol]) // 每次 symbol 改变都会刷新图表
return (
<section className="py-16 px-4 md:px-8">
<div className="max-w-6xl mx-auto text-left mb-10">
<h2 className="text-3xl md:text-4xl font-bold mb-4">
Live Market Analysis
</h2>
<p className="text-gray-400 max-w-2xl">
Track real-time market movements with professional-grade charts.
</p>
</div>
<div className="bg-gray-900 border border-gray-700 rounded-2xl shadow-md overflow-hidden">
{/* Tabs 按钮 */}
<div className="flex border-b border-gray-700">
<button
className={`flex-1 py-2 text-sm font-medium ${
symbol === "FX:EURUSD" ? "text-white bg-gray-800" : "text-gray-400 hover:text-white"
}`}
onClick={() => setSymbol("FX:EURUSD")}
>
EUR/USD
</button>
<button
className={`flex-1 py-2 text-sm font-medium ${
symbol === "FX:GBPUSD" ? "text-white bg-gray-800" : "text-gray-400 hover:text-white"
}`}
onClick={() => setSymbol("FX:GBPUSD")}
>
GBP/USD
</button>
<button
className={`flex-1 py-2 text-sm font-medium ${
symbol === "FX:USDJPY" ? "text-white bg-gray-800" : "text-gray-400 hover:text-white"
}`}
onClick={() => setSymbol("FX:USDJPY")}
>
USD/JPY
</button>
<button
className={`flex-1 py-2 text-sm font-medium ${
symbol === "OANDA:XAUUSD" ? "text-white bg-gray-800" : "text-gray-400 hover:text-white"
}`}
onClick={() => setSymbol("OANDA:XAUUSD")}
>
XAU/USD
</button>
</div>
{/* TradingView 容器 */}
<div
ref={containerRef}
id="tradingview_container"
className="w-full h-[600px]"
></div>
</div>
</section>
)
}

被折叠的 条评论
为什么被折叠?



