import { KLine } from './api'; export type Period = 'day' | 'week' | 'month'; /** * 获取日期所属的周期标识 */ function getPeriodKey(dateStr: string, period: Period): string { const date = new Date(dateStr); if (period === 'week') { // 获取周一的日期 const day = date.getDay() || 7; // 1 (Mon) - 7 (Sun) const weekStart = new Date(date); weekStart.setDate(date.getDate() - day + 1); return weekStart.toISOString().split('T')[0]; // e.g., "2023-01-02" } else if (period === 'month') { // 获取月初日期 return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; // e.g., "2023-01" } return dateStr; } /** * 重采样 K 线数据 * @param dailyKlines 日线数据源 * @param period 目标周期 * @returns { klines: 聚合后的K线数组, indexMap: 日线索引到聚合索引的映射数组 } */ export function resample(dailyKlines: KLine[], period: Period) { if (period === 'day') { // 如果是日线,直接返回,映射为 1:1 return { klines: dailyKlines, indexMap: dailyKlines.map((_, i) => i) }; } const resampledKlines: KLine[] = []; const indexMap: number[] = []; // indexMap[dailyIndex] = resampledIndex let currentCandle: KLine | null = null; let currentPeriodKey = ''; dailyKlines.forEach((k, i) => { const key = getPeriodKey(k.date, period); if (key !== currentPeriodKey) { // 新周期开始 if (currentCandle) { resampledKlines.push(currentCandle); } currentPeriodKey = key; currentCandle = { ...k }; // 复制一份作为新周期的起始 } else { // 聚合到当前周期 if (currentCandle) { currentCandle.high = Math.max(currentCandle.high, k.high); currentCandle.low = Math.min(currentCandle.low, k.low); currentCandle.close = k.close; currentCandle.volume += k.volume; currentCandle.date = k.date; // 使用最新的日期作为显示日期 // amount 和 pct_chg 聚合比较复杂,暂时简化处理或忽略 if (k.amount && currentCandle.amount) currentCandle.amount += k.amount; } } // 记录映射关系:当前日线 i 对应 resampledKlines 的最后一个元素(即当前正在构建的 candle) // 注意:如果是新周期的第一天,currentCandle 还没 push 进去,所以索引是 length // 如果是周期的后续天,currentCandle 也没 push,索引还是 length // 等等,上面的逻辑是“发现新周期时 push 上一个”。 // 所以当前正在构建的 candle 还没有在 resampledKlines 里。 // 它的索引将是 resampledKlines.length indexMap.push(resampledKlines.length); }); // Push 最后一个 candle if (currentCandle) { resampledKlines.push(currentCandle); } return { klines: resampledKlines, indexMap }; }