
1. 主程序框架
clear; clc; close all;
nBonds = 50;
bondData = generateBondData(nBonds);
gaOptions = setupGAOptions();
fprintf('开始债券投资组合优化...\n');
[optimalWeights, optimalFitness, exitFlag, output] = ...runBondPortfolioGA(bondData, gaOptions);
analyzeResults(optimalWeights, optimalFitness, bondData, output);
2. 债券数据结构
function bondData = generateBondData(nBonds)rng(42); bondData = struct();bondData.nBonds = nBonds;bondData.yield = 0.02 + 0.08 * rand(nBonds, 1); bondData.duration = 1 + 9 * rand(nBonds, 1); bondData.convexity = 0.5 + 4.5 * rand(nBonds, 1); bondData.creditRating = randi([1, 10], nBonds, 1); bondData.sector = randi([1, 5], nBonds, 1); bondData.liquidity = 0.1 + 0.9 * rand(nBonds, 1); bondData.targetDuration = 5.0; bondData.maxSectorWeight = 0.3; bondData.budget = 1.0;
end
3. 遗传算法配置
function gaOptions = setupGAOptions()gaOptions = optimoptions('ga');gaOptions.PopulationSize = 100; gaOptions.MaxGenerations = 200; gaOptions.CrossoverFraction = 0.8; gaOptions.EliteCount = 2; gaOptions.Display = 'iter';gaOptions.PlotFcn = {@gaplotbestf, @gaplotdistance};gaOptions.FunctionTolerance = 1e-6;gaOptions.StallGenLimit = 50;
end
4. 适应度函数
function fitness = bondPortfolioFitness(weights, bondData)weights = weights(:)';[valid, penalty] = checkConstraints(weights, bondData);if ~validfitness = -1e6 * penalty; return;endportfolioReturn = sum(weights .* bondData.yield');riskPenalty = calculateRiskPenalty(weights, bondData);fitness = portfolioReturn - riskPenalty;
endfunction [valid, penalty] = checkConstraints(weights, bondData)penalty = 0;weightSum = sum(weights);if abs(weightSum - bondData.budget) > 1e-6penalty = penalty + 100 * abs(weightSum - bondData.budget);endnegativeWeights = sum(weights(weights < 0));if negativeWeights < 0penalty = penalty + 1000 * abs(negativeWeights);endmaxWeightViolation = max(0, max(weights) - 0.2);penalty = penalty + 500 * maxWeightViolation;sectorPenalty = checkSectorConstraints(weights, bondData);penalty = penalty + sectorPenalty;valid = (penalty == 0);
endfunction sectorPenalty = checkSectorConstraints(weights, bondData)sectorPenalty = 0;uniqueSectors = unique(bondData.sector);for i = 1:length(uniqueSectors)sectorMask = (bondData.sector == uniqueSectors(i));sectorWeight = sum(weights(sectorMask));if sectorWeight > bondData.maxSectorWeightsectorPenalty = sectorPenalty + 200 * (sectorWeight - bondData.maxSectorWeight);endend
endfunction riskPenalty = calculateRiskPenalty(weights, bondData)portfolioDuration = sum(weights .* bondData.duration');durationDeviation = abs(portfolioDuration - bondData.targetDuration);durationPenalty = 0.1 * durationDeviation;creditRisk = sum(weights .* (bondData.creditRating' / 10));creditPenalty = 0.05 * creditRisk;liquidityPenalty = 0.02 * sum(weights .* (1 - bondData.liquidity'));concentrationPenalty = 0.1 * (sum(weights.^2)); riskPenalty = durationPenalty + creditPenalty + liquidityPenalty + concentrationPenalty;
end
5. 遗传算法执行
function [optimalWeights, optimalFitness, exitFlag, output] = runBondPortfolioGA(bondData, gaOptions)nVars = bondData.nBonds;A = []; b = []; Aeq = ones(1, nVars); beq = bondData.budget;lb = zeros(1, nVars);ub = 0.2 * ones(1, nVars);nonlcon = @(weights) nonLinearConstraints(weights, bondData);fitnessFcn = @(weights) -bondPortfolioFitness(weights, bondData);[optimalWeights, optimalFitness, exitFlag, output] = ...ga(fitnessFcn, nVars, A, b, Aeq, beq, lb, ub, nonlcon, gaOptions);optimalFitness = -optimalFitness;
endfunction [c, ceq] = nonLinearConstraints(weights, bondData)c = []; uniqueSectors = unique(bondData.sector);ceq = zeros(length(uniqueSectors), 1);for i = 1:length(uniqueSectors)sectorMask = (bondData.sector == uniqueSectors(i));sectorWeight = sum(weights(sectorMask));ceq(i) = max(0, sectorWeight - bondData.maxSectorWeight);end
end
6. 结果分析
function analyzeResults(optimalWeights, optimalFitness, bondData, output)fprintf('\n=== 优化结果分析 ===\n');fprintf('最优适应度值: %.4f\n', optimalFitness);fprintf('组合收益率: %.4f%%\n', 100 * sum(optimalWeights .* bondData.yield'));fprintf('组合久期: %.4f 年\n', sum(optimalWeights .* bondData.duration'));nSelected = sum(optimalWeights > 0.001);fprintf('选择的债券数量: %d/%d\n', nSelected, bondData.nBonds);figure('Position', [100, 100, 1200, 800]);subplot(2, 3, 1);bar(optimalWeights(optimalWeights > 0.001));title('债券权重分布');xlabel('债券编号');ylabel('权重');grid on;subplot(2, 3, 2);scatter(bondData.duration, bondData.yield, 50, optimalWeights, 'filled');colorbar;xlabel('久期 (年)');ylabel('收益率');title('债券特征与权重');grid on;subplot(2, 3, 3);uniqueSectors = unique(bondData.sector);sectorWeights = zeros(size(uniqueSectors));for i = 1:length(uniqueSectors)sectorMask = (bondData.sector == uniqueSectors(i));sectorWeights(i) = sum(optimalWeights(sectorMask));endpie(sectorWeights);title('行业权重分布');legend(arrayfun(@(x) sprintf('行业 %d', x), uniqueSectors, 'UniformOutput', false));subplot(2, 3, 4);plot(output.fitness);title('遗传算法收敛曲线');xlabel('代数');ylabel('最佳适应度');grid on;subplot(2, 3, 5);riskContribution = optimalWeights .* bondData.creditRating';riskContribution = riskContribution / sum(riskContribution);bar(riskContribution(optimalWeights > 0.001));title('信用风险贡献');xlabel('债券编号');ylabel('风险贡献比例');grid on;subplot(2, 3, 6);scatter(bondData.liquidity(optimalWeights > 0.001), ...optimalWeights(optimalWeights > 0.001), 50, 'filled');xlabel('流动性指标');ylabel('权重');title('权重 vs 流动性');grid on;fprintf('\n投资组合详细统计:\n');fprintf('信用风险加权平均: %.4f\n', sum(optimalWeights .* bondData.creditRating'));fprintf('流动性加权平均: %.4f\n', sum(optimalWeights .* bondData.liquidity'));fprintf('凸性加权平均: %.4f\n', sum(optimalWeights .* bondData.convexity'));[sortedWeights, sortedIdx] = sort(optimalWeights, 'descend');fprintf('\n前10大持仓:\n');fprintf('债券#\t权重\t收益率\t久期\t信用评级\n');for i = 1:min(10, nSelected)idx = sortedIdx(i);fprintf('%d\t%.4f\t%.4f\t%.2f\t%d\n', ...idx, sortedWeights(i), bondData.yield(idx), ...bondData.duration(idx), bondData.creditRating(idx));end
end
7. 高级功能扩展
function [weights, scores] = multiObjectiveBondOptimization(bondData)fitnessFcn = @(weights) [ -sum(weights .* bondData.yield'), ... calculateTotalRisk(weights, bondData) ]; nVars = bondData.nBonds;A = []; b = [];Aeq = ones(1, nVars);beq = 1;lb = zeros(1, nVars);ub = 0.2 * ones(1, nVars);[weights, scores] = gamultiobj(fitnessFcn, nVars, A, b, Aeq, beq, lb, ub);
endfunction totalRisk = calculateTotalRisk(weights, bondData)durationRisk = abs(sum(weights .* bondData.duration') - bondData.targetDuration);creditRisk = sum(weights .* (bondData.creditRating' / 10));concentrationRisk = sum(weights.^2);totalRisk = 0.1 * durationRisk + 0.05 * creditRisk + 0.1 * concentrationRisk;
end