Files
vf_react/argon-pro-react-native/src/services/ztService.js
2026-01-13 15:10:13 +08:00

305 lines
8.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 涨停数据服务
* 获取涨停板块、个股、统计等数据
*/
import { apiRequest, API_BASE } from './api';
export const ztService = {
/**
* 获取指定日期的涨停数据
* @param {string} dateStr - 日期 YYYYMMDD 格式
* @returns {Promise<object>} 涨停数据
*/
getDailyZt: async (dateStr) => {
try {
// 尝试从静态数据获取
const response = await fetch(`${API_BASE}/data/zt/daily/${dateStr}.json`);
if (response.ok) {
const data = await response.json();
return { success: true, data };
}
return { success: false, message: '数据不存在' };
} catch (error) {
console.error('获取涨停数据失败:', error);
return { success: false, message: error.message };
}
},
/**
* 获取最新的涨停数据(今日或最近交易日)
* @returns {Promise<object>} 最新涨停数据
*/
getLatestZt: async () => {
try {
const response = await fetch(`${API_BASE}/data/zt/latest.json`);
if (response.ok) {
const data = await response.json();
return { success: true, data };
}
// 如果没有 latest.json尝试获取今天的数据
const today = new Date();
const dateStr = today.toISOString().slice(0, 10).replace(/-/g, '');
return ztService.getDailyZt(dateStr);
} catch (error) {
console.error('获取最新涨停数据失败:', error);
return { success: false, message: error.message };
}
},
/**
* 获取可用的涨停数据日期列表
* @returns {Promise<object>} 日期列表
*/
getAvailableDates: async () => {
try {
const response = await fetch(`${API_BASE}/data/zt/dates.json`);
if (response.ok) {
const data = await response.json();
return { success: true, data };
}
return { success: false, data: [] };
} catch (error) {
return { success: false, data: [] };
}
},
/**
* 获取板块详情(包含该板块的所有涨停股票)
* @param {string} dateStr - 日期
* @param {string} sectorName - 板块名称
* @returns {Promise<object>} 板块详情
*/
getSectorDetail: async (dateStr, sectorName) => {
const result = await ztService.getDailyZt(dateStr);
if (!result.success) return result;
const sectorData = result.data.sector_data?.[sectorName];
if (!sectorData) {
return { success: false, message: '板块不存在' };
}
// 获取该板块的股票详情
const stocks = result.data.stocks?.filter(s =>
sectorData.stock_codes?.includes(s.scode)
) || [];
return {
success: true,
data: {
name: sectorName,
count: sectorData.count,
stocks,
related_events: sectorData.related_events || [],
}
};
},
/**
* 获取连板统计
* @param {object} ztData - 涨停原始数据
* @returns {object} 统计结果
*/
calculateStats: (ztData) => {
if (!ztData) return null;
const stocks = ztData.stocks || ztData.stock_infos || [];
// 连板统计
const continuousStats = {};
// 时间统计
const timeStats = { '秒板': 0, '早盘': 0, '盘中': 0, '尾盘': 0 };
// 公告驱动统计
let announcementCount = 0;
stocks.forEach(stock => {
// 连板统计
const continuous = stock.continuous_days || '首板';
continuousStats[continuous] = (continuousStats[continuous] || 0) + 1;
// 时间统计
const time = stock.formatted_time || stock.zt_time;
if (time) {
const hour = parseInt(time.split(':')[0]);
const minute = parseInt(time.split(':')[1]);
const totalMinutes = hour * 60 + minute;
if (totalMinutes <= 9 * 60 + 31) {
timeStats['秒板']++;
} else if (totalMinutes <= 10 * 60 + 30) {
timeStats['早盘']++;
} else if (totalMinutes <= 14 * 60) {
timeStats['盘中']++;
} else {
timeStats['尾盘']++;
}
}
// 公告驱动
if (stock.is_announcement) {
announcementCount++;
}
});
return {
total: ztData.total_stocks || stocks.length,
continuousStats,
timeStats,
announcementCount,
announcementRatio: stocks.length > 0
? ((announcementCount / stocks.length) * 100).toFixed(1)
: 0,
};
},
/**
* 获取热门板块排序
* @param {object} ztData - 涨停原始数据
* @param {number} limit - 返回数量
* @returns {Array} 排序后的板块列表
*/
getHotSectors: (ztData, limit = 10) => {
if (!ztData?.sector_data) return [];
// 需要过滤掉的板块名称
const excludedSectors = ['其他', '公告'];
const sectors = Object.entries(ztData.sector_data)
.filter(([name]) => !excludedSectors.includes(name)) // 过滤掉"其他"和"公告"
.map(([name, data]) => ({
name,
count: data.count || 0,
stock_codes: data.stock_codes || [],
related_events: data.related_events || [],
// 计算热度分数:涨停数 * 权重 + 关联事件数 * 权重
hotScore: (data.count || 0) * 10 + (data.related_events?.length || 0) * 5,
}))
.sort((a, b) => b.hotScore - a.hotScore)
.slice(0, limit);
return sectors;
},
/**
* 获取连板龙头股
* @param {object} ztData - 涨停原始数据
* @param {number} minDays - 最小连板天数
* @returns {Array} 连板股列表
*/
getContinuousLeaders: (ztData, minDays = 2) => {
const stocks = ztData?.stocks || ztData?.stock_infos || [];
// 解析连板天数
const parseDay = (str) => {
if (!str || str === '首板') return 1;
const match = str.match(/(\d+)/);
return match ? parseInt(match[1]) : 1;
};
return stocks
.filter(s => parseDay(s.continuous_days) >= minDays)
.sort((a, b) => parseDay(b.continuous_days) - parseDay(a.continuous_days));
},
/**
* 获取热门关键词
* @param {object} ztData - 涨停原始数据
* @param {number} limit - 返回数量
* @returns {Array} 关键词列表
*/
getHotKeywords: (ztData, limit = 12) => {
return (ztData?.word_freq_data || []).slice(0, limit);
},
/**
* 获取日历综合数据(涨停数 + 事件数 + 涨跌幅 + 热门概念)
* @param {number} year - 年份
* @param {number} month - 月份 (1-12)
* @returns {Promise<object>} 日历数据
*/
getCalendarData: async (year, month) => {
try {
// 获取日期列表
const datesResult = await ztService.getAvailableDates();
if (!datesResult.success) {
return { success: false, data: [] };
}
const dates = datesResult.data.dates || [];
const calendarData = [];
// 过滤当月数据
const monthStr = String(month).padStart(2, '0');
const monthPrefix = `${year}${monthStr}`;
for (const dateInfo of dates) {
const dateStr = dateInfo.date;
if (!dateStr.startsWith(monthPrefix)) continue;
// 获取每日详细数据
const dailyResult = await ztService.getDailyZt(dateStr);
let topSector = '';
let indexChange = null;
if (dailyResult.success && dailyResult.data) {
// 获取最热板块
const hotSectors = ztService.getHotSectors(dailyResult.data, 1);
topSector = hotSectors[0]?.name || '';
// 获取指数涨跌幅(如果数据中有)
indexChange = dailyResult.data.index_change || null;
}
calendarData.push({
date: dateStr,
ztCount: dateInfo.count || 0,
topSector,
eventCount: dateInfo.event_count || 0,
indexChange,
});
}
return { success: true, data: calendarData };
} catch (error) {
console.error('获取日历数据失败:', error);
return { success: false, data: [] };
}
},
/**
* 快速获取日历数据(只从 dates.json 获取基础信息)
* @param {number} year - 年份
* @param {number} month - 月份 (1-12)
* @returns {Promise<object>} 日历数据
*/
getCalendarDataFast: async (year, month) => {
try {
const datesResult = await ztService.getAvailableDates();
if (!datesResult.success) {
return { success: false, data: [] };
}
const dates = datesResult.data.dates || [];
const monthStr = String(month).padStart(2, '0');
const monthPrefix = `${year}${monthStr}`;
const calendarData = dates
.filter(d => d.date.startsWith(monthPrefix))
.map(d => ({
date: d.date,
ztCount: d.count || 0,
formattedDate: d.formatted_date,
topSector: d.top_sector || '',
eventCount: d.event_count || 0,
indexChange: d.index_change || null,
}));
return { success: true, data: calendarData };
} catch (error) {
console.error('获取日历数据失败:', error);
return { success: false, data: [] };
}
},
};
export default ztService;