248 lines
6.9 KiB
TypeScript
248 lines
6.9 KiB
TypeScript
// Core candle data structure (normalized from API)
|
|
export interface Candle {
|
|
time: number; // UTC timestamp in milliseconds
|
|
open: number;
|
|
high: number;
|
|
low: number;
|
|
close: number;
|
|
volume: number;
|
|
}
|
|
|
|
// Chart size options
|
|
export type ChartSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
|
|
// Chart configuration props
|
|
export interface CandlestickGraphProps {
|
|
/** Stock symbol (e.g., "AAPL", "MSFT") */
|
|
symbol: string;
|
|
/** User identifier for API authentication */
|
|
user: string;
|
|
/** Start date for the data range (ISO string or Date) */
|
|
startDate: string | Date;
|
|
/** End date for the data range (ISO string or Date) */
|
|
endDate: string | Date;
|
|
/** Bar size / time interval for each candle */
|
|
barSize: string;
|
|
/** Predefined chart size or custom className */
|
|
size?: ChartSize;
|
|
/** Additional CSS classes */
|
|
className?: string;
|
|
}
|
|
|
|
// View model state for the chart
|
|
export interface ChartViewModel {
|
|
candles: Candle[];
|
|
visibleStartIndex: number;
|
|
visibleEndIndex: number;
|
|
hoveredIndex: number | null;
|
|
chartDimensions: ChartDimensions;
|
|
priceRange: PriceRange;
|
|
volumeRange: VolumeRange;
|
|
candleLayout: CandleLayout;
|
|
selectedIndicators: string[];
|
|
selectedEvents: string[];
|
|
}
|
|
|
|
export interface ChartDimensions {
|
|
width: number;
|
|
height: number;
|
|
priceChartHeight: number;
|
|
volumeChartHeight: number;
|
|
paddingTop: number;
|
|
paddingRight: number;
|
|
paddingBottom: number;
|
|
paddingLeft: number;
|
|
}
|
|
|
|
export interface PriceRange {
|
|
min: number;
|
|
max: number;
|
|
}
|
|
|
|
export interface VolumeRange {
|
|
min: number;
|
|
max: number;
|
|
}
|
|
|
|
export interface CandleLayout {
|
|
candleWidth: number;
|
|
candleSpacing: number;
|
|
xStep: number;
|
|
}
|
|
|
|
// Mouse interaction state
|
|
export interface MouseState {
|
|
x: number;
|
|
y: number;
|
|
isInChart: boolean;
|
|
}
|
|
|
|
// Measure point for the measure tool
|
|
export interface MeasurePoint {
|
|
x: number;
|
|
y: number;
|
|
price: number;
|
|
time: number;
|
|
index: number;
|
|
}
|
|
|
|
// Note annotation for the chart
|
|
export interface ChartNote {
|
|
id: string;
|
|
time: number; // UTC timestamp - x position
|
|
price: number; // price level - y position
|
|
text: string;
|
|
isMinimized: boolean;
|
|
createdAt: number;
|
|
}
|
|
|
|
// Draw line annotation for the chart
|
|
export interface DrawLine {
|
|
id: string;
|
|
startTime: number; // UTC timestamp - x position of start point
|
|
startPrice: number; // price level - y position of start point
|
|
endTime: number; // UTC timestamp - x position of end point
|
|
endPrice: number; // price level - y position of end point
|
|
lineType: 'line' | 'arrow' | 'dotted';
|
|
color: string;
|
|
createdAt: number;
|
|
}
|
|
|
|
// Combined annotations type for API persistence
|
|
export interface ChartAnnotations {
|
|
notes: ChartNote[];
|
|
drawLines: DrawLine[];
|
|
}
|
|
|
|
// Indicator types
|
|
export type IndicatorType = 'sma' | 'ema' | 'boll' | 'rsi' | 'macd' | 'volume';
|
|
|
|
export interface IndicatorParams {
|
|
period?: number;
|
|
stdDev?: number; // For Bollinger Bands
|
|
fastPeriod?: number; // For MACD
|
|
slowPeriod?: number; // For MACD
|
|
signalPeriod?: number; // For MACD
|
|
}
|
|
|
|
export interface ChartIndicator {
|
|
id: string;
|
|
type: IndicatorType;
|
|
params: IndicatorParams;
|
|
color: string;
|
|
enabled: boolean;
|
|
}
|
|
|
|
// Default indicator configurations
|
|
export const DEFAULT_INDICATOR_PARAMS: Record<IndicatorType, IndicatorParams> = {
|
|
sma: { period: 20 },
|
|
ema: { period: 12 },
|
|
boll: { period: 20, stdDev: 2 },
|
|
rsi: { period: 14 },
|
|
macd: { fastPeriod: 12, slowPeriod: 26, signalPeriod: 9 },
|
|
volume: {},
|
|
};
|
|
|
|
export const INDICATOR_COLORS = [
|
|
'#3b82f6', // blue
|
|
'#f59e0b', // amber
|
|
'#8b5cf6', // violet
|
|
'#ec4899', // pink
|
|
'#06b6d4', // cyan
|
|
'#84cc16', // lime
|
|
];
|
|
|
|
// Chart colors/theme
|
|
export interface ChartTheme {
|
|
background: string;
|
|
gridLine: string;
|
|
gridLineMajor: string;
|
|
bullishBody: string;
|
|
bullishWick: string;
|
|
bearishBody: string;
|
|
bearishWick: string;
|
|
volumeBullish: string;
|
|
volumeBearish: string;
|
|
crosshair: string;
|
|
axisText: string;
|
|
tooltipBackground: string;
|
|
tooltipBorder: string;
|
|
tooltipText: string;
|
|
}
|
|
|
|
/**
|
|
* Gets theme colors from CSS custom properties.
|
|
* This enables dark/light mode support.
|
|
*/
|
|
export function getChartTheme(): ChartTheme {
|
|
if (typeof window === 'undefined') {
|
|
// SSR fallback - use dark theme defaults
|
|
return {
|
|
background: '#0d0d0f',
|
|
gridLine: 'rgba(255, 255, 255, 0.04)',
|
|
gridLineMajor: 'rgba(255, 255, 255, 0.08)',
|
|
bullishBody: '#22c55e',
|
|
bullishWick: '#22c55e',
|
|
bearishBody: '#ef4444',
|
|
bearishWick: '#ef4444',
|
|
volumeBullish: 'rgba(34, 197, 94, 0.3)',
|
|
volumeBearish: 'rgba(239, 68, 68, 0.3)',
|
|
crosshair: 'rgba(255, 255, 255, 0.5)',
|
|
axisText: 'rgba(255, 255, 255, 0.5)',
|
|
tooltipBackground: '#1a1a1f',
|
|
tooltipBorder: 'rgba(255, 255, 255, 0.1)',
|
|
tooltipText: '#ffffff',
|
|
};
|
|
}
|
|
|
|
const styles = getComputedStyle(document.documentElement);
|
|
|
|
return {
|
|
background: styles.getPropertyValue('--chart-bg').trim() || '#0d0d0f',
|
|
gridLine: styles.getPropertyValue('--chart-grid').trim() || 'rgba(255, 255, 255, 0.04)',
|
|
gridLineMajor: styles.getPropertyValue('--chart-grid-major').trim() || 'rgba(255, 255, 255, 0.08)',
|
|
bullishBody: '#22c55e',
|
|
bullishWick: '#22c55e',
|
|
bearishBody: '#ef4444',
|
|
bearishWick: '#ef4444',
|
|
volumeBullish: 'rgba(34, 197, 94, 0.3)',
|
|
volumeBearish: 'rgba(239, 68, 68, 0.3)',
|
|
crosshair: styles.getPropertyValue('--chart-crosshair').trim() || 'rgba(255, 255, 255, 0.5)',
|
|
axisText: styles.getPropertyValue('--chart-axis-text').trim() || 'rgba(255, 255, 255, 0.5)',
|
|
tooltipBackground: styles.getPropertyValue('--chart-tooltip-bg').trim() || '#1a1a1f',
|
|
tooltipBorder: styles.getPropertyValue('--chart-tooltip-border').trim() || 'rgba(255, 255, 255, 0.1)',
|
|
tooltipText: styles.getPropertyValue('--foreground').trim() || '#ffffff',
|
|
};
|
|
}
|
|
|
|
// Default theme for initial render (dark mode)
|
|
export const DEFAULT_CHART_THEME: ChartTheme = {
|
|
background: '#0d0d0f',
|
|
gridLine: 'rgba(255, 255, 255, 0.04)',
|
|
gridLineMajor: 'rgba(255, 255, 255, 0.08)',
|
|
bullishBody: '#22c55e',
|
|
bullishWick: '#22c55e',
|
|
bearishBody: '#ef4444',
|
|
bearishWick: '#ef4444',
|
|
volumeBullish: 'rgba(34, 197, 94, 0.3)',
|
|
volumeBearish: 'rgba(239, 68, 68, 0.3)',
|
|
crosshair: 'rgba(255, 255, 255, 0.5)',
|
|
axisText: 'rgba(255, 255, 255, 0.5)',
|
|
tooltipBackground: '#1a1a1f',
|
|
tooltipBorder: 'rgba(255, 255, 255, 0.1)',
|
|
tooltipText: '#ffffff',
|
|
};
|
|
|
|
// Chart layout constants
|
|
export const CHART_LAYOUT = {
|
|
PRICE_CHART_RATIO: 0.75, // 75% for price, 25% for volume
|
|
PADDING_TOP: 20,
|
|
PADDING_RIGHT: 70, // Space for price axis
|
|
PADDING_BOTTOM: 30, // Space for time axis
|
|
PADDING_LEFT: 10,
|
|
MIN_CANDLE_WIDTH: 3,
|
|
MAX_CANDLE_WIDTH: 20,
|
|
CANDLE_SPACING_RATIO: 0.2, // 20% of candle width as spacing
|
|
VOLUME_SECTION_GAP: 10,
|
|
};
|