added more functions

This commit is contained in:
Bojan Kucera 2025-06-04 20:01:39 -04:00
parent a1c82ae0b8
commit cca9ac03dd
8 changed files with 1563 additions and 2 deletions

View file

@ -664,8 +664,293 @@ export function volumeConcentrationHHI(
return hhi * 10000; // Scale to 0-10000 range
}
/**
* Volume Profile
*/
export function volumeProfile(
ohlcv: OHLCVData[],
priceLevels: number
): { [price: number]: number } {
const profile: { [price: number]: number } = {};
// Helper functions
if (ohlcv.length === 0) return profile;
const minPrice = Math.min(...ohlcv.map(candle => candle.low));
const maxPrice = Math.max(...ohlcv.map(candle => candle.high));
const priceRange = maxPrice - minPrice;
const priceIncrement = priceRange / priceLevels;
for (let i = 0; i < priceLevels; i++) {
const priceLevel = minPrice + i * priceIncrement;
profile[priceLevel] = 0;
}
for (const candle of ohlcv) {
const typicalPrice = (candle.high + candle.low + candle.close) / 3;
const priceLevel = minPrice + Math.floor((typicalPrice - minPrice) / priceIncrement) * priceIncrement;
if (profile[priceLevel] !== undefined) {
profile[priceLevel] += candle.volume;
}
}
return profile;
}
/**
* Delta Neutral Hedging Ratio
*/
export function deltaNeutralHedgingRatio(
optionDelta: number
): number {
return -optionDelta;
}
/**
* Gamma Scalping Range
*/
export function gammaScalpingRange(
gamma: number,
theta: number,
timeIncrement: number
): number {
return Math.sqrt(2 * Math.abs(theta) * timeIncrement / gamma);
}
/**
* Optimal Order Size (based on market impact)
*/
export function optimalOrderSize(
alpha: number,
lambda: number
): number {
return alpha / (2 * lambda);
}
/**
* Adverse Selection Component of the Spread
*/
export function adverseSelectionComponent(
probabilityOfInformedTrader: number,
spread: number
): number {
return probabilityOfInformedTrader * spread;
}
/**
* Inventory Risk Component of the Spread
*/
export function inventoryRiskComponent(
inventoryHoldingCost: number,
orderArrivalRate: number
): number {
return inventoryHoldingCost * Math.sqrt(orderArrivalRate);
}
/**
* Quote Age
*/
export function quoteAge(
lastUpdate: Date
): number {
return Date.now() - lastUpdate.getTime();
}
/**
* Trade Classification (Lee-Ready algorithm)
*/
export function tradeClassification(
tradePrice: number,
bidPrice: number,
askPrice: number,
previousTradePrice: number
): 'buy' | 'sell' | 'unknown' {
if (tradePrice > askPrice) {
return 'buy';
} else if (tradePrice < bidPrice) {
return 'sell';
} else if (tradePrice >= previousTradePrice) {
return 'buy';
} else {
return 'sell';
}
}
/**
* Tick Rule
*/
export function tickRule(
tradePrice: number,
previousTradePrice: number
): 'buy' | 'sell' | 'unknown' {
if (tradePrice > previousTradePrice) {
return 'buy';
} else if (tradePrice < previousTradePrice) {
return 'sell';
} else {
return 'unknown';
}
}
/**
* Amihud's Lambda Variation with High-Frequency Data
*/
export function amihudIlliquidityHFT(
priceChanges: number[],
dollarVolumes: number[],
timeDeltas: number[]
): number {
let illiquiditySum = 0;
let validTrades = 0;
for (let i = 0; i < priceChanges.length; i++) {
if (dollarVolumes[i] > 0 && timeDeltas[i] > 0) {
illiquiditySum += Math.abs(priceChanges[i]) / (dollarVolumes[i] * timeDeltas[i]);
validTrades++;
}
}
return validTrades > 0 ? illiquiditySum / validTrades : 0;
}
/**
* Parkinson Volatility
*/
export function parkinsonVolatility(
highPrices: number[],
lowPrices: number[]
): number {
if (highPrices.length !== lowPrices.length || highPrices.length < 2) return 0;
let sumSquaredLogHL = 0;
for (let i = 0; i < highPrices.length; i++) {
const logHL = Math.log(highPrices[i] / lowPrices[i]);
sumSquaredLogHL += logHL * logHL;
}
const parkinsonVariance = (1 / (4 * highPrices.length * Math.log(2))) * sumSquaredLogHL;
return Math.sqrt(parkinsonVariance);
}
/**
* Garman-Klass Volatility
*/
export function garmanKlassVolatility(
openPrices: number[],
highPrices: number[],
lowPrices: number[],
closePrices: number[]
): number {
if (openPrices.length !== highPrices.length || openPrices.length !== lowPrices.length || openPrices.length !== closePrices.length || openPrices.length < 2) return 0;
let sumSquaredTerm1 = 0;
let sumSquaredTerm2 = 0;
let sumSquaredTerm3 = 0;
for (let i = 0; i < openPrices.length; i++) {
const logHO = Math.log(highPrices[i] / openPrices[i]);
const logLO = Math.log(lowPrices[i] / openPrices[i]);
const logCO = Math.log(closePrices[i] / openPrices[i]);
sumSquaredTerm1 += 0.5 * (logHO * logHO + logLO * logLO);
sumSquaredTerm2 += - (2 * Math.log(2) - 1) * (logCO * logCO);
}
const garmanKlassVariance = (1 / openPrices.length) * (sumSquaredTerm1 + sumSquaredTerm2);
return Math.sqrt(garmanKlassVariance);
}
/**
* Yang-Zhang Volatility
*/
export function yangZhangVolatility(
openPrices: number[],
highPrices: number[],
lowPrices: number[],
closePrices: number[],
previousClosePrices: number[]
): number {
if (openPrices.length !== highPrices.length || openPrices.length !== lowPrices.length || openPrices.length !== closePrices.length || openPrices.length !== previousClosePrices.length || openPrices.length < 2) return 0;
const k = 0.34 / (1.34 + (openPrices.length + 1) / (previousClosePrices.length - 1));
let sumSquaredTerm1 = 0;
let sumSquaredTerm2 = 0;
let sumSquaredTerm3 = 0;
for (let i = 0; i < openPrices.length; i++) {
const overnightReturn = Math.log(openPrices[i] / previousClosePrices[i]);
const openToHigh = Math.log(highPrices[i] / openPrices[i]);
const openToLow = Math.log(lowPrices[i] / openPrices[i]);
const closeToOpen = Math.log(closePrices[i] / openPrices[i]);
sumSquaredTerm1 += overnightReturn * overnightReturn;
sumSquaredTerm2 += openToHigh * openToHigh;
sumSquaredTerm3 += openToLow * openToLow;
}
const variance = sumSquaredTerm1 + k * sumSquaredTerm2 + (1 - k) * sumSquaredTerm3;
return Math.sqrt(variance);
}
/**
* Volume Order Imbalance (VOI)
*/
export function volumeOrderImbalance(
buyVolumes: number[],
sellVolumes: number[]
): number[] {
if (buyVolumes.length !== sellVolumes.length) return [];
const voi: number[] = [];
for (let i = 0; i < buyVolumes.length; i++) {
voi.push(buyVolumes[i] - sellVolumes[i]);
}
return voi;
}
/**
* Cumulative Volume Delta (CVD)
*/
export function cumulativeVolumeDelta(
buyVolumes: number[],
sellVolumes: number[]
): number[] {
if (buyVolumes.length !== sellVolumes.length) return [];
const cvd: number[] = [];
let cumulativeDelta = 0;
for (let i = 0; i < buyVolumes.length; i++) {
cumulativeDelta += buyVolumes[i] - sellVolumes[i];
cvd.push(cumulativeDelta);
}
return cvd;
}
/**
* Market Order Ratio
*/
export function marketOrderRatio(
marketOrders: number[],
limitOrders: number[]
): number[] {
if (marketOrders.length !== limitOrders.length) return [];
const ratios: number[] = [];
for (let i = 0; i < marketOrders.length; i++) {
const totalOrders = marketOrders[i] + limitOrders[i];
ratios.push(totalOrders > 0 ? marketOrders[i] / totalOrders : 0);
}
return ratios;
}
/**
* Helper function to calculate the average of an array of numbers
*/
function average(arr: number[]): number {
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;