added more functions
This commit is contained in:
parent
a53d8d13ca
commit
a1c82ae0b8
7 changed files with 648 additions and 177 deletions
|
|
@ -389,33 +389,188 @@ export function performanceAttribution(
|
|||
}
|
||||
|
||||
/**
|
||||
* Calculate efficient frontier points
|
||||
* Calculate Efficient Frontier points
|
||||
*/
|
||||
export function calculateEfficientFrontier(
|
||||
expectedReturns: number[],
|
||||
covarianceMatrix: number[][],
|
||||
numPoints: number = 100
|
||||
): Array<{ return: number; volatility: number; sharpeRatio: number; weights: number[] }> {
|
||||
returns: number[][], // Array of return series for each asset
|
||||
symbols: string[],
|
||||
riskFreeRate: number = 0.02,
|
||||
numPoints: number = 50
|
||||
): Array<{
|
||||
weights: number[];
|
||||
expectedReturn: number;
|
||||
volatility: number;
|
||||
sharpeRatio: number;
|
||||
}> {
|
||||
if (returns.length !== symbols.length || returns.length < 2) return [];
|
||||
|
||||
const n = returns.length;
|
||||
const results: Array<{ weights: number[]; expectedReturn: number; volatility: number; sharpeRatio: number; }> = [];
|
||||
|
||||
// Calculate expected returns and covariance matrix
|
||||
const expectedReturns = returns.map(assetReturns =>
|
||||
assetReturns.reduce((sum, ret) => sum + ret, 0) / assetReturns.length
|
||||
);
|
||||
|
||||
const covarianceMatrix = calculateCovarianceMatrix(returns);
|
||||
|
||||
// Generate target returns from min to max expected return
|
||||
const minReturn = Math.min(...expectedReturns);
|
||||
const maxReturn = Math.max(...expectedReturns);
|
||||
const returnStep = (maxReturn - minReturn) / (numPoints - 1);
|
||||
|
||||
const frontierPoints: Array<{ return: number; volatility: number; sharpeRatio: number; weights: number[] }> = [];
|
||||
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
const targetReturn = minReturn + i * returnStep;
|
||||
|
||||
// Simplified optimization for target return
|
||||
// In production, use proper constrained optimization
|
||||
const result = markowitzOptimization(expectedReturns, covarianceMatrix);
|
||||
// Find minimum variance portfolio for target return using quadratic programming (simplified)
|
||||
const weights = findMinimumVarianceWeights(expectedReturns, covarianceMatrix, targetReturn);
|
||||
|
||||
frontierPoints.push({
|
||||
return: targetReturn,
|
||||
volatility: result.volatility,
|
||||
sharpeRatio: result.sharpeRatio,
|
||||
weights: result.weights
|
||||
});
|
||||
if (weights && weights.length === n) {
|
||||
const portfolioReturn = weights.reduce((sum, w, j) => sum + w * expectedReturns[j], 0);
|
||||
const portfolioVariance = calculatePortfolioVariance(weights, covarianceMatrix);
|
||||
const portfolioVolatility = Math.sqrt(portfolioVariance);
|
||||
const sharpeRatio = portfolioVolatility > 0 ? (portfolioReturn - riskFreeRate) / portfolioVolatility : 0;
|
||||
|
||||
results.push({
|
||||
weights,
|
||||
expectedReturn: portfolioReturn,
|
||||
volatility: portfolioVolatility,
|
||||
sharpeRatio
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return frontierPoints;
|
||||
return results.sort((a, b) => a.volatility - b.volatility);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Minimum Variance Portfolio
|
||||
*/
|
||||
export function findMinimumVariancePortfolio(
|
||||
returns: number[][],
|
||||
symbols: string[]
|
||||
): PortfolioOptimizationResult | null {
|
||||
if (returns.length !== symbols.length || returns.length < 2) return null;
|
||||
|
||||
const covarianceMatrix = calculateCovarianceMatrix(returns);
|
||||
const n = returns.length;
|
||||
|
||||
// For minimum variance portfolio: w = (Σ^-1 * 1) / (1' * Σ^-1 * 1)
|
||||
// Simplified implementation using equal weights as starting point
|
||||
const weights = new Array(n).fill(1 / n);
|
||||
|
||||
// Iterative optimization (simplified)
|
||||
for (let iter = 0; iter < 100; iter++) {
|
||||
const gradient = calculateVarianceGradient(weights, covarianceMatrix);
|
||||
const stepSize = 0.01;
|
||||
|
||||
// Update weights
|
||||
for (let i = 0; i < n; i++) {
|
||||
weights[i] -= stepSize * gradient[i];
|
||||
}
|
||||
|
||||
// Normalize weights to sum to 1
|
||||
const weightSum = weights.reduce((sum, w) => sum + w, 0);
|
||||
for (let i = 0; i < n; i++) {
|
||||
weights[i] = Math.max(0, weights[i] / weightSum);
|
||||
}
|
||||
}
|
||||
|
||||
const expectedReturns = returns.map(assetReturns =>
|
||||
assetReturns.reduce((sum, ret) => sum + ret, 0) / assetReturns.length
|
||||
);
|
||||
|
||||
const portfolioReturn = weights.reduce((sum, w, i) => sum + w * expectedReturns[i], 0);
|
||||
const portfolioVariance = calculatePortfolioVariance(weights, covarianceMatrix);
|
||||
const portfolioVolatility = Math.sqrt(portfolioVariance);
|
||||
const sharpeRatio = portfolioVolatility > 0 ? portfolioReturn / portfolioVolatility : 0;
|
||||
|
||||
return {
|
||||
weights,
|
||||
expectedReturn: portfolioReturn,
|
||||
volatility: portfolioVolatility,
|
||||
sharpeRatio,
|
||||
symbols
|
||||
};
|
||||
}
|
||||
|
||||
// Helper functions for portfolio optimization
|
||||
|
||||
function calculateCovarianceMatrix(returns: number[][]): number[][] {
|
||||
const n = returns.length;
|
||||
const matrix: number[][] = [];
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
matrix[i] = [];
|
||||
for (let j = 0; j < n; j++) {
|
||||
matrix[i][j] = calculateCovariance(returns[i], returns[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
function calculateCovariance(x: number[], y: number[]): number {
|
||||
if (x.length !== y.length || x.length < 2) return 0;
|
||||
|
||||
const n = x.length;
|
||||
const meanX = x.reduce((sum, val) => sum + val, 0) / n;
|
||||
const meanY = y.reduce((sum, val) => sum + val, 0) / n;
|
||||
|
||||
return x.reduce((sum, val, i) => sum + (val - meanX) * (y[i] - meanY), 0) / (n - 1);
|
||||
}
|
||||
|
||||
// calculatePortfolioVariance is already exported above
|
||||
|
||||
function calculateVarianceGradient(weights: number[], covarianceMatrix: number[][]): number[] {
|
||||
const n = weights.length;
|
||||
const gradient: number[] = [];
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
let grad = 0;
|
||||
for (let j = 0; j < n; j++) {
|
||||
grad += 2 * weights[j] * covarianceMatrix[i][j];
|
||||
}
|
||||
gradient[i] = grad;
|
||||
}
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
function findMinimumVarianceWeights(
|
||||
expectedReturns: number[],
|
||||
covarianceMatrix: number[][],
|
||||
targetReturn: number
|
||||
): number[] | null {
|
||||
const n = expectedReturns.length;
|
||||
|
||||
// Simplified implementation - in practice would use quadratic programming solver
|
||||
// Start with equal weights and adjust
|
||||
const weights = new Array(n).fill(1 / n);
|
||||
|
||||
// Iterative adjustment to meet target return constraint
|
||||
for (let iter = 0; iter < 50; iter++) {
|
||||
const currentReturn = weights.reduce((sum, w, i) => sum + w * expectedReturns[i], 0);
|
||||
const returnDiff = targetReturn - currentReturn;
|
||||
|
||||
if (Math.abs(returnDiff) < 0.001) break;
|
||||
|
||||
// Adjust weights proportionally to expected returns
|
||||
const totalExpectedReturn = expectedReturns.reduce((sum, r) => sum + Math.abs(r), 0);
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const adjustment = (returnDiff * Math.abs(expectedReturns[i])) / totalExpectedReturn;
|
||||
weights[i] = Math.max(0, weights[i] + adjustment * 0.1);
|
||||
}
|
||||
|
||||
// Normalize weights
|
||||
const weightSum = weights.reduce((sum, w) => sum + w, 0);
|
||||
if (weightSum > 0) {
|
||||
for (let i = 0; i < n; i++) {
|
||||
weights[i] /= weightSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return weights;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue