initial charts / backtest
This commit is contained in:
parent
11c24b2280
commit
1b9010ebf4
37 changed files with 3888 additions and 23 deletions
145
apps/stock/web-app/src/features/charts/hooks/useChartData.ts
Normal file
145
apps/stock/web-app/src/features/charts/hooks/useChartData.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import { useState, useEffect, useRef } from 'react';
|
||||
import { ChartDataService } from '../services/chartDataService';
|
||||
import type { CandlestickData, MarketData, ChartInterval } from '../types';
|
||||
|
||||
interface UseChartDataOptions {
|
||||
symbol: string;
|
||||
interval: ChartInterval;
|
||||
enableRealtime?: boolean;
|
||||
}
|
||||
|
||||
export function useChartData({ symbol, interval, enableRealtime = true }: UseChartDataOptions) {
|
||||
const [data, setData] = useState<CandlestickData[]>([]);
|
||||
const [marketData, setMarketData] = useState<MarketData | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const unsubscribeRef = useRef<(() => void) | null>(null);
|
||||
|
||||
// Fetch historical data
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const { start, end } = ChartDataService.getDefaultDateRange(interval);
|
||||
const historicalData = await ChartDataService.getHistoricalData(
|
||||
symbol,
|
||||
interval,
|
||||
start,
|
||||
end
|
||||
);
|
||||
|
||||
if (!cancelled) {
|
||||
setData(historicalData);
|
||||
}
|
||||
|
||||
// Fetch current market data
|
||||
const currentData = await ChartDataService.getRealtimeData(symbol);
|
||||
if (!cancelled) {
|
||||
setMarketData(currentData);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load chart data');
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [symbol, interval]);
|
||||
|
||||
// Subscribe to real-time updates
|
||||
useEffect(() => {
|
||||
if (!enableRealtime || isLoading || error) return;
|
||||
|
||||
const handleRealtimeUpdate = (newCandle: CandlestickData) => {
|
||||
setData(prevData => {
|
||||
const lastCandle = prevData[prevData.length - 1];
|
||||
|
||||
// Check if this is an update to the current candle or a new one
|
||||
if (lastCandle && lastCandle.time === newCandle.time) {
|
||||
// Update existing candle
|
||||
return [...prevData.slice(0, -1), newCandle];
|
||||
} else {
|
||||
// Add new candle
|
||||
return [...prevData, newCandle];
|
||||
}
|
||||
});
|
||||
|
||||
// Update market data
|
||||
setMarketData(prev => {
|
||||
if (!prev) return prev;
|
||||
|
||||
const change = newCandle.close - (prev.previousClose || prev.price);
|
||||
const changePercent = (change / (prev.previousClose || prev.price)) * 100;
|
||||
|
||||
return {
|
||||
...prev,
|
||||
price: newCandle.close,
|
||||
change,
|
||||
changePercent,
|
||||
high: Math.max(prev.high, newCandle.high),
|
||||
low: Math.min(prev.low, newCandle.low),
|
||||
volume: prev.volume + (newCandle.volume || 0),
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const handleError = (error: Error) => {
|
||||
console.error('Real-time data error:', error);
|
||||
};
|
||||
|
||||
unsubscribeRef.current = ChartDataService.subscribeToRealtime(
|
||||
symbol,
|
||||
handleRealtimeUpdate,
|
||||
handleError
|
||||
);
|
||||
|
||||
return () => {
|
||||
if (unsubscribeRef.current) {
|
||||
unsubscribeRef.current();
|
||||
unsubscribeRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [symbol, enableRealtime, isLoading, error]);
|
||||
|
||||
const refresh = async () => {
|
||||
const { start, end } = ChartDataService.getDefaultDateRange(interval);
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const newData = await ChartDataService.getHistoricalData(symbol, interval, start, end);
|
||||
setData(newData);
|
||||
|
||||
const currentData = await ChartDataService.getRealtimeData(symbol);
|
||||
setMarketData(currentData);
|
||||
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to refresh data');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
data,
|
||||
marketData,
|
||||
isLoading,
|
||||
error,
|
||||
refresh,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue