Sergey Krylov 9ae3906971 feat: Sprint 5 - frontend detail pages with charts
- Add useStock, useStockCandles, useStockDividends, useBond, useBondCandles hooks
- Add StockDetails component with full share info
- Add BondDetails component with full bond info (NKD, YTM, duration, coupon)
- Add PriceChart component using lightweight-charts (candlestick chart)
- Update StockPage and BondPage with details, charts, and dividends table
2026-06-13 19:21:03 +03:00

72 lines
1.8 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { createChart, ColorType, CandlestickData, Time } from 'lightweight-charts';
interface PriceChartProps {
data: Array<{
open: number;
high: number;
low: number;
close: number;
begin: string;
}>;
height?: number;
}
export function PriceChart({ data, height = 400 }: PriceChartProps) {
const chartContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!chartContainerRef.current) return;
const chart = createChart(chartContainerRef.current, {
layout: {
background: { type: ColorType.Solid, color: '#ffffff' },
textColor: '#333',
},
width: chartContainerRef.current.clientWidth,
height,
grid: {
vertLines: { color: '#f0f0f0' },
horzLines: { color: '#f0f0f0' },
},
timeScale: {
timeVisible: false,
},
});
const candleSeries = chart.addCandlestickSeries({
upColor: '#2e7d32',
downColor: '#c62828',
borderDownColor: '#c62828',
borderUpColor: '#2e7d32',
wickDownColor: '#c62828',
wickUpColor: '#2e7d32',
});
const chartData: CandlestickData[] = data.map((candle) => ({
time: (new Date(candle.begin).getTime() / 1000) as Time,
open: candle.open,
high: candle.high,
low: candle.low,
close: candle.close,
}));
candleSeries.setData(chartData);
chart.timeScale().fitContent();
const handleResize = () => {
if (chartContainerRef.current) {
chart.applyOptions({ width: chartContainerRef.current.clientWidth });
}
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
chart.remove();
};
}, [data, height]);
return <div ref={chartContainerRef} />;
}