Spaces:
Running
Running
| 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 }; | |
| } | |