|
|
@@ -1,26 +1,84 @@
|
|
|
-import { useState, useCallback } from 'react';
|
|
|
+import { useState, useCallback, useEffect } from 'react';
|
|
|
import type { StockData } from '../types/index';
|
|
|
|
|
|
-export function useStockDataFilter(fullData: StockData[]) {
|
|
|
+export function useStockDataFilter(
|
|
|
+ fullData: StockData[],
|
|
|
+ fixedDateRange?: { startDate?: string; endDate?: string }
|
|
|
+) {
|
|
|
const [selectedDays, setSelectedDays] = useState(120); // 默认120天
|
|
|
const [trainingProgress, setTrainingProgress] = useState(0); // 训练日进度
|
|
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
|
+ const [isFixedRangeMode, setIsFixedRangeMode] = useState(false);
|
|
|
+ const [fixedRangeIndices, setFixedRangeIndices] = useState<{ startIndex: number; endIndex: number } | null>(null);
|
|
|
+
|
|
|
+ // 检测固定日期范围模式并计算索引
|
|
|
+ useEffect(() => {
|
|
|
+ console.log('fixedDateRange',fixedDateRange)
|
|
|
+ if (fixedDateRange?.startDate && fixedDateRange?.endDate && fullData.length > 0) {
|
|
|
+ setIsFixedRangeMode(true);
|
|
|
+
|
|
|
+ const startIndex = fullData.findIndex(item => item.d === fixedDateRange.startDate);
|
|
|
+ const endIndex = fullData.findIndex(item => item.d === fixedDateRange.endDate);
|
|
|
+ console.log('startIndex',startIndex)
|
|
|
+ console.log('endIndex',endIndex)
|
|
|
+ console.log('fullData',fullData)
|
|
|
+
|
|
|
+ if (startIndex !== -1 && endIndex !== -1 && startIndex <= endIndex) {
|
|
|
+ const calculatedDays = endIndex - startIndex + 1;
|
|
|
+ setFixedRangeIndices({ startIndex, endIndex });
|
|
|
+ setSelectedDays(calculatedDays);
|
|
|
+ // 保持 trainingProgress 为0,让用户从头开始训练
|
|
|
+ } else {
|
|
|
+ // 日期不在数据范围内,回退到普通模式
|
|
|
+ setIsFixedRangeMode(false);
|
|
|
+ setFixedRangeIndices(null);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ setIsFixedRangeMode(false);
|
|
|
+ setFixedRangeIndices(null);
|
|
|
+ }
|
|
|
+ }, [fixedDateRange?.startDate, fixedDateRange?.endDate, fullData.length]);
|
|
|
|
|
|
const filterData = useCallback(() => {
|
|
|
if (!isInitialized || fullData.length === 0) {
|
|
|
return []; // 未初始化或数据为空时返回空数组
|
|
|
}
|
|
|
|
|
|
+ // 固定日期范围模式
|
|
|
+ if (isFixedRangeMode && fixedRangeIndices) {
|
|
|
+ const { startIndex, endIndex } = fixedRangeIndices;
|
|
|
+ const totalDays = endIndex - startIndex + 1;
|
|
|
+
|
|
|
+ // 计算训练结束索引
|
|
|
+ const trainingEndIndex = Math.min(
|
|
|
+ startIndex + trainingProgress,
|
|
|
+ endIndex + 1 // +1 因为 slice 是 [start, end)
|
|
|
+ );
|
|
|
+
|
|
|
+ // 计算历史数据部分:从训练开始往前推至少365天或可用数据
|
|
|
+ const historyEndIndex = startIndex;
|
|
|
+ const historyStartIndex = Math.max(0, historyEndIndex - Math.max(totalDays, 365));
|
|
|
+
|
|
|
+ if (historyStartIndex >= historyEndIndex) {
|
|
|
+ // 没有历史数据,只返回训练数据
|
|
|
+ return fullData.slice(startIndex, trainingEndIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合并历史数据和训练数据
|
|
|
+ const historyData = fullData.slice(historyStartIndex, historyEndIndex);
|
|
|
+ const trainingData = fullData.slice(startIndex, trainingEndIndex);
|
|
|
+
|
|
|
+ return [...historyData, ...trainingData];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 原有天数选择模式(向后兼容)
|
|
|
const totalDays = fullData.length;
|
|
|
-
|
|
|
- // 计算训练日部分:从当前日期往前推选择的天数到当前日期
|
|
|
const trainingStartIndex = totalDays - selectedDays;
|
|
|
const trainingEndIndex = Math.min(
|
|
|
trainingStartIndex + trainingProgress,
|
|
|
totalDays
|
|
|
);
|
|
|
|
|
|
- // 计算历史天数部分:从训练日开始日期往前推至少365天
|
|
|
const historyEndIndex = trainingStartIndex;
|
|
|
const historyStartIndex = Math.max(0, historyEndIndex - Math.max(selectedDays, 365));
|
|
|
|
|
|
@@ -34,12 +92,30 @@ export function useStockDataFilter(fullData: StockData[]) {
|
|
|
const trainingData = fullData.slice(trainingStartIndex, trainingEndIndex);
|
|
|
|
|
|
return [...historyData, ...trainingData];
|
|
|
- }, [fullData, selectedDays, trainingProgress, isInitialized]);
|
|
|
+ }, [fullData, selectedDays, trainingProgress, isInitialized, isFixedRangeMode, fixedRangeIndices]);
|
|
|
|
|
|
const moveToNextDay = useCallback(() => {
|
|
|
- return new Promise<string>((resolve) => {
|
|
|
+ return new Promise<string>((resolve, reject) => {
|
|
|
setTrainingProgress((prev) => {
|
|
|
const newProgress = prev + 1;
|
|
|
+
|
|
|
+ // 固定日期范围模式
|
|
|
+ if (isFixedRangeMode && fixedRangeIndices) {
|
|
|
+ const { startIndex, endIndex } = fixedRangeIndices;
|
|
|
+ const totalTrainingDays = endIndex - startIndex + 1;
|
|
|
+
|
|
|
+ if (newProgress > totalTrainingDays) {
|
|
|
+ reject(new Error('已到达训练结束日期'));
|
|
|
+ return prev; // 不更新进度
|
|
|
+ }
|
|
|
+
|
|
|
+ const nextDateIndex = startIndex + newProgress - 1;
|
|
|
+ const nextDate = fullData[nextDateIndex]?.d || '';
|
|
|
+ resolve(nextDate);
|
|
|
+ return newProgress;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 原有天数选择模式
|
|
|
const totalDays = fullData.length;
|
|
|
const trainingStartIndex = totalDays - selectedDays;
|
|
|
const trainingEndIndex = Math.min(
|
|
|
@@ -47,22 +123,24 @@ export function useStockDataFilter(fullData: StockData[]) {
|
|
|
totalDays
|
|
|
);
|
|
|
|
|
|
- // 返回最新日期
|
|
|
const nextDate = fullData[trainingEndIndex - 1]?.d || '';
|
|
|
resolve(nextDate);
|
|
|
return newProgress;
|
|
|
});
|
|
|
});
|
|
|
- }, [fullData, selectedDays]);
|
|
|
+ }, [fullData, selectedDays, isFixedRangeMode, fixedRangeIndices]);
|
|
|
|
|
|
const resetTrainingProgress = useCallback(() => {
|
|
|
setTrainingProgress(0);
|
|
|
}, []);
|
|
|
|
|
|
const setDayNumWithTraining = useCallback((days: number) => {
|
|
|
- setSelectedDays(days);
|
|
|
- setTrainingProgress(0); // 重置训练进度
|
|
|
- }, []);
|
|
|
+ // 固定模式下禁用天数修改
|
|
|
+ if (!isFixedRangeMode) {
|
|
|
+ setSelectedDays(days);
|
|
|
+ setTrainingProgress(0); // 重置训练进度
|
|
|
+ }
|
|
|
+ }, [isFixedRangeMode]);
|
|
|
|
|
|
const initializeView = useCallback(() => {
|
|
|
setIsInitialized(true);
|
|
|
@@ -70,6 +148,21 @@ export function useStockDataFilter(fullData: StockData[]) {
|
|
|
|
|
|
// 获取当前训练进度信息
|
|
|
const getTrainingInfo = useCallback(() => {
|
|
|
+ // 固定日期范围模式
|
|
|
+ if (isFixedRangeMode && fixedRangeIndices) {
|
|
|
+ const { startIndex, endIndex } = fixedRangeIndices;
|
|
|
+ const totalTrainingDays = endIndex - startIndex + 1;
|
|
|
+
|
|
|
+ return {
|
|
|
+ currentProgress: trainingProgress,
|
|
|
+ totalTrainingDays,
|
|
|
+ remainingDays: totalTrainingDays - trainingProgress,
|
|
|
+ isTrainingComplete: trainingProgress >= totalTrainingDays,
|
|
|
+ isFixedRangeMode: true
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 原有天数选择模式
|
|
|
const totalDays = fullData.length;
|
|
|
const trainingStartIndex = totalDays - selectedDays;
|
|
|
const totalTrainingDays = Math.min(selectedDays, totalDays - trainingStartIndex);
|
|
|
@@ -78,9 +171,10 @@ export function useStockDataFilter(fullData: StockData[]) {
|
|
|
currentProgress: trainingProgress,
|
|
|
totalTrainingDays,
|
|
|
remainingDays: totalTrainingDays - trainingProgress,
|
|
|
- isTrainingComplete: trainingProgress >= totalTrainingDays
|
|
|
+ isTrainingComplete: trainingProgress >= totalTrainingDays,
|
|
|
+ isFixedRangeMode: false
|
|
|
};
|
|
|
- }, [fullData, selectedDays, trainingProgress]);
|
|
|
+ }, [fullData, selectedDays, trainingProgress, isFixedRangeMode, fixedRangeIndices]);
|
|
|
|
|
|
return {
|
|
|
filteredData: filterData(),
|
|
|
@@ -89,6 +183,7 @@ export function useStockDataFilter(fullData: StockData[]) {
|
|
|
setDayNum: setDayNumWithTraining,
|
|
|
initializeView,
|
|
|
isInitialized,
|
|
|
- trainingInfo: getTrainingInfo()
|
|
|
+ trainingInfo: getTrainingInfo(),
|
|
|
+ isFixedRangeMode
|
|
|
};
|
|
|
}
|