update pay ui
This commit is contained in:
@@ -79,14 +79,100 @@ const handleSSEMessage = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理深交所实时消息 (realtime)
|
* 从深交所数据中提取盘口价格和量
|
||||||
|
* 新 API 格式:直接使用 bid_prices/bid_volumes/ask_prices/ask_volumes 数组
|
||||||
|
* 旧 API 格式:使用 bids/asks 对象数组
|
||||||
|
*/
|
||||||
|
const extractSZSEOrderBook = (
|
||||||
|
stockData: SZSEStockData
|
||||||
|
): { bidPrices: number[]; bidVolumes: number[]; askPrices: number[]; askVolumes: number[] } => {
|
||||||
|
// 新 API 格式:直接是数组
|
||||||
|
if (stockData.bid_prices && Array.isArray(stockData.bid_prices)) {
|
||||||
|
return {
|
||||||
|
bidPrices: stockData.bid_prices,
|
||||||
|
bidVolumes: stockData.bid_volumes || [],
|
||||||
|
askPrices: stockData.ask_prices || [],
|
||||||
|
askVolumes: stockData.ask_volumes || [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 旧 API 格式:对象数组
|
||||||
|
const { prices: bidPrices, volumes: bidVolumes } = extractOrderBook(stockData.bids);
|
||||||
|
const { prices: askPrices, volumes: askVolumes } = extractOrderBook(stockData.asks);
|
||||||
|
return { bidPrices, bidVolumes, askPrices, askVolumes };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从深交所数据中提取价格字段(兼容新旧 API)
|
||||||
|
*/
|
||||||
|
const extractSZSEPrices = (stockData: SZSEStockData) => {
|
||||||
|
return {
|
||||||
|
prevClose: stockData.prev_close_px ?? stockData.prev_close ?? 0,
|
||||||
|
volume: stockData.total_volume_trade ?? stockData.volume ?? 0,
|
||||||
|
amount: stockData.total_value_trade ?? stockData.amount ?? 0,
|
||||||
|
upperLimit: stockData.upper_limit_px ?? stockData.upper_limit,
|
||||||
|
lowerLimit: stockData.lower_limit_px ?? stockData.lower_limit,
|
||||||
|
tradingPhase: stockData.trading_phase_code ?? stockData.trading_phase,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理深交所股票行情消息(新 API 格式:type='stock')
|
||||||
|
*/
|
||||||
|
const handleSZSEStockMessage = (
|
||||||
|
data: SZSEStockData,
|
||||||
|
timestamp: string,
|
||||||
|
subscribedCodes: Set<string>,
|
||||||
|
prevQuotes: QuotesMap
|
||||||
|
): QuotesMap | null => {
|
||||||
|
const rawCode = data.security_id;
|
||||||
|
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
||||||
|
|
||||||
|
if (!subscribedCodes.has(rawCode) && !subscribedCodes.has(fullCode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { bidPrices, bidVolumes, askPrices, askVolumes } = extractSZSEOrderBook(data);
|
||||||
|
const { prevClose, volume, amount, upperLimit, lowerLimit, tradingPhase } = extractSZSEPrices(data);
|
||||||
|
|
||||||
|
const updated: QuotesMap = { ...prevQuotes };
|
||||||
|
updated[fullCode] = {
|
||||||
|
code: fullCode,
|
||||||
|
name: prevQuotes[fullCode]?.name || '',
|
||||||
|
price: data.last_px,
|
||||||
|
prevClose,
|
||||||
|
open: data.open_px,
|
||||||
|
high: data.high_px,
|
||||||
|
low: data.low_px,
|
||||||
|
volume,
|
||||||
|
amount,
|
||||||
|
numTrades: data.num_trades,
|
||||||
|
upperLimit,
|
||||||
|
lowerLimit,
|
||||||
|
change: data.last_px - prevClose,
|
||||||
|
changePct: calcChangePct(data.last_px, prevClose),
|
||||||
|
bidPrices,
|
||||||
|
bidVolumes,
|
||||||
|
askPrices,
|
||||||
|
askVolumes,
|
||||||
|
tradingPhase,
|
||||||
|
updateTime: data.update_time || timestamp,
|
||||||
|
exchange: 'SZSE',
|
||||||
|
} as QuoteData;
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理深交所实时消息 (兼容新旧 API)
|
||||||
|
* 新 API: type='stock'/'bond'/'fund', data 直接是行情对象
|
||||||
|
* 旧 API: type='realtime', category='stock'/'bond'/etc
|
||||||
*/
|
*/
|
||||||
const handleSZSERealtimeMessage = (
|
const handleSZSERealtimeMessage = (
|
||||||
msg: SZSERealtimeMessage,
|
msg: SZSERealtimeMessage,
|
||||||
subscribedCodes: Set<string>,
|
subscribedCodes: Set<string>,
|
||||||
prevQuotes: QuotesMap
|
prevQuotes: QuotesMap
|
||||||
): QuotesMap | null => {
|
): QuotesMap | null => {
|
||||||
const { category, data, timestamp } = msg;
|
const { type, category, data, timestamp } = msg;
|
||||||
const rawCode = data.security_id;
|
const rawCode = data.security_id;
|
||||||
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
||||||
|
|
||||||
@@ -96,33 +182,36 @@ const handleSZSERealtimeMessage = (
|
|||||||
|
|
||||||
const updated: QuotesMap = { ...prevQuotes };
|
const updated: QuotesMap = { ...prevQuotes };
|
||||||
|
|
||||||
switch (category) {
|
// 确定实际的类别:新 API 直接用 type,旧 API 用 category
|
||||||
|
const actualCategory = (type === 'realtime' ? category : type) as string;
|
||||||
|
|
||||||
|
switch (actualCategory) {
|
||||||
case 'stock': {
|
case 'stock': {
|
||||||
const stockData = data as SZSEStockData;
|
const stockData = data as SZSEStockData;
|
||||||
const { prices: bidPrices, volumes: bidVolumes } = extractOrderBook(stockData.bids);
|
const { bidPrices, bidVolumes, askPrices, askVolumes } = extractSZSEOrderBook(stockData);
|
||||||
const { prices: askPrices, volumes: askVolumes } = extractOrderBook(stockData.asks);
|
const { prevClose, volume, amount, upperLimit, lowerLimit, tradingPhase } = extractSZSEPrices(stockData);
|
||||||
|
|
||||||
updated[fullCode] = {
|
updated[fullCode] = {
|
||||||
code: fullCode,
|
code: fullCode,
|
||||||
name: prevQuotes[fullCode]?.name || '',
|
name: prevQuotes[fullCode]?.name || '',
|
||||||
price: stockData.last_px,
|
price: stockData.last_px,
|
||||||
prevClose: stockData.prev_close,
|
prevClose,
|
||||||
open: stockData.open_px,
|
open: stockData.open_px,
|
||||||
high: stockData.high_px,
|
high: stockData.high_px,
|
||||||
low: stockData.low_px,
|
low: stockData.low_px,
|
||||||
volume: stockData.volume,
|
volume,
|
||||||
amount: stockData.amount,
|
amount,
|
||||||
numTrades: stockData.num_trades,
|
numTrades: stockData.num_trades,
|
||||||
upperLimit: stockData.upper_limit,
|
upperLimit,
|
||||||
lowerLimit: stockData.lower_limit,
|
lowerLimit,
|
||||||
change: stockData.last_px - stockData.prev_close,
|
change: stockData.last_px - prevClose,
|
||||||
changePct: calcChangePct(stockData.last_px, stockData.prev_close),
|
changePct: calcChangePct(stockData.last_px, prevClose),
|
||||||
bidPrices,
|
bidPrices,
|
||||||
bidVolumes,
|
bidVolumes,
|
||||||
askPrices,
|
askPrices,
|
||||||
askVolumes,
|
askVolumes,
|
||||||
tradingPhase: stockData.trading_phase,
|
tradingPhase,
|
||||||
updateTime: timestamp,
|
updateTime: stockData.update_time || timestamp,
|
||||||
exchange: 'SZSE',
|
exchange: 'SZSE',
|
||||||
} as QuoteData;
|
} as QuoteData;
|
||||||
break;
|
break;
|
||||||
@@ -155,13 +244,15 @@ const handleSZSERealtimeMessage = (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'bond': {
|
case 'bond':
|
||||||
|
case 'fund': {
|
||||||
const bondData = data as SZSEBondData;
|
const bondData = data as SZSEBondData;
|
||||||
|
const prevClose = (bondData as SZSEStockData).prev_close_px ?? bondData.prev_close ?? 0;
|
||||||
updated[fullCode] = {
|
updated[fullCode] = {
|
||||||
code: fullCode,
|
code: fullCode,
|
||||||
name: prevQuotes[fullCode]?.name || '',
|
name: prevQuotes[fullCode]?.name || '',
|
||||||
price: bondData.last_px,
|
price: bondData.last_px,
|
||||||
prevClose: bondData.prev_close,
|
prevClose,
|
||||||
open: bondData.open_px,
|
open: bondData.open_px,
|
||||||
high: bondData.high_px,
|
high: bondData.high_px,
|
||||||
low: bondData.low_px,
|
low: bondData.low_px,
|
||||||
@@ -169,8 +260,8 @@ const handleSZSERealtimeMessage = (
|
|||||||
amount: bondData.amount,
|
amount: bondData.amount,
|
||||||
numTrades: bondData.num_trades,
|
numTrades: bondData.num_trades,
|
||||||
weightedAvgPx: bondData.weighted_avg_px,
|
weightedAvgPx: bondData.weighted_avg_px,
|
||||||
change: bondData.last_px - bondData.prev_close,
|
change: bondData.last_px - prevClose,
|
||||||
changePct: calcChangePct(bondData.last_px, bondData.prev_close),
|
changePct: calcChangePct(bondData.last_px, prevClose),
|
||||||
bidPrices: [],
|
bidPrices: [],
|
||||||
bidVolumes: [],
|
bidVolumes: [],
|
||||||
askPrices: [],
|
askPrices: [],
|
||||||
@@ -280,45 +371,90 @@ const handleSZSERealtimeMessage = (
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理深交所快照消息 (snapshot)
|
* 处理深交所快照消息 (snapshot)
|
||||||
* 订阅后首次返回的批量数据
|
* 新 API: data 是单个股票对象 { security_id, last_px, ... }
|
||||||
|
* 旧 API: data 是批量数据 { stocks: [...], indexes: [...], bonds: [...] }
|
||||||
*/
|
*/
|
||||||
const handleSZSESnapshotMessage = (
|
const handleSZSESnapshotMessage = (
|
||||||
msg: SZSESnapshotMessage,
|
msg: SZSESnapshotMessage,
|
||||||
subscribedCodes: Set<string>,
|
subscribedCodes: Set<string>,
|
||||||
prevQuotes: QuotesMap
|
prevQuotes: QuotesMap
|
||||||
): QuotesMap | null => {
|
): QuotesMap | null => {
|
||||||
const { stocks = [], indexes = [], bonds = [] } = msg.data || {};
|
|
||||||
const updated: QuotesMap = { ...prevQuotes };
|
const updated: QuotesMap = { ...prevQuotes };
|
||||||
let hasUpdate = false;
|
let hasUpdate = false;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const data = msg.data as any;
|
||||||
|
|
||||||
|
// 新 API 格式:data 直接是单个股票对象
|
||||||
|
if (data && data.security_id) {
|
||||||
|
const stockData = data as SZSEStockData;
|
||||||
|
const rawCode = stockData.security_id;
|
||||||
|
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
||||||
|
|
||||||
|
if (subscribedCodes.has(rawCode) || subscribedCodes.has(fullCode)) {
|
||||||
|
const { bidPrices, bidVolumes, askPrices, askVolumes } = extractSZSEOrderBook(stockData);
|
||||||
|
const { prevClose, volume, amount, upperLimit, lowerLimit, tradingPhase } = extractSZSEPrices(stockData);
|
||||||
|
|
||||||
|
updated[fullCode] = {
|
||||||
|
code: fullCode,
|
||||||
|
name: '',
|
||||||
|
price: stockData.last_px,
|
||||||
|
prevClose,
|
||||||
|
open: stockData.open_px,
|
||||||
|
high: stockData.high_px,
|
||||||
|
low: stockData.low_px,
|
||||||
|
volume,
|
||||||
|
amount,
|
||||||
|
numTrades: stockData.num_trades,
|
||||||
|
upperLimit,
|
||||||
|
lowerLimit,
|
||||||
|
change: stockData.last_px - prevClose,
|
||||||
|
changePct: calcChangePct(stockData.last_px, prevClose),
|
||||||
|
bidPrices,
|
||||||
|
bidVolumes,
|
||||||
|
askPrices,
|
||||||
|
askVolumes,
|
||||||
|
tradingPhase,
|
||||||
|
updateTime: stockData.update_time || msg.timestamp,
|
||||||
|
exchange: 'SZSE',
|
||||||
|
} as QuoteData;
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旧 API 格式:批量数据
|
||||||
|
const { stocks = [], indexes = [], bonds = [] } = data || {};
|
||||||
|
|
||||||
stocks.forEach((s: SZSEStockData) => {
|
stocks.forEach((s: SZSEStockData) => {
|
||||||
const rawCode = s.security_id;
|
const rawCode = s.security_id;
|
||||||
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
const fullCode = rawCode.includes('.') ? rawCode : `${rawCode}.SZ`;
|
||||||
|
|
||||||
if (subscribedCodes.has(rawCode) || subscribedCodes.has(fullCode)) {
|
if (subscribedCodes.has(rawCode) || subscribedCodes.has(fullCode)) {
|
||||||
hasUpdate = true;
|
hasUpdate = true;
|
||||||
const { prices: bidPrices, volumes: bidVolumes } = extractOrderBook(s.bids);
|
const { bidPrices, bidVolumes, askPrices, askVolumes } = extractSZSEOrderBook(s);
|
||||||
const { prices: askPrices, volumes: askVolumes } = extractOrderBook(s.asks);
|
const { prevClose, volume, amount, upperLimit, lowerLimit, tradingPhase } = extractSZSEPrices(s);
|
||||||
|
|
||||||
updated[fullCode] = {
|
updated[fullCode] = {
|
||||||
code: fullCode,
|
code: fullCode,
|
||||||
name: '',
|
name: '',
|
||||||
price: s.last_px,
|
price: s.last_px,
|
||||||
prevClose: s.prev_close,
|
prevClose,
|
||||||
open: s.open_px,
|
open: s.open_px,
|
||||||
high: s.high_px,
|
high: s.high_px,
|
||||||
low: s.low_px,
|
low: s.low_px,
|
||||||
volume: s.volume,
|
volume,
|
||||||
amount: s.amount,
|
amount,
|
||||||
numTrades: s.num_trades,
|
numTrades: s.num_trades,
|
||||||
upperLimit: s.upper_limit,
|
upperLimit,
|
||||||
lowerLimit: s.lower_limit,
|
lowerLimit,
|
||||||
change: s.last_px - s.prev_close,
|
change: s.last_px - prevClose,
|
||||||
changePct: calcChangePct(s.last_px, s.prev_close),
|
changePct: calcChangePct(s.last_px, prevClose),
|
||||||
bidPrices,
|
bidPrices,
|
||||||
bidVolumes,
|
bidVolumes,
|
||||||
askPrices,
|
askPrices,
|
||||||
askVolumes,
|
askVolumes,
|
||||||
|
tradingPhase,
|
||||||
exchange: 'SZSE',
|
exchange: 'SZSE',
|
||||||
} as QuoteData;
|
} as QuoteData;
|
||||||
}
|
}
|
||||||
@@ -358,18 +494,19 @@ const handleSZSESnapshotMessage = (
|
|||||||
|
|
||||||
if (subscribedCodes.has(rawCode) || subscribedCodes.has(fullCode)) {
|
if (subscribedCodes.has(rawCode) || subscribedCodes.has(fullCode)) {
|
||||||
hasUpdate = true;
|
hasUpdate = true;
|
||||||
|
const prevClose = (b as SZSEStockData).prev_close_px ?? b.prev_close ?? 0;
|
||||||
updated[fullCode] = {
|
updated[fullCode] = {
|
||||||
code: fullCode,
|
code: fullCode,
|
||||||
name: '',
|
name: '',
|
||||||
price: b.last_px,
|
price: b.last_px,
|
||||||
prevClose: b.prev_close,
|
prevClose,
|
||||||
open: b.open_px,
|
open: b.open_px,
|
||||||
high: b.high_px,
|
high: b.high_px,
|
||||||
low: b.low_px,
|
low: b.low_px,
|
||||||
volume: b.volume,
|
volume: b.volume,
|
||||||
amount: b.amount,
|
amount: b.amount,
|
||||||
change: b.last_px - b.prev_close,
|
change: b.last_px - prevClose,
|
||||||
changePct: calcChangePct(b.last_px, b.prev_close),
|
changePct: calcChangePct(b.last_px, prevClose),
|
||||||
bidPrices: [],
|
bidPrices: [],
|
||||||
bidVolumes: [],
|
bidVolumes: [],
|
||||||
askPrices: [],
|
askPrices: [],
|
||||||
@@ -419,27 +556,25 @@ export const useRealtimeQuote = (codes: string[] = []): UseRealtimeQuoteReturn =
|
|||||||
heartbeatRefs.current[exchange] = setInterval(() => {
|
heartbeatRefs.current[exchange] = setInterval(() => {
|
||||||
const ws = wsRefs.current[exchange];
|
const ws = wsRefs.current[exchange];
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||||
// 上交所使用 action: 'ping',深交所使用 type: 'ping'
|
// 上交所和深交所(新 API)都使用 action: 'ping'
|
||||||
const pingMsg = exchange === 'SSE'
|
ws.send(JSON.stringify({ action: 'ping' }));
|
||||||
? { action: 'ping' }
|
|
||||||
: { type: 'ping' };
|
|
||||||
ws.send(JSON.stringify(pingMsg));
|
|
||||||
}
|
}
|
||||||
}, HEARTBEAT_INTERVAL);
|
}, HEARTBEAT_INTERVAL);
|
||||||
}, [stopHeartbeat]);
|
}, [stopHeartbeat]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送深交所订阅请求
|
* 发送深交所订阅请求(新 API 格式)
|
||||||
* 格式:{ type: 'subscribe', securities: ['000001', '000002'] }
|
* 格式:{ action: 'subscribe', channels: ['stock'], codes: ['000001', '000002'] }
|
||||||
*/
|
*/
|
||||||
const sendSZSESubscribe = useCallback((baseCodes: string[]) => {
|
const sendSZSESubscribe = useCallback((baseCodes: string[]) => {
|
||||||
const ws = wsRefs.current.SZSE;
|
const ws = wsRefs.current.SZSE;
|
||||||
if (ws && ws.readyState === WebSocket.OPEN && baseCodes.length > 0) {
|
if (ws && ws.readyState === WebSocket.OPEN && baseCodes.length > 0) {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: 'subscribe',
|
action: 'subscribe',
|
||||||
securities: baseCodes,
|
channels: ['stock'], // 订阅股票频道
|
||||||
|
codes: baseCodes,
|
||||||
}));
|
}));
|
||||||
logger.info('FlexScreen', `SZSE 发送订阅请求`, { securities: baseCodes });
|
logger.info('FlexScreen', `SZSE 发送订阅请求`, { codes: baseCodes });
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -473,7 +608,7 @@ export const useRealtimeQuote = (codes: string[] = []): UseRealtimeQuoteReturn =
|
|||||||
setQuotes(prev => ({ ...prev, ...result }));
|
setQuotes(prev => ({ ...prev, ...result }));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 深交所消息处理
|
// 深交所消息处理(支持新旧两种 API 格式)
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case 'welcome':
|
case 'welcome':
|
||||||
// 收到欢迎消息,深交所 WebSocket 就绪
|
// 收到欢迎消息,深交所 WebSocket 就绪
|
||||||
@@ -494,8 +629,10 @@ export const useRealtimeQuote = (codes: string[] = []): UseRealtimeQuoteReturn =
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'subscribed':
|
case 'subscribed':
|
||||||
// 订阅成功确认
|
// 订阅成功确认(兼容新旧格式)
|
||||||
logger.info('FlexScreen', 'SZSE 订阅成功', {
|
logger.info('FlexScreen', 'SZSE 订阅成功', {
|
||||||
|
channels: anyMsg.channels,
|
||||||
|
codes: anyMsg.codes,
|
||||||
securities: anyMsg.securities,
|
securities: anyMsg.securities,
|
||||||
categories: anyMsg.categories,
|
categories: anyMsg.categories,
|
||||||
});
|
});
|
||||||
@@ -519,7 +656,21 @@ export const useRealtimeQuote = (codes: string[] = []): UseRealtimeQuoteReturn =
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'realtime':
|
case 'realtime':
|
||||||
// 实时行情推送
|
// 旧 API:实时行情推送 (type='realtime' + category)
|
||||||
|
setQuotes(prev => {
|
||||||
|
const result = handleSZSERealtimeMessage(
|
||||||
|
msg as SZSERealtimeMessage,
|
||||||
|
subscribedCodes.current.SZSE,
|
||||||
|
prev
|
||||||
|
);
|
||||||
|
return result || prev;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 新 API:直接使用 type='stock'/'bond'/'fund' 作为消息类型
|
||||||
|
case 'stock':
|
||||||
|
case 'bond':
|
||||||
|
case 'fund':
|
||||||
setQuotes(prev => {
|
setQuotes(prev => {
|
||||||
const result = handleSZSERealtimeMessage(
|
const result = handleSZSERealtimeMessage(
|
||||||
msg as SZSERealtimeMessage,
|
msg as SZSERealtimeMessage,
|
||||||
@@ -700,12 +851,12 @@ export const useRealtimeQuote = (codes: string[] = []): UseRealtimeQuoteReturn =
|
|||||||
|
|
||||||
subscribedCodes.current[exchange].delete(fullCode);
|
subscribedCodes.current[exchange].delete(fullCode);
|
||||||
|
|
||||||
// 发送取消订阅请求(深交所)
|
// 发送取消订阅请求(深交所新 API 格式)
|
||||||
const ws = wsRefs.current[exchange];
|
const ws = wsRefs.current[exchange];
|
||||||
if (exchange === 'SZSE' && ws && ws.readyState === WebSocket.OPEN) {
|
if (exchange === 'SZSE' && ws && ws.readyState === WebSocket.OPEN) {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: 'unsubscribe',
|
action: 'unsubscribe',
|
||||||
securities: [baseCode],
|
codes: [baseCode],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,36 +139,44 @@ export interface SSEMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 深交所 WebSocket 消息类型 ====================
|
// ==================== 深交所 WebSocket 消息类型 ====================
|
||||||
|
// API 文档: SZSE_WEBSOCKET_API.md
|
||||||
|
// 与上交所 API 保持一致的设计
|
||||||
|
|
||||||
/** 深交所数据类别 */
|
/** 深交所数据类别(对应 channels) */
|
||||||
export type SZSECategory =
|
export type SZSECategory = 'stock' | 'bond' | 'fund';
|
||||||
| 'stock' // 300111 股票快照
|
|
||||||
| 'bond' // 300211 债券快照
|
|
||||||
| 'afterhours_block' // 300611 盘后定价大宗交易
|
|
||||||
| 'afterhours_trading' // 303711 盘后定价交易
|
|
||||||
| 'hk_stock' // 306311 港股快照
|
|
||||||
| 'index' // 309011 指数快照
|
|
||||||
| 'volume_stats' // 309111 成交量统计
|
|
||||||
| 'fund_nav'; // 309211 基金净值
|
|
||||||
|
|
||||||
/** 深交所股票行情数据 */
|
/** 深交所股票行情数据(新 API 格式) */
|
||||||
export interface SZSEStockData {
|
export interface SZSEStockData {
|
||||||
security_id: string;
|
security_id: string;
|
||||||
|
md_stream_id?: string; // MDStreamID: 010
|
||||||
orig_time?: number;
|
orig_time?: number;
|
||||||
channel_no?: number;
|
channel_no?: number;
|
||||||
trading_phase?: string;
|
trading_phase_code?: string; // 新字段名
|
||||||
last_px: number;
|
trading_phase?: string; // 兼容旧字段名
|
||||||
|
prev_close_px: number; // 新字段名
|
||||||
|
prev_close?: number; // 兼容旧字段名
|
||||||
open_px: number;
|
open_px: number;
|
||||||
high_px: number;
|
high_px: number;
|
||||||
low_px: number;
|
low_px: number;
|
||||||
prev_close: number;
|
last_px: number;
|
||||||
volume: number;
|
upper_limit_px?: number; // 新字段名
|
||||||
amount: number;
|
upper_limit?: number; // 兼容旧字段名
|
||||||
|
lower_limit_px?: number; // 新字段名
|
||||||
|
lower_limit?: number; // 兼容旧字段名
|
||||||
num_trades?: number;
|
num_trades?: number;
|
||||||
upper_limit?: number;
|
total_volume_trade?: number; // 新字段名 (成交量)
|
||||||
lower_limit?: number;
|
total_value_trade?: number; // 新字段名 (成交额)
|
||||||
|
volume?: number; // 兼容旧字段名
|
||||||
|
amount?: number; // 兼容旧字段名
|
||||||
|
// 新 API 格式:直接是数组
|
||||||
|
bid_prices?: number[];
|
||||||
|
bid_volumes?: number[];
|
||||||
|
ask_prices?: number[];
|
||||||
|
ask_volumes?: number[];
|
||||||
|
// 兼容旧格式
|
||||||
bids?: OrderBookLevel[];
|
bids?: OrderBookLevel[];
|
||||||
asks?: OrderBookLevel[];
|
asks?: OrderBookLevel[];
|
||||||
|
update_time?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 深交所指数行情数据 */
|
/** 深交所指数行情数据 */
|
||||||
@@ -245,10 +253,10 @@ export interface SZSEAfterhoursData {
|
|||||||
num_trades?: number;
|
num_trades?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 深交所实时消息 */
|
/** 深交所实时消息(新 API 格式:type 直接是 'stock' | 'bond' | 'fund') */
|
||||||
export interface SZSERealtimeMessage {
|
export interface SZSERealtimeMessage {
|
||||||
type: 'realtime';
|
type: 'stock' | 'bond' | 'fund' | 'realtime'; // 新 API 直接用 type='stock' 等
|
||||||
category: SZSECategory;
|
category?: SZSECategory; // 旧 API 使用 category
|
||||||
msg_type?: number;
|
msg_type?: number;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
data: SZSEStockData | SZSEIndexData | SZSEBondData | SZSEHKStockData | SZSEAfterhoursData;
|
data: SZSEStockData | SZSEIndexData | SZSEBondData | SZSEHKStockData | SZSEAfterhoursData;
|
||||||
@@ -257,8 +265,9 @@ export interface SZSERealtimeMessage {
|
|||||||
/** 深交所快照消息 */
|
/** 深交所快照消息 */
|
||||||
export interface SZSESnapshotMessage {
|
export interface SZSESnapshotMessage {
|
||||||
type: 'snapshot';
|
type: 'snapshot';
|
||||||
timestamp: string;
|
timestamp?: string;
|
||||||
data: {
|
data: SZSEStockData | {
|
||||||
|
// 兼容旧格式的批量快照
|
||||||
stocks?: SZSEStockData[];
|
stocks?: SZSEStockData[];
|
||||||
indexes?: SZSEIndexData[];
|
indexes?: SZSEIndexData[];
|
||||||
bonds?: SZSEBondData[];
|
bonds?: SZSEBondData[];
|
||||||
@@ -277,8 +286,10 @@ export interface SZSEWelcomeMessage {
|
|||||||
/** 深交所订阅确认消息 */
|
/** 深交所订阅确认消息 */
|
||||||
export interface SZSESubscribedMessage {
|
export interface SZSESubscribedMessage {
|
||||||
type: 'subscribed';
|
type: 'subscribed';
|
||||||
securities?: string[];
|
channels?: string[]; // 新 API 格式
|
||||||
categories?: string[];
|
codes?: string[]; // 新 API 格式
|
||||||
|
securities?: string[]; // 兼容旧格式
|
||||||
|
categories?: string[]; // 兼容旧格式
|
||||||
all?: boolean;
|
all?: boolean;
|
||||||
incremental_only?: boolean;
|
incremental_only?: boolean;
|
||||||
message?: string;
|
message?: string;
|
||||||
@@ -287,8 +298,10 @@ export interface SZSESubscribedMessage {
|
|||||||
/** 深交所取消订阅确认消息 */
|
/** 深交所取消订阅确认消息 */
|
||||||
export interface SZSEUnsubscribedMessage {
|
export interface SZSEUnsubscribedMessage {
|
||||||
type: 'unsubscribed';
|
type: 'unsubscribed';
|
||||||
securities?: string[];
|
channels?: string[]; // 新 API 格式
|
||||||
categories?: string[];
|
codes?: string[]; // 新 API 格式
|
||||||
|
securities?: string[]; // 兼容旧格式
|
||||||
|
categories?: string[]; // 兼容旧格式
|
||||||
remaining_securities?: string[];
|
remaining_securities?: string[];
|
||||||
remaining_categories?: string[];
|
remaining_categories?: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user