摘要
由于模拟退火的本质是一种暴力搜索,且相比遗传算法和粒子群算法更容易陷入局部最优解,因此本文采用的自适应温控记忆模型优化的思路,目的是使其迭代出来的解尽可能接近全局最优解。自适应温控记忆优化的步骤有三:初始温度、Metropolis准则、回温公式。回温公式的设定需要具体问题具体分析,其目的是增加迭代次数,避免陷入局部最优解,我这里因为例子简单,故没有设定。关于目标函数的建立,同样也是具体问题具体分析,我这里拿的是投资组合的例子。
题目如下:基于135天的收盘价数据,对评价出来的十支最优股票进行组合投资,要求每支股票的仓位不能超过25%。
十支重要股票的评价见博客:数模实操演示|投影寻踪法评价十支最值得购买的股票
数据和代码见百度网盘:
链接:https://pan.baidu.com/s/1p9uSNuDBFubvTNfI03PC_g?pwd=hvaz
提取码:hvaz
目录
Step.1 建立 目标函数
均值公式:
方差公式:
故目标函数为:
Step.2 初始温度
改进的初始温度公式如下:我用的例子求出来的温度接近5万度
首先生成多个权重,代入目标函数,求得多个目标函数值,E(max)和E(min)即是这些值里面的最大值和最小值,P可取[0.9,0.99],本文取0.99。
%% 生成自适应温控的权重
% 初始化
temp_w=zeros(10000,10);
temp_total_get=zeros(10000,1);
temp_total_std1=zeros(10000,1);
temp_total_std2=zeros(10000,1);
temp_total_std=zeros(10000,1);
temp_target=zeros(10000,1);
for h=1:10000
temp_w(h,:)=0.25*rand(1,10);% 生成权重
% 计算10000个投资权重下的期望收益,即日收益率相加
temp_total_get(h)=sum(sum(100*daily_get.*temp_w(h,:)));
% 计算方差公式的第一项
temp_total_std1(h)=sum(temp_w(h,:).^2.*per_std);
% 计算方差公式的第二项
for i=1:m
for j=1:m
temp_total_std2(h)=2*sum(sum(temp_w(h,i)*temp_w(h,j)*per_std(i)*per_std(j))*every_corr(i,j));
end
end
% 两项整合在一起,即为当前投资权重的模型方差
temp_total_std(h)=temp_total_std1(h)+temp_total_std2(h);
% 当前投资权重下的目标函数
temp_target(h)=temp_total_get(h)/temp_total_std(h);
end
% 计算需要用到的自适应温控数值
Emax=max(temp_target);
Emin=min(temp_target);
Eave=mean(temp_target);
Step. 3 Metropolis准则
将Metropolis准则修改为如下公式:
其中:
其他参数如下:
模拟退火部分代码如下:
%% 模拟退火
for L=1:300% 外层迭代次数
sensitivity=[];% 灵敏度分析矩阵,用来记录目标函数值寻优过程中是否收敛
temper=(Emin-Emax)/log(0.99);% 初始温度
iter=100;% 内层循环次数
w1=0.25*rand(1,10);% 产生初始权重1
while sum(w1)>1 | sum(w1)<0.75% 限定权重之和在0.75~1之间
w1=0.25*rand(1,10);
end
% 计算当前权重下的期望收益
total_get1=sum(sum(100*daily_get.*w1));
% 计算模型方差的第一项
total_std11=sum(w1.^2.*per_std);
% 计算模型方差的第二项
for i=1:m
for j=1:m
total_std12=2*sum(sum(w1(i)*w1(j)*per_std(i)*per_std(j))*every_corr(i,j));
end
end
% 当前投资权重下的模型方差
total_std1=total_std11+total_std12;
% 当前投资权重下的目标函数
target1=total_get1/total_std1;
sensitivity=[target1];
% 退火过程
while temper > 0.0001
for j=1:iter
w2=0.25*rand(1,10);% 产生初始权重2
while sum(w2)>1 | sum(w2)<0.75% 限定权重之和在0.75~1之间
w2=0.25*rand(1,10);
end
% 计算当前权重下的期望收益
total_get2=sum(sum(100*daily_get.*w2));
% 计算模型方差的第一项
total_std21=sum(w2.^2.*per_std);
% 计算模型方差的第二项
for i=1:m
for j=1:m
total_std22=2*sum(sum(w2(i)*w2(j)*per_std(i)*per_std(j))*every_corr(i,j));
end
end
% 当前投资权重下的模型方差
total_std2=total_std21+total_std22;
% 当前投资权重下的目标函数
target2=total_get2/total_std2;
if target2 > target1
target1 = target2;
best_w=w2;
else
S=exp(Emin-Eave)/temper;
if exp((target2-target1)/S*temper)>rand(1)
target1 = target2;
best_w=w2;
sensitivity=[sensitivity,target1];
end
end
end
sensitivity=[sensitivity,target1];
temper=temper*0.999
end
end
Step. 4 完整源码
clear;clc
tic
data=xlsread("股票数据.xlsx");
data(1,:)=[];
[n,m]=size(data);
%% 建立Markeowitz均值方差模型,假设投资资金为100块
% 计算日收益率
daily_get=zeros(n-1,m);% 初始化
for i=1:n-1
for j=1:m
daily_get(i,j)=(data(i+1,j)-data(i,j))/data(i,j);
end
end
% 计算方差
per_std=std(data);% 每支股票的方差
every_corr=corrcoef(data);% 各支股票的相关系数
%% 生成自适应温控的权重
% 初始化
temp_w=zeros(10000,10);
temp_total_get=zeros(10000,1);
temp_total_std1=zeros(10000,1);
temp_total_std2=zeros(10000,1);
temp_total_std=zeros(10000,1);
temp_target=zeros(10000,1);
for h=1:10000
temp_w(h,:)=0.25*rand(1,10);% 生成权重
% 计算10000个投资权重下的期望收益,即日收益率相加
temp_total_get(h)=sum(sum(100*daily_get.*temp_w(h,:)));
% 计算方差公式的第一项
temp_total_std1(h)=sum(temp_w(h,:).^2.*per_std);
% 计算方差公式的第二项
for i=1:m
for j=1:m
temp_total_std2(h)=2*sum(sum(temp_w(h,i)*temp_w(h,j)*per_std(i)*per_std(j))*every_corr(i,j));
end
end
% 两项整合在一起,即为当前投资权重的模型方差
temp_total_std(h)=temp_total_std1(h)+temp_total_std2(h);
% 当前投资权重下的目标函数
temp_target(h)=temp_total_get(h)/temp_total_std(h);
end
% 计算需要用到的自适应温控数值
Emax=max(temp_target);
Emin=min(temp_target);
Eave=mean(temp_target);
%% 模拟退火
for L=1:300% 外层迭代次数
sensitivity=[];% 灵敏度分析矩阵,用来记录目标函数值寻优过程中是否收敛
temper=(Emin-Emax)/log(0.99);% 初始温度
iter=100;% 内层循环次数
w1=0.25*rand(1,10);% 产生初始权重1
while sum(w1)>1 | sum(w1)<0.75% 限定权重之和在0.75~1之间
w1=0.25*rand(1,10);
end
% 计算当前权重下的期望收益
total_get1=sum(sum(100*daily_get.*w1));
% 计算模型方差的第一项
total_std11=sum(w1.^2.*per_std);
% 计算模型方差的第二项
for i=1:m
for j=1:m
total_std12=2*sum(sum(w1(i)*w1(j)*per_std(i)*per_std(j))*every_corr(i,j));
end
end
% 当前投资权重下的模型方差
total_std1=total_std11+total_std12;
% 当前投资权重下的目标函数
target1=total_get1/total_std1;
sensitivity=[target1];
% 退火过程
while temper > 0.0001
for j=1:iter
w2=0.25*rand(1,10);% 产生初始权重2
while sum(w2)>1 | sum(w2)<0.75% 限定权重之和在0.75~1之间
w2=0.25*rand(1,10);
end
% 计算当前权重下的期望收益
total_get2=sum(sum(100*daily_get.*w2));
% 计算模型方差的第一项
total_std21=sum(w2.^2.*per_std);
% 计算模型方差的第二项
for i=1:m
for j=1:m
total_std22=2*sum(sum(w2(i)*w2(j)*per_std(i)*per_std(j))*every_corr(i,j));
end
end
% 当前投资权重下的模型方差
total_std2=total_std21+total_std22;
% 当前投资权重下的目标函数
target2=total_get2/total_std2;
if target2 > target1
target1 = target2;
best_w=w2;
else
S=exp(Emin-Eave)/temper;
if exp((target2-target1)/S*temper)>rand(1)
target1 = target2;
best_w=w2;
sensitivity=[sensitivity,target1];
end
end
end
sensitivity=[sensitivity,target1];
temper=temper*0.999
end
end
%% 绘图及展示结果
plot(sensitivity,'--')
title('灵敏度分析');
xlabel('迭代次数');
ylabel('目标函数值');
toc
结果展示