fixed up types and working on utils lib
This commit is contained in:
parent
25d9f2dd85
commit
da75979574
17 changed files with 1093 additions and 901 deletions
|
|
@ -95,8 +95,11 @@ export function internalRateOfReturn(
|
|||
let dnpv = 0;
|
||||
|
||||
for (let j = 0; j < cashFlows.length; j++) {
|
||||
npv += cashFlows[j] / Math.pow(1 + rate, j);
|
||||
dnpv += (-j * cashFlows[j]) / Math.pow(1 + rate, j + 1);
|
||||
const cashFlow = cashFlows[j];
|
||||
if (cashFlow !== undefined) {
|
||||
npv += cashFlow / Math.pow(1 + rate, j);
|
||||
dnpv += (-j * cashFlow) / Math.pow(1 + rate, j + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.abs(npv) < 1e-10) {
|
||||
|
|
@ -119,9 +122,12 @@ export function paybackPeriod(initialInvestment: number, cashFlows: number[]): n
|
|||
let cumulativeCashFlow = 0;
|
||||
|
||||
for (let i = 0; i < cashFlows.length; i++) {
|
||||
cumulativeCashFlow += cashFlows[i];
|
||||
if (cumulativeCashFlow >= initialInvestment) {
|
||||
return i + 1 - (cumulativeCashFlow - initialInvestment) / cashFlows[i];
|
||||
const cashFlow = cashFlows[i];
|
||||
if (cashFlow !== undefined) {
|
||||
cumulativeCashFlow += cashFlow;
|
||||
if (cumulativeCashFlow >= initialInvestment) {
|
||||
return i + 1 - (cumulativeCashFlow - initialInvestment) / cashFlow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,10 @@ export function pearsonCorrelation(x: number[], y: number[]): CorrelationResult
|
|||
const n = x.length;
|
||||
const sumX = x.reduce((a, b) => a + b, 0);
|
||||
const sumY = y.reduce((a, b) => a + b, 0);
|
||||
const sumXY = x.reduce((sum, xi, i) => sum + xi * y[i], 0);
|
||||
const sumXY = x.reduce((sum, xi, i) => {
|
||||
const yi = y[i];
|
||||
return yi !== undefined ? sum + xi * yi : sum;
|
||||
}, 0);
|
||||
const sumX2 = x.reduce((sum, xi) => sum + xi * xi, 0);
|
||||
const sumY2 = y.reduce((sum, yi) => sum + yi * yi, 0);
|
||||
|
||||
|
|
@ -127,13 +130,20 @@ export function kendallTau(x: number[], y: number[]): CorrelationResult {
|
|||
|
||||
for (let i = 0; i < n - 1; i++) {
|
||||
for (let j = i + 1; j < n; j++) {
|
||||
const xDiff = x[i] - x[j];
|
||||
const yDiff = y[i] - y[j];
|
||||
const xi = x[i];
|
||||
const xj = x[j];
|
||||
const yi = y[i];
|
||||
const yj = y[j];
|
||||
|
||||
if (xi !== undefined && xj !== undefined && yi !== undefined && yj !== undefined) {
|
||||
const xDiff = xi - xj;
|
||||
const yDiff = yi - yj;
|
||||
|
||||
if (xDiff * yDiff > 0) {
|
||||
concordant++;
|
||||
} else if (xDiff * yDiff < 0) {
|
||||
discordant++;
|
||||
if (xDiff * yDiff > 0) {
|
||||
concordant++;
|
||||
} else if (xDiff * yDiff < 0) {
|
||||
discordant++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,98 +25,40 @@ import {
|
|||
* Organized into logical categories for easy use and maintenance.
|
||||
*/
|
||||
|
||||
// Re-export standardized types
|
||||
export type { OHLCV, OHLCVWithMetadata } from '@stock-bot/types';
|
||||
|
||||
// Legacy interface for backward compatibility - prefer OHLCV from @stock-bot/types
|
||||
/** @deprecated Use OHLCV from @stock-bot/types instead */
|
||||
export interface OHLCVData {
|
||||
open: number;
|
||||
high: number;
|
||||
low: number;
|
||||
close: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface PriceData {
|
||||
price: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
// Financial calculation result interfaces
|
||||
export interface PortfolioMetrics {
|
||||
totalValue: number;
|
||||
totalReturn: number;
|
||||
totalReturnPercent: number;
|
||||
dailyReturn: number;
|
||||
dailyReturnPercent: number;
|
||||
maxDrawdown: number;
|
||||
sharpeRatio: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
volatility: number;
|
||||
}
|
||||
|
||||
export interface RiskMetrics {
|
||||
var95: number; // Value at Risk 95%
|
||||
var99: number; // Value at Risk 99%
|
||||
cvar95: number; // Conditional VaR 95%
|
||||
maxDrawdown: number;
|
||||
volatility: number;
|
||||
downside_deviation: number;
|
||||
calmar_ratio: number;
|
||||
sortino_ratio: number;
|
||||
beta: number;
|
||||
alpha: number;
|
||||
sharpeRatio: number;
|
||||
treynorRatio: number;
|
||||
trackingError: number;
|
||||
informationRatio: number;
|
||||
}
|
||||
|
||||
export interface TechnicalIndicators {
|
||||
sma: number[];
|
||||
ema: number[];
|
||||
rsi: number[];
|
||||
macd: { macd: number[]; signal: number[]; histogram: number[] };
|
||||
bollinger: { upper: number[]; middle: number[]; lower: number[] };
|
||||
atr: number[];
|
||||
stochastic: { k: number[]; d: number[] };
|
||||
williams_r: number[];
|
||||
cci: number[];
|
||||
momentum: number[];
|
||||
roc: number[];
|
||||
}
|
||||
|
||||
// Additional interfaces for new functionality
|
||||
export interface TradeExecution {
|
||||
entry: number;
|
||||
exit: number;
|
||||
peak?: number;
|
||||
trough?: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface MarketData {
|
||||
price: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
bid?: number;
|
||||
ask?: number;
|
||||
bidSize?: number;
|
||||
askSize?: number;
|
||||
}
|
||||
|
||||
export interface BacktestResults {
|
||||
trades: TradeExecution[];
|
||||
equityCurve: Array<{ value: number; date: Date }>;
|
||||
|
||||
performance: PortfolioMetrics;
|
||||
riskMetrics: RiskMetrics;
|
||||
drawdownAnalysis: any; // Import from performance-metrics
|
||||
}
|
||||
// Re-export all standardized types from @stock-bot/types
|
||||
export type {
|
||||
OHLCV,
|
||||
OHLCVWithMetadata,
|
||||
PortfolioPosition,
|
||||
PortfolioAnalysis,
|
||||
AssetAllocation,
|
||||
TradeExecution,
|
||||
TradePerformance,
|
||||
RiskMetrics,
|
||||
DrawdownAnalysis,
|
||||
ReturnAnalysis,
|
||||
OptionParameters,
|
||||
OptionPricing,
|
||||
GreeksCalculation,
|
||||
MarketData,
|
||||
LiquidityMetrics,
|
||||
MarketRegime,
|
||||
PositionSizeParams,
|
||||
KellyParams,
|
||||
BalanceSheet,
|
||||
IncomeStatement,
|
||||
CashFlowStatement,
|
||||
TechnicalIndicators,
|
||||
CorrelationResult,
|
||||
CorrelationMatrix,
|
||||
VolatilityEstimates,
|
||||
GARCHParameters,
|
||||
BacktestResults,
|
||||
HasClose,
|
||||
HasOHLC,
|
||||
HasVolume,
|
||||
HasTimestamp
|
||||
} from '@stock-bot/types';
|
||||
|
||||
// Export all calculation functions
|
||||
export * from './basic-calculations';
|
||||
|
|
@ -132,7 +74,7 @@ export * from './correlation-analysis';
|
|||
|
||||
// Convenience function to calculate all technical indicators at once
|
||||
export function calculateAllTechnicalIndicators(
|
||||
ohlcv: OHLCVData[],
|
||||
ohlcv: OHLCV[],
|
||||
periods: { sma?: number; ema?: number; rsi?: number; atr?: number } = {}
|
||||
): TechnicalIndicators {
|
||||
const {
|
||||
|
|
@ -166,7 +108,7 @@ export function analyzePortfolio(
|
|||
benchmarkReturns?: number[],
|
||||
riskFreeRate: number = 0.02
|
||||
): {
|
||||
performance: PortfolioMetrics;
|
||||
performance: PortfolioAnalysis;
|
||||
risk: RiskMetrics;
|
||||
trades?: any;
|
||||
drawdown?: any;
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ export const dateUtils = {
|
|||
* Format a date as YYYY-MM-DD
|
||||
*/
|
||||
formatDate(date: Date): string {
|
||||
return date.toISOString().split('T')[0];
|
||||
const parts = date.toISOString().split('T');
|
||||
return parts[0] ?? '';
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
191
libs/utils/src/generic-functions.ts
Normal file
191
libs/utils/src/generic-functions.ts
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/**
|
||||
* Generic utility functions that work with standardized types
|
||||
* These functions demonstrate how to use generic types with OHLCV data
|
||||
*/
|
||||
|
||||
import type { OHLCV, HasClose, HasOHLC, HasVolume } 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<T extends HasClose>(data: T[]): number[] {
|
||||
return data.map(item => item.close);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract OHLC prices from any data structure that has OHLC fields
|
||||
*/
|
||||
export function extractOHLC<T extends HasOHLC>(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<T extends HasVolume>(data: T[]): number[] {
|
||||
return data.map(item => item.volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate simple moving average using close prices from any compatible data type
|
||||
*/
|
||||
export function calculateSMA<T extends HasClose>(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<T extends HasOHLC>(data: T[]): number[] {
|
||||
return data.map(item => (item.high + item.low + item.close) / 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate true range from OHLC data
|
||||
*/
|
||||
export function calculateTrueRange<T extends HasOHLC>(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<T extends HasClose>(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<T extends HasClose>(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<T extends HasOHLC & HasVolume>(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<string, OHLCV[]> {
|
||||
const grouped: Record<string, OHLCV[]> = {};
|
||||
|
||||
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<OHLCV & { date: Date }> {
|
||||
return data.map(item => ({
|
||||
...item,
|
||||
date: new Date(item.timestamp)
|
||||
}));
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export * from './calculations/index';
|
||||
export * from './common';
|
||||
export * from './dateUtils';
|
||||
export * from './generic-functions';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue