stock-bot/libs/utils/test/utils.test.ts
2025-06-25 11:38:23 -04:00

232 lines
6.3 KiB
TypeScript

import { describe, expect, it } from 'bun:test';
import {
calculateLogReturns,
calculateReturns,
calculateSMA,
calculateTrueRange,
calculateTypicalPrice,
calculateVWAP,
convertTimestamps,
// Common utilities
createProxyUrl,
// Date utilities
dateUtils,
// Generic functions
extractCloses,
extractOHLC,
extractVolumes,
filterBySymbol,
filterByTimeRange,
groupBySymbol,
sleep,
} from '../src/index';
describe('Utility Functions', () => {
describe('common utilities', () => {
it('should create proxy URL with auth', () => {
const proxy = {
protocol: 'http',
host: '192.168.1.1',
port: 8080,
username: 'user',
password: 'pass',
};
const url = createProxyUrl(proxy);
expect(url).toBe('http://user:pass@192.168.1.1:8080');
});
it('should create proxy URL without auth', () => {
const proxy = {
protocol: 'socks5',
host: '192.168.1.1',
port: 1080,
};
const url = createProxyUrl(proxy);
expect(url).toBe('socks5://192.168.1.1:1080');
});
it('should sleep for specified milliseconds', async () => {
const start = Date.now();
await sleep(100);
const elapsed = Date.now() - start;
expect(elapsed).toBeGreaterThanOrEqual(90);
expect(elapsed).toBeLessThan(200);
});
});
describe('date utilities', () => {
it('should check if date is trading day', () => {
const monday = new Date('2023-12-25'); // Monday
const saturday = new Date('2023-12-23'); // Saturday
const sunday = new Date('2023-12-24'); // Sunday
expect(dateUtils.isTradingDay(monday)).toBe(true);
expect(dateUtils.isTradingDay(saturday)).toBe(false);
expect(dateUtils.isTradingDay(sunday)).toBe(false);
});
it('should get next trading day', () => {
const friday = new Date('2023-12-22'); // Friday
const nextDay = dateUtils.getNextTradingDay(friday);
expect(nextDay.getDay()).toBe(1); // Monday
});
it('should get previous trading day', () => {
const monday = new Date('2023-12-25'); // Monday
const prevDay = dateUtils.getPreviousTradingDay(monday);
expect(prevDay.getDay()).toBe(5); // Friday
});
it('should format date as YYYY-MM-DD', () => {
const date = new Date('2023-12-25T10:30:00Z');
const formatted = dateUtils.formatDate(date);
expect(formatted).toBe('2023-12-25');
});
it('should parse date from string', () => {
const date = dateUtils.parseDate('2023-12-25');
expect(date.getFullYear()).toBe(2023);
expect(date.getMonth()).toBe(11); // 0-based
expect(date.getDate()).toBe(25);
});
});
describe('generic functions', () => {
const testData = [
{ open: 100, high: 105, low: 98, close: 103, volume: 1000 },
{ open: 103, high: 107, low: 101, close: 105, volume: 1200 },
{ open: 105, high: 108, low: 104, close: 106, volume: 1100 },
];
it('should extract close prices', () => {
const closes = extractCloses(testData);
expect(closes).toEqual([103, 105, 106]);
});
it('should extract OHLC data', () => {
const ohlc = extractOHLC(testData);
expect(ohlc.opens).toEqual([100, 103, 105]);
expect(ohlc.highs).toEqual([105, 107, 108]);
expect(ohlc.lows).toEqual([98, 101, 104]);
expect(ohlc.closes).toEqual([103, 105, 106]);
});
it('should extract volumes', () => {
const volumes = extractVolumes(testData);
expect(volumes).toEqual([1000, 1200, 1100]);
});
it('should calculate SMA', () => {
const sma = calculateSMA(testData, 2);
expect(sma).toHaveLength(2);
expect(sma[0]).toBe(104);
expect(sma[1]).toBe(105.5);
});
it('should calculate typical price', () => {
const typical = calculateTypicalPrice(testData);
expect(typical[0]).toBeCloseTo((105 + 98 + 103) / 3);
expect(typical[1]).toBeCloseTo((107 + 101 + 105) / 3);
expect(typical[2]).toBeCloseTo((108 + 104 + 106) / 3);
});
it('should calculate true range', () => {
const tr = calculateTrueRange(testData);
expect(tr).toHaveLength(3);
expect(tr[0]).toBe(7); // 105 - 98
});
it('should calculate returns', () => {
const returns = calculateReturns(testData);
expect(returns).toHaveLength(2);
expect(returns[0]).toBeCloseTo((105 - 103) / 103);
expect(returns[1]).toBeCloseTo((106 - 105) / 105);
});
it('should calculate log returns', () => {
const logReturns = calculateLogReturns(testData);
expect(logReturns).toHaveLength(2);
expect(logReturns[0]).toBeCloseTo(Math.log(105 / 103));
expect(logReturns[1]).toBeCloseTo(Math.log(106 / 105));
});
it('should calculate VWAP', () => {
const vwap = calculateVWAP(testData);
expect(vwap).toHaveLength(3);
expect(vwap[0]).toBeGreaterThan(0);
});
});
describe('OHLCV data operations', () => {
const ohlcvData = [
{
symbol: 'AAPL',
open: 100,
high: 105,
low: 98,
close: 103,
volume: 1000,
timestamp: 1000000,
},
{
symbol: 'GOOGL',
open: 200,
high: 205,
low: 198,
close: 203,
volume: 2000,
timestamp: 1000000,
},
{
symbol: 'AAPL',
open: 103,
high: 107,
low: 101,
close: 105,
volume: 1200,
timestamp: 2000000,
},
];
it('should filter by symbol', () => {
const filtered = filterBySymbol(ohlcvData, 'AAPL');
expect(filtered).toHaveLength(2);
expect(filtered.every(item => item.symbol === 'AAPL')).toBe(true);
});
it('should filter by time range', () => {
const filtered = filterByTimeRange(ohlcvData, 1500000, 2500000);
expect(filtered).toHaveLength(1);
expect(filtered[0].timestamp).toBe(2000000);
});
it('should group by symbol', () => {
const grouped = groupBySymbol(ohlcvData);
expect(grouped['AAPL']).toHaveLength(2);
expect(grouped['GOOGL']).toHaveLength(1);
});
it('should convert timestamps to dates', () => {
const converted = convertTimestamps(ohlcvData);
expect(converted[0].date).toBeInstanceOf(Date);
expect(converted[0].date.getTime()).toBe(1000000);
});
});
});