Fix: Add symbol format conversion for A-Share/HK stocks in TradingView widgets

This commit is contained in:
wenliang 2026-02-12 12:37:02 +08:00
parent 10e8c1a261
commit 294d0b98e9
3 changed files with 33 additions and 8 deletions

View File

@ -12,9 +12,11 @@ import {
import { auth } from '@/lib/better-auth/auth'; import { auth } from '@/lib/better-auth/auth';
import { headers } from 'next/headers'; import { headers } from 'next/headers';
import { isStockInWatchlist } from '@/lib/actions/watchlist.actions'; import { isStockInWatchlist } from '@/lib/actions/watchlist.actions';
import { formatSymbolForTradingView } from '@/lib/utils';
export default async function StockDetails({ params }: StockDetailsPageProps) { export default async function StockDetails({ params }: StockDetailsPageProps) {
const { symbol } = await params; const { symbol } = await params;
const tvSymbol = formatSymbolForTradingView(symbol);
const scriptUrl = `https://s3.tradingview.com/external-embedding/embed-widget-`; const scriptUrl = `https://s3.tradingview.com/external-embedding/embed-widget-`;
const session = await auth.api.getSession({ const session = await auth.api.getSession({
@ -30,13 +32,13 @@ export default async function StockDetails({ params }: StockDetailsPageProps) {
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
<TradingViewWidget <TradingViewWidget
scriptUrl={`${scriptUrl}symbol-info.js`} scriptUrl={`${scriptUrl}symbol-info.js`}
config={SYMBOL_INFO_WIDGET_CONFIG(symbol)} config={SYMBOL_INFO_WIDGET_CONFIG(tvSymbol)}
height={170} height={170}
/> />
<TradingViewWidget <TradingViewWidget
scriptUrl={`${scriptUrl}advanced-chart.js`} scriptUrl={`${scriptUrl}advanced-chart.js`}
config={CANDLE_CHART_WIDGET_CONFIG(symbol)} config={CANDLE_CHART_WIDGET_CONFIG(tvSymbol)}
className="custom-chart" className="custom-chart"
height={600} height={600}
allowExpand={true} allowExpand={true}
@ -44,7 +46,7 @@ export default async function StockDetails({ params }: StockDetailsPageProps) {
<TradingViewWidget <TradingViewWidget
scriptUrl={`${scriptUrl}advanced-chart.js`} scriptUrl={`${scriptUrl}advanced-chart.js`}
config={BASELINE_WIDGET_CONFIG(symbol)} config={BASELINE_WIDGET_CONFIG(tvSymbol)}
className="custom-chart" className="custom-chart"
height={600} height={600}
allowExpand={true} allowExpand={true}
@ -64,19 +66,19 @@ export default async function StockDetails({ params }: StockDetailsPageProps) {
<TradingViewWidget <TradingViewWidget
scriptUrl={`${scriptUrl}technical-analysis.js`} scriptUrl={`${scriptUrl}technical-analysis.js`}
config={TECHNICAL_ANALYSIS_WIDGET_CONFIG(symbol)} config={TECHNICAL_ANALYSIS_WIDGET_CONFIG(tvSymbol)}
height={400} height={400}
/> />
<TradingViewWidget <TradingViewWidget
scriptUrl={`${scriptUrl}company-profile.js`} scriptUrl={`${scriptUrl}company-profile.js`}
config={COMPANY_PROFILE_WIDGET_CONFIG(symbol)} config={COMPANY_PROFILE_WIDGET_CONFIG(tvSymbol)}
height={440} height={440}
/> />
<TradingViewWidget <TradingViewWidget
scriptUrl={`${scriptUrl}financials.js`} scriptUrl={`${scriptUrl}financials.js`}
config={COMPANY_FINANCIALS_WIDGET_CONFIG(symbol)} config={COMPANY_FINANCIALS_WIDGET_CONFIG(tvSymbol)}
height={800} height={800}
/> />
</div> </div>

View File

@ -2,6 +2,7 @@
import React, { useEffect, useRef, memo } from 'react'; import React, { useEffect, useRef, memo } from 'react';
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { formatSymbolForTradingView } from '@/lib/utils';
interface TradingViewWatchlistProps { interface TradingViewWatchlistProps {
symbols: string[]; symbols: string[];
@ -26,7 +27,7 @@ function TradingViewWatchlist({ symbols }: TradingViewWatchlistProps) {
// Since we don't have exchange data easily, we'll try raw symbol. // Since we don't have exchange data easily, we'll try raw symbol.
// Ideally we'd prefix "NASDAQ:" or "NYSE:" but let's test without first. // Ideally we'd prefix "NASDAQ:" or "NYSE:" but let's test without first.
const symbolList = symbols.map(s => ({ const symbolList = symbols.map(s => ({
name: s, name: formatSymbolForTradingView(s),
displayName: s displayName: s
})); }));

View File

@ -153,3 +153,25 @@ export const getFormattedTodayDate = () => new Date().toLocaleDateString('en-US'
day: 'numeric', day: 'numeric',
timeZone: 'UTC', timeZone: 'UTC',
}); });
export function formatSymbolForTradingView(symbol: string): string {
if (!symbol) return symbol;
const upperSymbol = symbol.toUpperCase();
// Shanghai
if (upperSymbol.endsWith('.SS')) {
return `SSE:${upperSymbol.replace('.SS', '')}`;
}
// Shenzhen
if (upperSymbol.endsWith('.SZ')) {
return `SZSE:${upperSymbol.replace('.SZ', '')}`;
}
// Hong Kong
if (upperSymbol.endsWith('.HK')) {
return `HKEX:${upperSymbol.replace('.HK', '')}`;
}
return upperSymbol;
}