work on calculations

This commit is contained in:
Bojan Kucera 2025-06-04 18:16:16 -04:00
parent 3d910a13e0
commit ab7ef2b678
20 changed files with 1343 additions and 222 deletions

View file

@ -352,38 +352,51 @@ export function estimateHestonParameters(
): HestonParameters {
const n = returns.length;
if (n < 10) {
throw new Error('Need at least 10 observations for Heston parameter estimation');
}
// Initial parameter estimates
let kappa = 2.0; // Mean reversion speed
let theta = 0.04; // Long-term variance
let sigma = 0.3; // Vol of vol
let sigma = 0.3; // Volatility of variance
let rho = -0.5; // Correlation
let v0 = 0.04; // Initial variance
// Calculate sample statistics for initialization
const meanReturn = returns.reduce((sum, r) => sum + r, 0) / n;
const sampleVariance = returns.reduce((sum, r) => sum + Math.pow(r - meanReturn, 2), 0) / (n - 1);
theta = sampleVariance;
v0 = sampleVariance;
let logLikelihood = -Infinity;
for (let iter = 0; iter < maxIterations; iter++) {
const variances: number[] = [v0];
let newLogLikelihood = 0;
// Euler discretization of Heston model
const dt = 1 / 252; // Daily time step
let currentVariance = v0;
for (let t = 1; t < n; t++) {
const prevVar = Math.max(variances[t - 1], 1e-8);
const sqrtVar = Math.sqrt(prevVar);
const dt = 1.0; // Assuming daily data
const prevReturn = returns[t - 1];
// Simulate variance process (simplified)
const dW2 = Math.random() - 0.5; // Should be proper random normal
const newVar = prevVar + kappa * (theta - prevVar) * dt + sigma * sqrtVar * Math.sqrt(dt) * dW2;
variances.push(Math.max(newVar, 1e-8));
// Euler discretization of variance process
const dW1 = Math.random() - 0.5; // Simplified random shock
const dW2 = rho * dW1 + Math.sqrt(1 - rho * rho) * (Math.random() - 0.5);
// Calculate likelihood contribution
const expectedReturn = 0; // Assuming zero drift for simplicity
const variance = prevVar;
const actualReturn = returns[t];
const varianceChange = kappa * (theta - currentVariance) * dt +
sigma * Math.sqrt(Math.max(currentVariance, 0)) * dW2;
newLogLikelihood -= 0.5 * (Math.log(2 * Math.PI) + Math.log(variance) +
Math.pow(actualReturn - expectedReturn, 2) / variance);
currentVariance = Math.max(currentVariance + varianceChange, 0.001);
// Log-likelihood contribution (simplified)
const expectedReturn = meanReturn;
const variance = currentVariance;
if (variance > 0) {
newLogLikelihood -= 0.5 * Math.log(2 * Math.PI * variance);
newLogLikelihood -= 0.5 * Math.pow(returns[t] - expectedReturn, 2) / variance;
}
}
// Check for convergence
@ -393,12 +406,13 @@ export function estimateHestonParameters(
logLikelihood = newLogLikelihood;
// Simple parameter update (in practice, use proper optimization)
kappa = Math.max(0.1, Math.min(10, kappa + 0.01));
theta = Math.max(0.001, Math.min(1, theta + 0.001));
sigma = Math.max(0.01, Math.min(2, sigma + 0.01));
rho = Math.max(-0.99, Math.min(0.99, rho + 0.01));
v0 = Math.max(0.001, Math.min(1, v0 + 0.001));
// Simple parameter updates (in practice, use maximum likelihood estimation)
const learningRate = 0.001;
kappa = Math.max(0.1, Math.min(10, kappa + learningRate));
theta = Math.max(0.001, Math.min(1, theta + learningRate));
sigma = Math.max(0.01, Math.min(2, sigma + learningRate));
rho = Math.max(-0.99, Math.min(0.99, rho + learningRate * 0.1));
v0 = Math.max(0.001, Math.min(1, v0 + learningRate));
}
return {
@ -458,3 +472,48 @@ export function calculateVolatilityRisk(
volatilityVolatility
};
}
/**
* Fix Yang-Zhang volatility calculation
*/
export function calculateYangZhangVolatility(
ohlcv: OHLCVData[],
annualizationFactor: number = 252
): number {
if (ohlcv.length < 2) {
throw new Error('Need at least 2 observations for Yang-Zhang volatility calculation');
}
const n = ohlcv.length;
let overnightSum = 0;
let openToCloseSum = 0;
let rogersSatchellSum = 0;
for (let i = 1; i < n; i++) {
const prev = ohlcv[i - 1];
const curr = ohlcv[i];
// Overnight return (close to open)
const overnight = Math.log(curr.open / prev.close);
overnightSum += overnight * overnight;
// Open to close return
const openToClose = Math.log(curr.close / curr.open);
openToCloseSum += openToClose * openToClose;
// Rogers-Satchell component
const logHighOpen = Math.log(curr.high / curr.open);
const logHighClose = Math.log(curr.high / curr.close);
const logLowOpen = Math.log(curr.low / curr.open);
const logLowClose = Math.log(curr.low / curr.close);
rogersSatchellSum += logHighOpen * logHighClose + logLowOpen * logLowClose;
}
// Yang-Zhang estimator
const k = 0.34 / (1.34 + (n + 1) / (n - 1)); // Drift adjustment factor
const yangZhangVariance = overnightSum / (n - 1) +
k * openToCloseSum / (n - 1) +
(1 - k) * rogersSatchellSum / (n - 1);
return Math.sqrt(yangZhangVariance * annualizationFactor);
}