/** * Generic utility functions that work with standardized types * These functions demonstrate how to use generic types with OHLCV data */ import type { HasClose, HasOHLC, HasVolume, OHLCV } from '@stock-bot/types'; /** * Extract close prices from any data structure that has a close field * Works with OHLCV, MarketData, or any custom type with close price */ export function extractCloses(data: T[]): number[] { return data.map(item => item.close); } /** * Extract OHLC prices from any data structure that has OHLC fields */ export function extractOHLC( data: T[] ): { opens: number[]; highs: number[]; lows: number[]; closes: number[]; } { return { opens: data.map(item => item.open), highs: data.map(item => item.high), lows: data.map(item => item.low), closes: data.map(item => item.close), }; } /** * Extract volumes from any data structure that has a volume field */ export function extractVolumes(data: T[]): number[] { return data.map(item => item.volume); } /** * Calculate simple moving average using close prices from any compatible data type */ export function calculateSMA(data: T[], period: number): number[] { const closes = extractCloses(data); const result: number[] = []; for (let i = period - 1; i < closes.length; i++) { const sum = closes.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0); result.push(sum / period); } return result; } /** * Calculate typical price (HLC/3) from any OHLC compatible data */ export function calculateTypicalPrice(data: T[]): number[] { return data.map(item => (item.high + item.low + item.close) / 3); } /** * Calculate true range from OHLC data */ export function calculateTrueRange(data: T[]): number[] { const result: number[] = []; for (let i = 0; i < data.length; i++) { if (i === 0) { result.push(data[i]!.high - data[i]!.low); } else { const current = data[i]!; const previous = data[i - 1]!; const tr = Math.max( current.high - current.low, Math.abs(current.high - previous.close), Math.abs(current.low - previous.close) ); result.push(tr); } } return result; } /** * Calculate returns from close prices */ export function calculateReturns(data: T[]): number[] { const closes = extractCloses(data); const returns: number[] = []; for (let i = 1; i < closes.length; i++) { const current = closes[i]!; const previous = closes[i - 1]!; if (previous > 0) { returns.push((current - previous) / previous); } else { returns.push(0); } } return returns; } /** * Calculate log returns from close prices */ export function calculateLogReturns(data: T[]): number[] { const closes = extractCloses(data); const logReturns: number[] = []; for (let i = 1; i < closes.length; i++) { const current = closes[i]!; const previous = closes[i - 1]!; if (previous > 0 && current > 0) { logReturns.push(Math.log(current / previous)); } else { logReturns.push(0); } } return logReturns; } /** * Calculate volume-weighted average price (VWAP) from OHLC + Volume data */ export function calculateVWAP(data: T[]): number[] { const result: number[] = []; let cumulativeVolumePrice = 0; let cumulativeVolume = 0; for (const item of data) { const typicalPrice = (item.high + item.low + item.close) / 3; cumulativeVolumePrice += typicalPrice * item.volume; cumulativeVolume += item.volume; if (cumulativeVolume > 0) { result.push(cumulativeVolumePrice / cumulativeVolume); } else { result.push(typicalPrice); } } return result; } /** * Filter OHLCV data by symbol */ export function filterBySymbol(data: OHLCV[], symbol: string): OHLCV[] { return data.filter(item => item.symbol === symbol); } /** * Filter OHLCV data by time range */ export function filterByTimeRange(data: OHLCV[], startTime: number, endTime: number): OHLCV[] { return data.filter(item => item.timestamp >= startTime && item.timestamp <= endTime); } /** * Group OHLCV data by symbol */ export function groupBySymbol(data: OHLCV[]): Record { const grouped: Record = {}; for (const item of data) { if (!grouped[item.symbol]) { grouped[item.symbol] = []; } grouped[item.symbol]!.push(item); } return grouped; } /** * Convert timestamp to Date for OHLCV data */ export function convertTimestamps(data: OHLCV[]): Array { return data.map(item => ({ ...item, date: new Date(item.timestamp), })); }