// 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 = { 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, };