
多市场切换:通过 iTick 同时展示股票、外汇、加密货币
1. 多市场切换的核心不是按钮,而是统一模型
很多页面会把“多市场切换”做成几个 Tab,看起来点一下就能从股票切到外汇,再切到加密货币。但真正麻烦的地方从来不是按钮,而是切换之后整套图表上下文是否仍然一致。股票市场有交易时段,外汇市场有更连续的时段逻辑,加密货币又是 24x7;不同市场的默认 symbol、精度、最小变动单位和周期偏好都可能不同。如果这些差异没有被抽象成统一模型,前端每加一个市场就会多一层判断。
所以第一步一定不是写 UI,而是先回答一个问题:对于图表系统来说,一个“市场上下文”到底应该包含什么。通常至少要包含 market、region、code、displayName、defaultResolution、timezone 和 session。当这些字段被收敛成固定结构后,你的切换逻辑才会变成“替换一个上下文对象”,而不是在组件里到处写 if/else。
2. 为什么 iTick 适合做多市场统一入口
如果你用的是多个供应商拼接而成的行情体系,多市场切换几乎一定会非常痛苦,因为每个供应商的命名、字段和鉴权方式都不同。iTick 的优势在于,它本身就覆盖了多个市场类别,并且在文档层面给出了相对统一的请求方式。对于教程站或图表中台来说,这一点非常关键,因为你可以把更多精力放在模型统一上,而不是不停做供应商之间的协议转换。
建议先在 https://itick.org/zh-cn 了解产品能力,再对照 https://docs.itick.org/zh-cn 确认各市场接口路径与字段语义。只要你在服务端先把多市场的差异收敛成一套内部标准,前端看到的就只是统一的 bars、quotes 和 symbol 元数据。真正的多市场体验,应该建立在“前端感知尽量少的市场差异”之上。
3. 先定义一份统一的市场配置表
多市场切换最值得提前做的一件事,就是建立配置表。不要把每个市场的默认周期、symbol 样例、session 规则散落在各个文件中。更稳妥的做法是集中到一个 map 里,由图表页、搜索接口和 datafeed 共同使用。只要配置表统一,切换市场这件事就会简单很多。
type MarketConfig = {
market: "stock" | "forex" | "crypto";
region: string;
label: string;
defaultCode: string;
defaultResolution: string;
session: string;
timezone: string;
};
export const MARKET_CONFIG: Record<string, MarketConfig> = {
stock: {
market: "stock",
region: "US",
label: "美股",
defaultCode: "AAPL",
defaultResolution: "D",
session: "0930-1600",
timezone: "America/New_York",
},
forex: {
market: "forex",
region: "GLOBAL",
label: "外汇",
defaultCode: "EURUSD",
defaultResolution: "60",
session: "24x5",
timezone: "Etc/UTC",
},
crypto: {
market: "crypto",
region: "US",
label: "加密货币",
defaultCode: "BTCUSDT",
defaultResolution: "60",
session: "24x7",
timezone: "Etc/UTC",
},
};
这份配置表的作用不只是方便初始化。后面当你做搜索、排序、分页或用户偏好持久化时,市场上下文都可以从这里读取。统一配置是一切多市场功能的底座。
4. 服务端代理要接住市场差异
前端不应该直接知道不同市场的 iTick 路径细节。更理想的方式是由服务端代理统一接住市场参数,把 market + region + code + resolution 转换成对应上游请求。这样做一来能保护 token,二来能把多市场的差异控制在服务端,三来能顺手加入缓存和日志。
import { NextRequest } from "next/server";
const ITICK_BASE = process.env.ITICK_BASE_URL ?? "https://api.itick.org";
const ITICK_TOKEN = process.env.ITICK_TOKEN!;
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const market = searchParams.get("market") ?? "crypto";
const region = searchParams.get("region") ?? "US";
const code = searchParams.get("code") ?? "BTCUSDT";
const kType = searchParams.get("kType") ?? "60";
const limit = searchParams.get("limit") ?? "300";
const upstream = new URL(`${ITICK_BASE}/${market}/kline`);
upstream.searchParams.set("region", region);
upstream.searchParams.set("code", code);
upstream.searchParams.set("kType", kType);
upstream.searchParams.set("limit", limit);
const res = await fetch(upstream, {
headers: { accept: "application/json", token: ITICK_TOKEN },
next: { revalidate: 5 },
});
if (!res.ok) {
return Response.json({ message: `iTick error: ${res.status}` }, { status: 500 });
}
return Response.json(await res.json());
}
多市场项目里,服务端代理是稳定性的分界线。只要这一层结构清楚,前端就不会被上游差异牵着走。
5. TradingView 里的 symbol 不能只是一串字符串
在单市场项目里,AAPL 或 BTCUSDT 这样的 symbol 看起来足够用了,但多市场时就远远不够。因为同一个代码在不同市场里可能意义不同,不同市场的展示名和请求参数也不一样。因此,Symbol 在图表系统里最好是一个结构,而不是只有 ticker。
一个实用做法是把 symbol 设计成“展示层”和“请求层”分离。展示层负责用户看到的名称,比如 NASDAQ:AAPL 或 FOREX:EURUSD;请求层则明确记录 market/region/code。这样做的好处是搜索结果、图表标题和后续接口请求都能沿着同一套数据结构流转,不容易出现“显示一个,实际请求另一个”的问题。
6. 切换市场时要保留哪些状态
很多页面在切市场时会顺手把所有状态清空,虽然简单,但体验通常很差。用户经常并不是想“重置一切”,而是想在另一个市场里继续观察类似的节奏。因此,切换市场时要不要保留周期、主题、技术指标、十字光标状态,其实需要认真设计。
比较稳妥的策略是:保留通用状态,重置强依赖市场的状态。比如亮暗主题、容器尺寸、工具栏开关这种显然应该保留;而默认 symbol、session、pricescale、搜索建议则应该随着市场切换更新。只要这条边界清楚,用户会觉得切换自然,而不是像跳到另一个页面。
7. 前端切换逻辑应该是“替换上下文”
在 React 页面里,不要让“切换市场”变成一串零散 setState。更好的方式是始终维护一个 currentMarketContext,图表组件和搜索组件都从这个上下文读取信息。这样当用户点下“外汇”时,你只需要替换上下文对象,后续所有依赖项自然更新。
const [currentMarket, setCurrentMarket] = useState<keyof typeof MARKET_CONFIG>("crypto");
const marketContext = MARKET_CONFIG[currentMarket];
useEffect(() => {
resetChartWithContext({
market: marketContext.market,
region: marketContext.region,
code: marketContext.defaultCode,
resolution: marketContext.defaultResolution,
});
}, [marketContext]);
这种写法的关键好处是可维护。你后面再加“港股”“期货”时,只需要补配置而不是重写切换逻辑。
8. 多市场最容易出错的是时间与精度
多市场切换一旦出问题,表面现象看起来千奇百怪,但真正高频的根因其实就两类:时间语义不统一,价格精度不统一。股票市场的日线、外汇市场的小数精度、加密市场的 24x7 时段,这些差异如果没有在 symbol 元数据层提前处理,图表就会出现刻度错位、价格显示难看、日线边界不对等问题。
所以不要把时间和精度当成渲染问题,它们本质上是数据建模问题。只要市场元数据在进入 TradingView 前就已经统一,图表部分反而会非常稳定。
9. 日志和缓存应该按市场维度拆开观察
多市场项目一旦上线,日志和缓存如果不带市场维度,很快就会失去价值。你至少应该能从日志里看出:哪个市场请求最多、哪个市场最慢、哪个市场的空数据率更高。否则你很可能以为“图表系统整体有问题”,但实际上只是某一类市场参数不稳定。
缓存也是同样道理。缓存 key 里必须带上 market 和 region,否则不同市场的结果非常容易串掉。看起来只是多拼两个字段,但这一步决定的是后续所有命中率统计和问题排查是否可靠。
10. 小结
多市场切换的本质不是让用户点不同按钮,而是让股票、外汇和加密货币在同一个图表系统里遵守一套统一的数据模型和状态管理方式。只要你把配置表、服务端代理、symbol 结构和市场上下文这几件事设计清楚,TradingView 与 iTick 这套组合就完全可以稳定承载多市场能力。
如果你准备继续扩展下一步,建议优先去做搜索与 symbol 解析,然后再补更细的市场时段和精度处理。这样你的多市场系统会从“能切换”逐步变成“真正可用”。详细接口能力仍建议继续对照 https://itick.org/zh-cn 与 https://docs.itick.org/zh-cn 逐项核对。


