feat(mock): 添加超预期得分 expectation_score 字段
- events.js: 生成随机超预期得分 30-90 - event.js: handler 同步添加字段 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -912,6 +912,7 @@ export function generateMockEvents(params = {}) {
|
|||||||
related_avg_chg: parseFloat(relatedAvgChg),
|
related_avg_chg: parseFloat(relatedAvgChg),
|
||||||
related_max_chg: parseFloat(relatedMaxChg),
|
related_max_chg: parseFloat(relatedMaxChg),
|
||||||
related_week_chg: parseFloat(relatedWeekChg),
|
related_week_chg: parseFloat(relatedWeekChg),
|
||||||
|
expectation_score: Math.floor(Math.random() * 60) + 30, // 30-90 超预期得分
|
||||||
keywords: generateKeywords(industry, i),
|
keywords: generateKeywords(industry, i),
|
||||||
is_ai_generated: i % 4 === 0, // 25% 的事件是AI生成
|
is_ai_generated: i % 4 === 0, // 25% 的事件是AI生成
|
||||||
industry: industry,
|
industry: industry,
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user