Paper_Trading / frontend /src /lib /resample.ts
superxuu
feat: Complete stock trading simulation system
2d4ab0a
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 };
}