This commit is contained in:
Boki 2025-06-11 10:35:15 -04:00
parent d85cd58acd
commit 597c6efc9b
91 changed files with 2224 additions and 1400 deletions

View file

@ -7,7 +7,7 @@
* Calculate percentage change between two values
*/
export function percentageChange(oldValue: number, newValue: number): number {
if (oldValue === 0) return 0;
if (oldValue === 0) {return 0;}
return ((newValue - oldValue) / oldValue) * 100;
}
@ -15,7 +15,7 @@ export function percentageChange(oldValue: number, newValue: number): number {
* Calculate simple return
*/
export function simpleReturn(initialPrice: number, finalPrice: number): number {
if (initialPrice === 0) return 0;
if (initialPrice === 0) {return 0;}
return (finalPrice - initialPrice) / initialPrice;
}
@ -23,7 +23,7 @@ export function simpleReturn(initialPrice: number, finalPrice: number): number {
* Calculate logarithmic return
*/
export function logReturn(initialPrice: number, finalPrice: number): number {
if (initialPrice <= 0 || finalPrice <= 0) return 0;
if (initialPrice <= 0 || finalPrice <= 0) {return 0;}
return Math.log(finalPrice / initialPrice);
}
@ -31,7 +31,7 @@ export function logReturn(initialPrice: number, finalPrice: number): number {
* Calculate compound annual growth rate (CAGR)
*/
export function cagr(startValue: number, endValue: number, years: number): number {
if (years <= 0 || startValue <= 0 || endValue <= 0) return 0;
if (years <= 0 || startValue <= 0 || endValue <= 0) {return 0;}
return Math.pow(endValue / startValue, 1 / years) - 1;
}
@ -91,8 +91,8 @@ export function internalRateOfReturn(
dnpv += (-j * cashFlows[j]) / Math.pow(1 + rate, j + 1);
}
if (Math.abs(npv) < 1e-10) break;
if (Math.abs(dnpv) < 1e-10) break;
if (Math.abs(npv) < 1e-10) {break;}
if (Math.abs(dnpv) < 1e-10) {break;}
rate = rate - npv / dnpv;
}
@ -186,7 +186,7 @@ export function bondYield(
);
const diff = calculatedPrice - price;
if (Math.abs(diff) < tolerance) break;
if (Math.abs(diff) < tolerance) {break;}
// Numerical derivative
const delta = 0.0001;
@ -199,7 +199,7 @@ export function bondYield(
);
const derivative = (priceUp - calculatedPrice) / delta;
if (Math.abs(derivative) < tolerance) break;
if (Math.abs(derivative) < tolerance) {break;}
yield_ = yield_ - diff / derivative;
}
@ -358,7 +358,7 @@ export function dividendDiscountModel(
growthRate: number,
discountRate: number
): number {
if (discountRate <= growthRate) return NaN; // Indeterminate
if (discountRate <= growthRate) {return NaN;} // Indeterminate
return (currentDividend * (1 + growthRate)) / (discountRate - growthRate);
}

View file

@ -488,7 +488,7 @@ export function dccModel(
const T = data[0].length;
// Initialize parameters [alpha, beta]
let params = [0.01, 0.95];
const params = [0.01, 0.95];
// Standardize data (assume unit variance for simplicity)
const standardizedData = data.map(series => {
@ -918,7 +918,7 @@ function shuffleArray<T>(array: T[]): T[] {
* Helper function to calculate the average of an array of numbers
*/
function average(arr: number[]): number {
if (arr.length === 0) return 0;
if (arr.length === 0) {return 0;}
return arr.reduce((a, b) => a + b, 0) / arr.length;
}
@ -963,8 +963,8 @@ function erf(x: number): number {
function betaIncomplete(a: number, b: number, x: number): number {
// Better approximation of incomplete beta function
if (x === 0) return 0;
if (x === 1) return 1;
if (x === 0) {return 0;}
if (x === 1) {return 1;}
// Use continued fraction approximation (Lentz's algorithm)
const fpmin = 1e-30;
@ -984,7 +984,7 @@ function betaIncomplete(a: number, b: number, x: number): number {
function betaContinuedFraction(a: number, b: number, x: number): number {
let c = 1;
let d = 1 - ((a + b) * x) / (a + 1);
if (Math.abs(d) < fpmin) d = fpmin;
if (Math.abs(d) < fpmin) {d = fpmin;}
d = 1 / d;
let h = d;
@ -992,22 +992,22 @@ function betaIncomplete(a: number, b: number, x: number): number {
const m2 = 2 * m;
const aa = (m * (b - m) * x) / ((a + m2 - 1) * (a + m2));
d = 1 + aa * d;
if (Math.abs(d) < fpmin) d = fpmin;
if (Math.abs(d) < fpmin) {d = fpmin;}
c = 1 + aa / c;
if (Math.abs(c) < fpmin) c = fpmin;
if (Math.abs(c) < fpmin) {c = fpmin;}
d = 1 / d;
h *= d * c;
const bb = (-(a + m) * (a + b + m) * x) / ((a + m2) * (a + m2 + 1));
d = 1 + bb * d;
if (Math.abs(d) < fpmin) d = fpmin;
if (Math.abs(d) < fpmin) {d = fpmin;}
c = 1 + bb / c;
if (Math.abs(c) < fpmin) c = fpmin;
if (Math.abs(c) < fpmin) {c = fpmin;}
d = 1 / d;
const del = d * c;
h *= del;
if (Math.abs(del - 1) < eps) break;
if (Math.abs(del - 1) < eps) {break;}
}
return h;
@ -1055,11 +1055,11 @@ function eigenDecomposition(matrix: number[][]): {
const newLambda = Av.reduce((sum, val, i) => sum + val * v[i], 0);
const norm = Math.sqrt(Av.reduce((sum, val) => sum + val * val, 0));
if (norm === 0) break;
if (norm === 0) {break;}
v = Av.map(val => val / norm);
if (Math.abs(newLambda - lambda) < 1e-10) break;
if (Math.abs(newLambda - lambda) < 1e-10) {break;}
lambda = newLambda;
}
@ -1215,8 +1215,8 @@ function arModel(y: number[], lag: number): { rss: number } {
function fCDF(f: number, df1: number, df2: number): number {
// Approximation for F distribution CDF
if (f <= 0) return 0;
if (f === Infinity) return 1;
if (f <= 0) {return 0;}
if (f === Infinity) {return 1;}
const x = df2 / (df2 + df1 * f);
return 1 - betaIncomplete(df2 / 2, df1 / 2, x);

View file

@ -55,7 +55,7 @@ export interface MarketRegime {
* Volume Weighted Average Price (VWAP)
*/
export function VWAP(ohlcv: OHLCVData[]): number[] {
if (ohlcv.length === 0) return [];
if (ohlcv.length === 0) {return [];}
const vwap: number[] = [];
let cumulativeVolumePrice = 0;
@ -76,7 +76,7 @@ export function VWAP(ohlcv: OHLCVData[]): number[] {
* Time Weighted Average Price (TWAP)
*/
export function TWAP(prices: number[], timeWeights?: number[]): number {
if (prices.length === 0) return 0;
if (prices.length === 0) {return 0;}
if (!timeWeights) {
return prices.reduce((sum, price) => sum + price, 0) / prices.length;
@ -227,9 +227,9 @@ export function identifyMarketRegime(
// Determine volatility level
let volatilityLevel: 'low' | 'medium' | 'high';
if (volatility < 0.01) volatilityLevel = 'low';
else if (volatility < 0.03) volatilityLevel = 'medium';
else volatilityLevel = 'high';
if (volatility < 0.01) {volatilityLevel = 'low';}
else if (volatility < 0.03) {volatilityLevel = 'medium';}
else {volatilityLevel = 'high';}
// Determine regime
let regime: 'trending' | 'ranging' | 'volatile' | 'quiet';
@ -281,7 +281,7 @@ export function OrderBookImbalance(
const totalVolume = totalBidVolume + totalAskVolume;
if (totalVolume === 0) return 0;
if (totalVolume === 0) {return 0;}
return (totalBidVolume - totalAskVolume) / totalVolume;
}
@ -452,10 +452,10 @@ export function MarketStress(
const overallStress = volatilityStress * 0.4 + liquidityStress * 0.3 + correlationStress * 0.3;
let stressLevel: 'low' | 'medium' | 'high' | 'extreme';
if (overallStress < 0.25) stressLevel = 'low';
else if (overallStress < 0.5) stressLevel = 'medium';
else if (overallStress < 0.75) stressLevel = 'high';
else stressLevel = 'extreme';
if (overallStress < 0.25) {stressLevel = 'low';}
else if (overallStress < 0.5) {stressLevel = 'medium';}
else if (overallStress < 0.75) {stressLevel = 'high';}
else {stressLevel = 'extreme';}
return {
stressLevel,
@ -474,7 +474,7 @@ export function RealizedSpread(
midPrices: number[],
timeWindow: number = 5 // minutes
): number {
if (trades.length === 0 || midPrices.length === 0) return 0;
if (trades.length === 0 || midPrices.length === 0) {return 0;}
let totalSpread = 0;
let count = 0;
@ -541,7 +541,7 @@ export function ImplementationShortfall(
* Amihud Illiquidity Measure (price impact per unit of volume)
*/
export function amihudIlliquidity(ohlcv: OHLCVData[], lookbackPeriod: number = 252): number {
if (ohlcv.length < lookbackPeriod) return 0;
if (ohlcv.length < lookbackPeriod) {return 0;}
const recentData = ohlcv.slice(-lookbackPeriod);
let illiquiditySum = 0;
@ -566,7 +566,7 @@ export function amihudIlliquidity(ohlcv: OHLCVData[], lookbackPeriod: number = 2
* Roll's Spread Estimator (effective spread from serial covariance)
*/
export function rollSpreadEstimator(prices: number[]): number {
if (prices.length < 3) return 0;
if (prices.length < 3) {return 0;}
// Calculate price changes
const priceChanges: number[] = [];
@ -594,7 +594,7 @@ export function kyleLambda(
priceChanges: number[],
orderFlow: number[] // Signed order flow (positive for buys, negative for sells)
): number {
if (priceChanges.length !== orderFlow.length || priceChanges.length < 2) return 0;
if (priceChanges.length !== orderFlow.length || priceChanges.length < 2) {return 0;}
// Calculate regression: priceChange = lambda * orderFlow + error
const n = priceChanges.length;
@ -623,7 +623,7 @@ export function probabilityInformedTrading(
sellVolumes: number[],
period: number = 20
): number {
if (buyVolumes.length !== sellVolumes.length || buyVolumes.length < period) return 0;
if (buyVolumes.length !== sellVolumes.length || buyVolumes.length < period) {return 0;}
const recentBuys = buyVolumes.slice(-period);
const recentSells = sellVolumes.slice(-period);
@ -647,11 +647,11 @@ export function probabilityInformedTrading(
* Herfindahl-Hirschman Index for Volume Concentration
*/
export function volumeConcentrationHHI(exchanges: Array<{ name: string; volume: number }>): number {
if (exchanges.length === 0) return 0;
if (exchanges.length === 0) {return 0;}
const totalVolume = exchanges.reduce((sum, exchange) => sum + exchange.volume, 0);
if (totalVolume === 0) return 0;
if (totalVolume === 0) {return 0;}
let hhi = 0;
for (const exchange of exchanges) {
@ -670,7 +670,7 @@ export function volumeProfile(
): { [price: number]: number } {
const profile: { [price: number]: number } = {};
if (ohlcv.length === 0) return profile;
if (ohlcv.length === 0) {return profile;}
const minPrice = Math.min(...ohlcv.map(candle => candle.low));
const maxPrice = Math.max(...ohlcv.map(candle => candle.high));
@ -814,11 +814,11 @@ export function garmanKlassVolatility(
openPrices.length !== closePrices.length ||
openPrices.length < 2
)
return 0;
{return 0;}
let sumSquaredTerm1 = 0;
let sumSquaredTerm2 = 0;
let sumSquaredTerm3 = 0;
const sumSquaredTerm3 = 0;
for (let i = 0; i < openPrices.length; i++) {
const logHO = Math.log(highPrices[i] / openPrices[i]);
@ -850,7 +850,7 @@ export function yangZhangVolatility(
openPrices.length !== previousClosePrices.length ||
openPrices.length < 2
)
return 0;
{return 0;}
const k = 0.34 / (1.34 + (openPrices.length + 1) / (previousClosePrices.length - 1));
@ -877,7 +877,7 @@ export function yangZhangVolatility(
* Volume Order Imbalance (VOI)
*/
export function volumeOrderImbalance(buyVolumes: number[], sellVolumes: number[]): number[] {
if (buyVolumes.length !== sellVolumes.length) return [];
if (buyVolumes.length !== sellVolumes.length) {return [];}
const voi: number[] = [];
for (let i = 0; i < buyVolumes.length; i++) {
@ -890,7 +890,7 @@ export function volumeOrderImbalance(buyVolumes: number[], sellVolumes: number[]
* Cumulative Volume Delta (CVD)
*/
export function cumulativeVolumeDelta(buyVolumes: number[], sellVolumes: number[]): number[] {
if (buyVolumes.length !== sellVolumes.length) return [];
if (buyVolumes.length !== sellVolumes.length) {return [];}
const cvd: number[] = [];
let cumulativeDelta = 0;
@ -905,7 +905,7 @@ export function cumulativeVolumeDelta(buyVolumes: number[], sellVolumes: number[
* Market Order Ratio
*/
export function marketOrderRatio(marketOrders: number[], limitOrders: number[]): number[] {
if (marketOrders.length !== limitOrders.length) return [];
if (marketOrders.length !== limitOrders.length) {return [];}
const ratios: number[] = [];
for (let i = 0; i < marketOrders.length; i++) {
@ -920,12 +920,12 @@ export function marketOrderRatio(marketOrders: number[], limitOrders: number[]):
*/
function average(arr: number[]): number {
if (arr.length === 0) return 0;
if (arr.length === 0) {return 0;}
return arr.reduce((a, b) => a + b, 0) / arr.length;
}
function calculateVolatility(returns: number[]): number {
if (returns.length < 2) return 0;
if (returns.length < 2) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
@ -935,7 +935,7 @@ function calculateVolatility(returns: number[]): number {
}
function calculateCorrelation(x: number[], y: number[]): number {
if (x.length !== y.length || x.length < 2) return 0;
if (x.length !== y.length || x.length < 2) {return 0;}
const n = x.length;
const meanX = x.reduce((sum, val) => sum + val, 0) / n;
@ -960,14 +960,14 @@ function calculateCorrelation(x: number[], y: number[]): number {
}
function calculateVariance(values: number[]): number {
if (values.length < 2) return 0;
if (values.length < 2) {return 0;}
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
return values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / (values.length - 1);
}
function calculateCovariance(x: number[], y: number[]): number {
if (x.length !== y.length || x.length < 2) return 0;
if (x.length !== y.length || x.length < 2) {return 0;}
const n = x.length;
const meanX = x.reduce((sum, val) => sum + val, 0) / n;

View file

@ -605,7 +605,7 @@ function erf(x: number): number {
*/
function boxMullerTransform(): number {
let u1 = Math.random();
let u2 = Math.random();
const u2 = Math.random();
// Ensure u1 is not zero
while (u1 === 0) {

View file

@ -153,7 +153,7 @@ export function analyzeDrawdowns(
}> = [];
let currentDrawdownStart: Date | null = null;
let drawdowns: number[] = [];
const drawdowns: number[] = [];
for (let i = 1; i < equityCurve.length; i++) {
const current = equityCurve[i];
@ -297,7 +297,7 @@ export function calculateRollingMetrics(
windowSize: number,
metricType: 'sharpe' | 'volatility' | 'return' = 'sharpe'
): number[] {
if (returns.length < windowSize) return [];
if (returns.length < windowSize) {return [];}
const rollingMetrics: number[] = [];
@ -377,7 +377,7 @@ export function strategyPerformanceAttribution(
* Calculate Omega ratio
*/
export function omegaRatio(returns: number[], threshold: number = 0): number {
if (returns.length === 0) return 0;
if (returns.length === 0) {return 0;}
const gains = returns
.filter(ret => ret > threshold)
@ -393,7 +393,7 @@ export function omegaRatio(returns: number[], threshold: number = 0): number {
* Calculate gain-to-pain ratio
*/
export function gainToPainRatio(returns: number[]): number {
if (returns.length === 0) return 0;
if (returns.length === 0) {return 0;}
const totalGain = returns.reduce((sum, ret) => sum + ret, 0);
const totalPain = returns.filter(ret => ret < 0).reduce((sum, ret) => sum + Math.abs(ret), 0);
@ -405,12 +405,12 @@ export function gainToPainRatio(returns: number[]): number {
* Calculate Martin ratio (modified Sharpe with downside deviation)
*/
export function martinRatio(returns: number[], riskFreeRate: number = 0): number {
if (returns.length === 0) return 0;
if (returns.length === 0) {return 0;}
const averageReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const downsideReturns = returns.filter(ret => ret < riskFreeRate);
if (downsideReturns.length === 0) return Infinity;
if (downsideReturns.length === 0) {return Infinity;}
const downsideDeviation = Math.sqrt(
downsideReturns.reduce((sum, ret) => sum + Math.pow(ret - riskFreeRate, 2), 0) / returns.length
@ -610,7 +610,7 @@ export function tailRatio(returns: number[], tailPercent: number = 0.1): number
const numReturns = returns.length;
const tailSize = Math.floor(numReturns * tailPercent);
if (tailSize === 0) return 0;
if (tailSize === 0) {return 0;}
const sortedReturns = [...returns].sort((a, b) => a - b);
const worstTail = sortedReturns.slice(0, tailSize);
@ -631,7 +631,7 @@ export function calculateRollingBeta(
windowSize: number
): number[] {
if (portfolioReturns.length !== marketReturns.length || portfolioReturns.length < windowSize)
return [];
{return [];}
const rollingBetas: number[] = [];
@ -668,7 +668,7 @@ export function calculateRollingAlpha(
windowSize: number
): number[] {
if (portfolioReturns.length !== marketReturns.length || portfolioReturns.length < windowSize)
return [];
{return [];}
const rollingAlphas: number[] = [];
@ -728,7 +728,7 @@ export function moneyWeightedRateOfReturn(
// Helper functions
function calculateSharpeRatio(returns: number[], riskFreeRate: number = 0): number {
if (returns.length < 2) return 0;
if (returns.length < 2) {return 0;}
const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
@ -739,7 +739,7 @@ function calculateSharpeRatio(returns: number[], riskFreeRate: number = 0): numb
}
function calculateVolatility(returns: number[]): number {
if (returns.length < 2) return 0;
if (returns.length < 2) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
@ -749,7 +749,7 @@ function calculateVolatility(returns: number[]): number {
}
function calculateBeta(portfolioReturns: number[], marketReturns: number[]): number {
if (portfolioReturns.length !== marketReturns.length || portfolioReturns.length < 2) return 0;
if (portfolioReturns.length !== marketReturns.length || portfolioReturns.length < 2) {return 0;}
const portfolioMean =
portfolioReturns.reduce((sum, ret) => sum + ret, 0) / portfolioReturns.length;
@ -786,13 +786,13 @@ function calculateAlpha(
}
function calculateSkewness(returns: number[]): number {
if (returns.length < 3) return 0;
if (returns.length < 3) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
if (stdDev === 0) {return 0;}
const skew =
returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 3), 0) / returns.length;
@ -801,13 +801,13 @@ function calculateSkewness(returns: number[]): number {
}
function calculateKurtosis(returns: number[]): number {
if (returns.length < 4) return 0;
if (returns.length < 4) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
if (stdDev === 0) {return 0;}
const kurt =
returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 4), 0) / returns.length;

View file

@ -209,7 +209,7 @@ export function riskParityOptimization(covarianceMatrix: number[][]): PortfolioO
const sum = newWeights.reduce((s, w) => s + w, 0);
weights = newWeights.map(w => w / sum);
if (converged) break;
if (converged) {break;}
}
const portfolioVariance = calculatePortfolioVariance(weights, covarianceMatrix);
@ -402,7 +402,7 @@ export function calculateEfficientFrontier(
volatility: number;
sharpeRatio: number;
}> {
if (returns.length !== symbols.length || returns.length < 2) return [];
if (returns.length !== symbols.length || returns.length < 2) {return [];}
const n = returns.length;
const results: Array<{
@ -456,7 +456,7 @@ export function findMinimumVariancePortfolio(
returns: number[][],
symbols: string[]
): PortfolioOptimizationResult | null {
if (returns.length !== symbols.length || returns.length < 2) return null;
if (returns.length !== symbols.length || returns.length < 2) {return null;}
const covarianceMatrix = calculateCovarianceMatrix(returns);
const n = returns.length;
@ -517,7 +517,7 @@ function calculateCovarianceMatrix(returns: number[][]): number[][] {
}
function calculateCovariance(x: number[], y: number[]): number {
if (x.length !== y.length || x.length < 2) return 0;
if (x.length !== y.length || x.length < 2) {return 0;}
const n = x.length;
const meanX = x.reduce((sum, val) => sum + val, 0) / n;
@ -559,7 +559,7 @@ function findMinimumVarianceWeights(
const currentReturn = weights.reduce((sum, w, i) => sum + w * expectedReturns[i], 0);
const returnDiff = targetReturn - currentReturn;
if (Math.abs(returnDiff) < 0.001) break;
if (Math.abs(returnDiff) < 0.001) {break;}
// Adjust weights proportionally to expected returns
const totalExpectedReturn = expectedReturns.reduce((sum, r) => sum + Math.abs(r), 0);

View file

@ -31,8 +31,8 @@ export function fixedRiskPositionSize(params: PositionSizeParams): number {
const { accountSize, riskPercentage, entryPrice, stopLoss, leverage = 1 } = params;
// Input validation
if (accountSize <= 0 || riskPercentage <= 0 || entryPrice <= 0 || leverage <= 0) return 0;
if (entryPrice === stopLoss) return 0;
if (accountSize <= 0 || riskPercentage <= 0 || entryPrice <= 0 || leverage <= 0) {return 0;}
if (entryPrice === stopLoss) {return 0;}
const riskAmount = accountSize * (riskPercentage / 100);
const riskPerShare = Math.abs(entryPrice - stopLoss);
@ -48,7 +48,7 @@ export function kellyPositionSize(params: KellyParams, accountSize: number): num
const { winRate, averageWin, averageLoss } = params;
// Validate inputs
if (averageLoss === 0 || winRate <= 0 || winRate >= 1 || averageWin <= 0) return 0;
if (averageLoss === 0 || winRate <= 0 || winRate >= 1 || averageWin <= 0) {return 0;}
const lossRate = 1 - winRate;
const winLossRatio = averageWin / Math.abs(averageLoss);
@ -72,7 +72,7 @@ export function fractionalKellyPositionSize(
fraction: number = 0.25
): number {
// Input validation
if (fraction <= 0 || fraction > 1) return 0;
if (fraction <= 0 || fraction > 1) {return 0;}
const fullKelly = kellyPositionSize(params, accountSize);
return fullKelly * fraction;
@ -88,7 +88,7 @@ export function volatilityTargetPositionSize(
const { price, volatility, targetVolatility } = params;
// Input validation
if (volatility <= 0 || price <= 0 || targetVolatility <= 0 || accountSize <= 0) return 0;
if (volatility <= 0 || price <= 0 || targetVolatility <= 0 || accountSize <= 0) {return 0;}
const volatilityRatio = targetVolatility / volatility;
const basePositionValue = accountSize * Math.min(volatilityRatio, 2); // Cap at 2x leverage
@ -105,7 +105,7 @@ export function equalWeightPositionSize(
price: number
): number {
// Input validation
if (numberOfPositions <= 0 || price <= 0 || accountSize <= 0) return 0;
if (numberOfPositions <= 0 || price <= 0 || accountSize <= 0) {return 0;}
const positionValue = accountSize / numberOfPositions;
return Math.floor(positionValue / price);
@ -121,7 +121,7 @@ export function atrBasedPositionSize(
atrMultiplier: number = 2,
price: number
): number {
if (atrValue === 0 || price === 0) return 0;
if (atrValue === 0 || price === 0) {return 0;}
const riskAmount = accountSize * (riskPercentage / 100);
const stopDistance = atrValue * atrMultiplier;
@ -143,11 +143,11 @@ export function expectancyPositionSize(
): number {
// Input validation
if (accountSize <= 0 || winRate <= 0 || winRate >= 1 || averageWin <= 0 || averageLoss === 0)
return 0;
{return 0;}
const expectancy = winRate * averageWin - (1 - winRate) * Math.abs(averageLoss);
if (expectancy <= 0) return 0;
if (expectancy <= 0) {return 0;}
// Scale position size based on expectancy relative to average loss
// Higher expectancy relative to risk allows for larger position
@ -167,7 +167,7 @@ export function monteCarloPositionSize(
simulations: number = 1000,
confidenceLevel: number = 0.95
): number {
if (historicalReturns.length === 0) return 0;
if (historicalReturns.length === 0) {return 0;}
const outcomes: number[] = [];
const mean = historicalReturns.reduce((sum, ret) => sum + ret, 0) / historicalReturns.length;
@ -230,7 +230,7 @@ export function sharpeOptimizedPositionSize(
): number {
// Input validation
if (volatility <= 0 || accountSize <= 0 || expectedReturn <= riskFreeRate || maxLeverage <= 0)
return 0;
{return 0;}
// Kelly criterion with Sharpe ratio optimization
const excessReturn = expectedReturn - riskFreeRate;
const kellyFraction = excessReturn / (volatility * volatility);
@ -251,7 +251,7 @@ export function fixedFractionalPositionSize(
price: number
): number {
// Input validation
if (stopLossPercentage <= 0 || price <= 0 || riskPercentage <= 0 || accountSize <= 0) return 0;
if (stopLossPercentage <= 0 || price <= 0 || riskPercentage <= 0 || accountSize <= 0) {return 0;}
const riskAmount = accountSize * (riskPercentage / 100);
const stopLossAmount = price * (stopLossPercentage / 100);
@ -269,7 +269,7 @@ export function volatilityAdjustedPositionSize(
price: number
): number {
// Input validation
if (assetVolatility <= 0 || price <= 0 || targetVolatility <= 0 || accountSize <= 0) return 0;
if (assetVolatility <= 0 || price <= 0 || targetVolatility <= 0 || accountSize <= 0) {return 0;}
const volatilityRatio = targetVolatility / assetVolatility;
const cappedRatio = Math.min(volatilityRatio, 3); // Cap at 3x leverage
@ -286,7 +286,7 @@ export function correlationAdjustedPositionSize(
existingPositions: Array<{ size: number; correlation: number }>,
maxCorrelationRisk: number = 0.3
): number {
if (existingPositions.length === 0 || basePositionSize <= 0) return basePositionSize;
if (existingPositions.length === 0 || basePositionSize <= 0) {return basePositionSize;}
// Calculate portfolio correlation risk
// This should consider the correlation between the new position and existing ones
@ -310,7 +310,7 @@ export function calculatePortfolioHeat(
accountSize: number
): number {
// Input validation
if (accountSize <= 0 || positions.length === 0) return 0;
if (accountSize <= 0 || positions.length === 0) {return 0;}
const totalRisk = positions.reduce((sum, position) => {
// Ensure risk values are positive
@ -331,8 +331,8 @@ export function dynamicPositionSize(
maxDrawdownThreshold: number = 0.1
): number {
// Input validation
if (basePositionSize <= 0 || marketVolatility <= 0 || normalVolatility <= 0) return 0;
if (drawdownLevel < 0 || maxDrawdownThreshold <= 0) return basePositionSize;
if (basePositionSize <= 0 || marketVolatility <= 0 || normalVolatility <= 0) {return 0;}
if (drawdownLevel < 0 || maxDrawdownThreshold <= 0) {return basePositionSize;}
// Volatility adjustment - reduce size when volatility is high
const volatilityAdjustment = Math.min(normalVolatility / marketVolatility, 2); // Cap at 2x
@ -354,7 +354,7 @@ export function liquidityConstrainedPositionSize(
maxVolumePercentage: number = 0.05,
price: number
): number {
if (averageDailyVolume === 0 || price === 0) return 0;
if (averageDailyVolume === 0 || price === 0) {return 0;}
const maxShares = averageDailyVolume * maxVolumePercentage;
@ -372,7 +372,7 @@ export function multiTimeframePositionSize(
baseRiskPercentage: number = 1
): number {
// Input validation
if (accountSize <= 0 || baseRiskPercentage <= 0) return 0;
if (accountSize <= 0 || baseRiskPercentage <= 0) {return 0;}
// Clamp signals to valid range
const clampedShort = Math.max(-1, Math.min(1, shortTermSignal));
@ -396,18 +396,18 @@ export function riskParityPositionSize(
targetRisk: number,
accountSize: number
): number[] {
if (assets.length === 0) return [];
if (assets.length === 0) {return [];}
// Calculate inverse volatility weights
const totalInverseVol = assets.reduce((sum, asset) => {
if (asset.volatility === 0) return sum;
if (asset.volatility === 0) {return sum;}
return sum + 1 / asset.volatility;
}, 0);
if (totalInverseVol === 0) return assets.map(() => 0);
if (totalInverseVol === 0) {return assets.map(() => 0);}
return assets.map(asset => {
if (asset.volatility === 0 || asset.price === 0) return 0;
if (asset.volatility === 0 || asset.price === 0) {return 0;}
// Calculate weight based on inverse volatility
const weight = 1 / asset.volatility / totalInverseVol;
@ -468,7 +468,7 @@ export function optimalFPositionSize(
historicalReturns: number[],
maxIterations: number = 100
): number {
if (historicalReturns.length === 0 || accountSize <= 0) return 0;
if (historicalReturns.length === 0 || accountSize <= 0) {return 0;}
// Convert returns to P&L per unit
const pnlValues = historicalReturns.map(ret => ret * 1000); // Assuming $1000 per unit
@ -512,7 +512,7 @@ export function secureFPositionSize(
historicalReturns: number[],
confidenceLevel: number = 0.95
): number {
if (historicalReturns.length === 0 || accountSize <= 0) return 0;
if (historicalReturns.length === 0 || accountSize <= 0) {return 0;}
// Sort returns to find worst-case scenarios
const sortedReturns = [...historicalReturns].sort((a, b) => a - b);
@ -523,7 +523,7 @@ export function secureFPositionSize(
const maxLoss = Math.abs(worstCaseReturn);
const maxRiskPercentage = 0.02; // Never risk more than 2% on worst case
if (maxLoss === 0) return accountSize * 0.1; // Default to 10% if no historical losses
if (maxLoss === 0) {return accountSize * 0.1;} // Default to 10% if no historical losses
const secureF = Math.min(maxRiskPercentage / maxLoss, 0.25); // Cap at 25%

View file

@ -9,7 +9,7 @@ import { RiskMetrics, treynorRatio } from './index';
* Calculate Value at Risk (VaR) using historical simulation
*/
export function valueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
if (returns.length === 0) return 0;
if (returns.length === 0) {return 0;}
const sortedReturns = [...returns].sort((a, b) => a - b);
const index = Math.floor((1 - confidenceLevel) * sortedReturns.length);
@ -21,12 +21,12 @@ export function valueAtRisk(returns: number[], confidenceLevel: number = 0.95):
* Calculate Conditional Value at Risk (CVaR/Expected Shortfall)
*/
export function conditionalValueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
if (returns.length === 0) return 0;
if (returns.length === 0) {return 0;}
const sortedReturns = [...returns].sort((a, b) => a - b);
const cutoffIndex = Math.floor((1 - confidenceLevel) * sortedReturns.length);
if (cutoffIndex === 0) return sortedReturns[0];
if (cutoffIndex === 0) {return sortedReturns[0];}
const tailReturns = sortedReturns.slice(0, cutoffIndex);
return tailReturns.reduce((sum, ret) => sum + ret, 0) / tailReturns.length;
@ -40,7 +40,7 @@ export function parametricVaR(
confidenceLevel: number = 0.95,
portfolioValue: number = 1
): number {
if (returns.length === 0) return 0;
if (returns.length === 0) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
@ -57,7 +57,7 @@ export function parametricVaR(
* Calculate maximum drawdown
*/
export function maxDrawdown(equityCurve: number[]): number {
if (equityCurve.length < 2) return 0;
if (equityCurve.length < 2) {return 0;}
let maxDD = 0;
let peak = equityCurve[0];
@ -78,11 +78,11 @@ export function maxDrawdown(equityCurve: number[]): number {
* Calculate downside deviation
*/
export function downsideDeviation(returns: number[], targetReturn: number = 0): number {
if (returns.length === 0) return 0;
if (returns.length === 0) {return 0;}
const downsideReturns = returns.filter(ret => ret < targetReturn);
if (downsideReturns.length === 0) return 0;
if (downsideReturns.length === 0) {return 0;}
const sumSquaredDownside = downsideReturns.reduce(
(sum, ret) => sum + Math.pow(ret - targetReturn, 2),
@ -96,14 +96,14 @@ export function downsideDeviation(returns: number[], targetReturn: number = 0):
* Calculate Sharpe ratio
*/
export function sharpeRatio(returns: number[], riskFreeRate: number = 0): number {
if (returns.length < 2) return 0;
if (returns.length < 2) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
if (stdDev === 0) {return 0;}
return (mean - riskFreeRate) / stdDev;
}
@ -172,7 +172,7 @@ export function trackingError(portfolioReturns: number[], benchmarkReturns: numb
* Calculate volatility (standard deviation of returns)
*/
export function volatility(returns: number[]): number {
if (returns.length < 2) return 0;
if (returns.length < 2) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
@ -192,13 +192,13 @@ export function annualizedVolatility(returns: number[], periodsPerYear: number =
* Calculate skewness (measure of asymmetry)
*/
export function skewness(returns: number[]): number {
if (returns.length < 3) return 0;
if (returns.length < 3) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
if (stdDev === 0) {return 0;}
const skew =
returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 3), 0) / returns.length;
@ -210,13 +210,13 @@ export function skewness(returns: number[]): number {
* Calculate kurtosis (measure of tail heaviness)
*/
export function kurtosis(returns: number[]): number {
if (returns.length < 4) return 0;
if (returns.length < 4) {return 0;}
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
if (stdDev === 0) {return 0;}
const kurt =
returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 4), 0) / returns.length;
@ -317,12 +317,12 @@ function getZScore(confidenceLevel: number): number {
};
const key = confidenceLevel.toString();
if (zScores[key]) return zScores[key];
if (zScores[key]) {return zScores[key];}
// For arbitrary confidence levels, use approximation
if (confidenceLevel < 0.5) return -getZScore(1 - confidenceLevel);
if (confidenceLevel < 0.5) {return -getZScore(1 - confidenceLevel);}
if (confidenceLevel >= 0.999) return 3.09; // Cap at 99.9% for numerical stability
if (confidenceLevel >= 0.999) {return 3.09;} // Cap at 99.9% for numerical stability
// Approximation of inverse normal CDF
const y = Math.sqrt(-2.0 * Math.log(1.0 - confidenceLevel));
@ -382,6 +382,6 @@ export function riskAdjustedReturn(
portfolioRisk: number,
riskFreeRate: number = 0
): number {
if (portfolioRisk === 0) return 0;
if (portfolioRisk === 0) {return 0;}
return (portfolioReturn - riskFreeRate) / portfolioRisk;
}

View file

@ -9,7 +9,7 @@ import { OHLCVData } from './index';
* Simple Moving Average
*/
export function sma(values: number[], period: number): number[] {
if (period > values.length) return [];
if (period > values.length) {return [];}
const result: number[] = [];
@ -25,7 +25,7 @@ export function sma(values: number[], period: number): number[] {
* Exponential Moving Average
*/
export function ema(values: number[], period: number): number[] {
if (period > values.length) return [];
if (period > values.length) {return [];}
const result: number[] = [];
const multiplier = 2 / (period + 1);
@ -46,7 +46,7 @@ export function ema(values: number[], period: number): number[] {
* Relative Strength Index (RSI)
*/
export function rsi(prices: number[], period: number = 14): number[] {
if (period >= prices.length) return [];
if (period >= prices.length) {return [];}
const gains: number[] = [];
const losses: number[] = [];
@ -141,7 +141,7 @@ export function bollingerBands(
* Average True Range (ATR)
*/
export function atr(ohlcv: OHLCVData[], period: number = 14): number[] {
if (period >= ohlcv.length) return [];
if (period >= ohlcv.length) {return [];}
const trueRanges: number[] = [];
@ -166,7 +166,7 @@ export function stochastic(
kPeriod: number = 14,
dPeriod: number = 3
): { k: number[]; d: number[] } {
if (kPeriod >= ohlcv.length) return { k: [], d: [] };
if (kPeriod >= ohlcv.length) {return { k: [], d: [] };}
const kValues: number[] = [];
@ -193,7 +193,7 @@ export function stochastic(
* Williams %R
*/
export function williamsR(ohlcv: OHLCVData[], period: number = 14): number[] {
if (period >= ohlcv.length) return [];
if (period >= ohlcv.length) {return [];}
const result: number[] = [];
@ -218,7 +218,7 @@ export function williamsR(ohlcv: OHLCVData[], period: number = 14): number[] {
* Commodity Channel Index (CCI)
*/
export function cci(ohlcv: OHLCVData[], period: number = 20): number[] {
if (period >= ohlcv.length) return [];
if (period >= ohlcv.length) {return [];}
const typicalPrices = ohlcv.map(d => (d.high + d.low + d.close) / 3);
const smaTP = sma(typicalPrices, period);
@ -244,7 +244,7 @@ export function cci(ohlcv: OHLCVData[], period: number = 20): number[] {
* Momentum
*/
export function momentum(prices: number[], period: number = 10): number[] {
if (period >= prices.length) return [];
if (period >= prices.length) {return [];}
const result: number[] = [];
@ -260,7 +260,7 @@ export function momentum(prices: number[], period: number = 10): number[] {
* Rate of Change (ROC)
*/
export function roc(prices: number[], period: number = 10): number[] {
if (period >= prices.length) return [];
if (period >= prices.length) {return [];}
const result: number[] = [];
@ -280,7 +280,7 @@ export function roc(prices: number[], period: number = 10): number[] {
* Money Flow Index (MFI)
*/
export function mfi(ohlcv: OHLCVData[], period: number = 14): number[] {
if (period >= ohlcv.length) return [];
if (period >= ohlcv.length) {return [];}
const typicalPrices = ohlcv.map(d => (d.high + d.low + d.close) / 3);
const moneyFlows = ohlcv.map((d, i) => typicalPrices[i] * d.volume);
@ -317,7 +317,7 @@ export function mfi(ohlcv: OHLCVData[], period: number = 14): number[] {
* On-Balance Volume (OBV)
*/
export function obv(ohlcv: OHLCVData[]): number[] {
if (ohlcv.length === 0) return [];
if (ohlcv.length === 0) {return [];}
const result: number[] = [ohlcv[0].volume];
@ -341,7 +341,7 @@ export function obv(ohlcv: OHLCVData[]): number[] {
* Accumulation/Distribution Line
*/
export function accumulationDistribution(ohlcv: OHLCVData[]): number[] {
if (ohlcv.length === 0) return [];
if (ohlcv.length === 0) {return [];}
const result: number[] = [];
let adLine = 0;
@ -367,7 +367,7 @@ export function accumulationDistribution(ohlcv: OHLCVData[]): number[] {
* Chaikin Money Flow (CMF)
*/
export function chaikinMoneyFlow(ohlcv: OHLCVData[], period: number = 20): number[] {
if (period >= ohlcv.length) return [];
if (period >= ohlcv.length) {return [];}
const adValues: number[] = [];
@ -406,7 +406,7 @@ export function parabolicSAR(
step: number = 0.02,
maxStep: number = 0.2
): number[] {
if (ohlcv.length < 2) return [];
if (ohlcv.length < 2) {return [];}
const result: number[] = [];
let trend = 1; // 1 for uptrend, -1 for downtrend
@ -467,7 +467,7 @@ export function parabolicSAR(
* Aroon Indicator
*/
export function aroon(ohlcv: OHLCVData[], period: number = 14): { up: number[]; down: number[] } {
if (period >= ohlcv.length) return { up: [], down: [] };
if (period >= ohlcv.length) {return { up: [], down: [] };}
const up: number[] = [];
const down: number[] = [];
@ -505,7 +505,7 @@ export function adx(
ohlcv: OHLCVData[],
period: number = 14
): { adx: number[]; plusDI: number[]; minusDI: number[] } {
if (period >= ohlcv.length) return { adx: [], plusDI: [], minusDI: [] };
if (period >= ohlcv.length) {return { adx: [], plusDI: [], minusDI: [] };}
const trueRanges: number[] = [];
const plusDM: number[] = [];
@ -572,7 +572,7 @@ export function adx(
* Volume Weighted Moving Average (VWMA)
*/
export function vwma(ohlcv: OHLCVData[], period: number = 20): number[] {
if (period >= ohlcv.length) return [];
if (period >= ohlcv.length) {return [];}
const result: number[] = [];
@ -607,7 +607,7 @@ export function pivotPoints(ohlcv: OHLCVData[]): Array<{
support2: number;
support3: number;
}> {
if (ohlcv.length === 0) return [];
if (ohlcv.length === 0) {return [];}
const result: Array<{
pivot: number;

View file

@ -242,7 +242,7 @@ export function identifyVolatilityRegimes(
// Classify returns into regimes
const regimeSequence = absReturns.map(absRet => {
for (let i = 0; i < thresholds.length; i++) {
if (absRet <= thresholds[i]) return i;
if (absRet <= thresholds[i]) {return i;}
}
return numRegimes - 1;
});
@ -537,7 +537,7 @@ export function calculateYangZhangVolatility(
* Parkinson volatility estimator
*/
export function parkinsonVolatility(ohlcv: OHLCVData[], annualizationFactor: number = 252): number {
if (ohlcv.length < 2) return 0;
if (ohlcv.length < 2) {return 0;}
const sum = ohlcv.slice(1).reduce((acc, curr) => {
const range = Math.log(curr.high / curr.low);
return acc + range * range;