diff --git a/src/mocks/data/events.js b/src/mocks/data/events.js index a5516b11..6ad46e77 100644 --- a/src/mocks/data/events.js +++ b/src/mocks/data/events.js @@ -912,6 +912,7 @@ export function generateMockEvents(params = {}) { related_avg_chg: parseFloat(relatedAvgChg), related_max_chg: parseFloat(relatedMaxChg), related_week_chg: parseFloat(relatedWeekChg), + expectation_score: Math.floor(Math.random() * 60) + 30, // 30-90 超预期得分 keywords: generateKeywords(industry, i), is_ai_generated: i % 4 === 0, // 25% 的事件是AI生成 industry: industry, diff --git a/src/mocks/handlers/event.js b/src/mocks/handlers/event.js index 78f64f40..5b788842 100644 --- a/src/mocks/handlers/event.js +++ b/src/mocks/handlers/event.js @@ -1894,4 +1894,178 @@ export const eventHandlers = [ ); } }), + + // ==================== 事件有效性统计 ==================== + + // 获取事件有效性统计数据 + http.get('/api/v1/events/effectiveness-stats', async ({ request }) => { + await delay(300); + + const url = new URL(request.url); + const days = parseInt(url.searchParams.get('days') || '30'); + + console.log('[Mock] 获取事件有效性统计:', { days }); + + try { + // 模拟统计数据 + const summary = { + totalEvents: Math.floor(Math.random() * 200) + 100, // 总事件数 + effectiveEvents: Math.floor(Math.random() * 150) + 50, // 有效事件数 + effectiveRate: parseFloat((Math.random() * 30 + 60).toFixed(1)), // 有效率 60%-90% + avgReturn: parseFloat((Math.random() * 5 + 2).toFixed(2)), // 平均收益 2%-7% + maxReturn: parseFloat((Math.random() * 15 + 5).toFixed(2)), // 最大收益 5%-20% + hitRate: parseFloat((Math.random() * 20 + 70).toFixed(1)), // 命中率 70%-90% + avgHoldDays: Math.floor(Math.random() * 5) + 3, // 平均持有天数 3-8 + winLossRatio: parseFloat((Math.random() * 2 + 1.5).toFixed(2)), // 盈亏比 1.5-3.5 + }; + + // 模拟表现最佳的事件 + const topPerformers = []; + const eventTitles = [ + '重大政策利好:半导体产业扶持政策出台', + '人工智能板块迎来突破性进展', + '新能源汽车销量创历史新高', + '消费复苏数据超预期', + '央行降准释放流动性', + '科技股龙头财报超预期', + '军工板块获重大订单', + '医药创新药获批上市', + ]; + + for (let i = 0; i < 5; i++) { + topPerformers.push({ + id: i + 1, + title: eventTitles[i % eventTitles.length], + importance: ['S', 'A', 'B'][Math.floor(Math.random() * 3)], + created_at: new Date(Date.now() - Math.random() * days * 24 * 60 * 60 * 1000).toISOString(), + avg_chg: parseFloat((Math.random() * 8 + 2).toFixed(2)), + max_chg: parseFloat((Math.random() * 15 + 5).toFixed(2)), + stock_count: Math.floor(Math.random() * 20) + 5, + }); + } + + // 按 max_chg 降序排列 + topPerformers.sort((a, b) => b.max_chg - a.max_chg); + + return HttpResponse.json({ + code: 200, + data: { + summary, + topPerformers, + }, + message: '获取成功' + }); + } catch (error) { + console.error('[Mock] 获取事件有效性统计失败:', error); + return HttpResponse.json( + { + code: 500, + error: '获取事件有效性统计失败', + data: null + }, + { status: 500 } + ); + } + }), + + // ==================== 涨停题材散点图数据 ==================== + + // 获取涨停题材散点图数据 + http.get('/api/v1/zt/theme-scatter', async ({ request }) => { + await delay(400); + + const url = new URL(request.url); + const days = parseInt(url.searchParams.get('days') || '5'); + const date = url.searchParams.get('date'); + + console.log('[Mock] 获取涨停题材散点图:', { days, date }); + + try { + // 生成可用日期列表(最近5个交易日) + const availableDates = []; + const today = new Date(); + let tradingDays = 0; + let checkDate = new Date(today); + + while (tradingDays < days) { + const dayOfWeek = checkDate.getDay(); + if (dayOfWeek !== 0 && dayOfWeek !== 6) { + const dateStr = checkDate.toISOString().split('T')[0]; + availableDates.push({ + date: dateStr, + formatted: `${checkDate.getMonth() + 1}月${checkDate.getDate()}日`, + }); + tradingDays++; + } + checkDate.setDate(checkDate.getDate() - 1); + } + + // 使用日期作为随机种子 + const targetDate = date || availableDates[0]?.date; + const dateSeed = targetDate ? parseInt(targetDate.replace(/-/g, '')) : Date.now(); + const seededRandom = (offset = 0) => { + const x = Math.sin(dateSeed + offset) * 10000; + return x - Math.floor(x); + }; + + // 题材数据模板 + const themeTemplates = [ + { label: '人工智能', baseX: 5, baseY: 15, status: 'rising' }, + { label: '机器人', baseX: 4, baseY: 12, status: 'rising' }, + { label: '半导体', baseX: 3, baseY: 10, status: 'clustering' }, + { label: '光模块', baseX: 6, baseY: 8, status: 'rising' }, + { label: '算力', baseX: 4, baseY: 9, status: 'clustering' }, + { label: '新能源汽车', baseX: 2, baseY: 7, status: 'lurking' }, + { label: '固态电池', baseX: 3, baseY: 6, status: 'lurking' }, + { label: '光伏', baseX: 2, baseY: 5, status: 'declining' }, + { label: '储能', baseX: 2, baseY: 4, status: 'declining' }, + { label: '医药', baseX: 1, baseY: 3, status: 'lurking' }, + ]; + + // 状态颜色映射 + const statusColors = { + rising: '#FF4D4F', + declining: '#52C41A', + lurking: '#1890FF', + clustering: '#722ED1', + }; + + // 生成题材数据 + const themes = themeTemplates.map((template, index) => { + const xVariation = seededRandom(index * 10) * 2 - 1; // -1 ~ 1 + const yVariation = seededRandom(index * 10 + 1) * 4 - 2; // -2 ~ 2 + + return { + label: template.label, + x: Math.max(1, Math.round(template.baseX + xVariation)), // 辨识度(最高板高度) + y: Math.max(1, Math.round(template.baseY + yVariation)), // 热度(涨停家数) + status: template.status, + color: statusColors[template.status], + countTrend: parseFloat((seededRandom(index * 10 + 2) * 40 - 20).toFixed(1)), // -20% ~ 20% + boardTrend: parseFloat((seededRandom(index * 10 + 3) * 2 - 1).toFixed(1)), // -1 ~ 1 + history: [], // 历史数据(简化版不填充) + }; + }); + + return HttpResponse.json({ + success: true, + data: { + themes, + availableDates, + currentDate: targetDate, + }, + message: '获取成功' + }); + } catch (error) { + console.error('[Mock] 获取涨停题材散点图失败:', error); + return HttpResponse.json( + { + success: false, + error: '获取涨停题材散点图失败', + data: null + }, + { status: 500 } + ); + } + }), ];