MPT, modern portfolio theory。现在资产配置理论。
理论很简单。
假设每个资产的收益率是一个随机变量 x i x_i xi。既然是随机变量,当然就会有均值和标准差。
如果资产数量不是只有一个的话(一个的话,做什么资产配置),也就是存在有多个随机变量,随机变量之间当然就会有协方差。
资产配置的目的就是,找到一种较好的资产配置组合,使得达到预期的收益率的情况下,风险最小。
这句话其实就已经告诉了我们这个模型该如何建立。
E ( r w ) = ∑ i = 1 n w i r i E(r_w) = \sum_{i=1}^{n}{w_ir_i} E(rw)=i=1∑nwiri
V a r ( r w ) = ∑ i = 1 n ∑ j = 1 n w i w j C o v ( r i , r j ) Var(r_w) = \sum_{i=1}^{n}{\sum_{j=1}^{n}{w_iw_jCov(r_i,r_j)}} Var(rw)=i=1∑nj=1∑nwiwjCov(ri,rj)
我们根据上面的加粗文字就可以知道模型应该为:
min w V a r ( r w ) s . t . E ( r w ) = μ ∑ i = 1 n w i = 1 \min_{w}{Var(r_w)}\\ \mathrm{ s.t. } \qquad E(r_w) = \mu \\ \sum_{i=1}^{n}{w_i} = 1 wminVar(rw)s.t.E(rw)=μi=1∑nwi=1
优化的话,这里使用粒子群优化的方式。
其实一般最常用的是拉格朗日乘子法。
(拉格朗日乘子法是优化的最基础的算法啦,大家直接查就好了。这里我直接截的附件中的图)
粒子群的思路很简单,就是给一个初始化的向量。然后,每个粒子记住自己的历史最优解和全局的最优解。每次的迭代往这两个方向上加权的偏移就好了。
假设有n个产品。cor是它们之间的协方差矩阵。然后,很明显在这个社会中风险越高(方差越大),那么这个产品的收益率越高。
给一个预期的期望收益。由于数据都是随机生成的。这里我们就直接取用在最大和0之间的alpha比例的数值(资产配置不可能高过最高均值收益)
n = 10
alpha = 0.8
cor = np.random.random((n, n))
mu = np.diag(cor)
EV = alpha * max(mu)
def randOne(n):
a = np.random.random(n)
return a / np.sum(a)
def ReCheck(newData, n=10, oriData=None):
for i in range(len(newData)):
if oriData is None:
while np.matmul(newData[i], mu.T) < EV:
newData[i] = randOne(n)
elif np.matmul(newData[i], mu.T) < EV:
newData[i] = oriData[i]
return newData
m = 30
por = np.random.random((m, n))
por = por / np.sum(por, axis=1)[:, np.newaxis] # 归一
por = ReCheck(por)
w, c1, c2 = 0.6, 2, 2
v = np.random.random((m, n))
MTime = 500
def CalVal(por):
tmp = 0
for i in range(len(por)):
for j in range(len(por)):
tmp += por[i] * por[j] * cor[i][j]
return tmp
t = 0
while t < MTime:
t += 1
val = np.zeros(m)
for i, p in enumerate(por):
val[i] = CalVal(p)
if t == 1:
local_Min_por = por.copy()
local_Min_val = val.copy()
else:
for i in range(m):
if val[i] < local_Min_val[i]:
local_Min_val[i] = val[i]
local_Min_por[i] = por[i]
global_Min_index = np.argmin(local_Min_val)
global_Min_por = local_Min_por[global_Min_index]
global_Min_val = local_Min_val[global_Min_index]
for i in range(m):
v[i] = w * v[i] + c1 * np.random.rand() * local_Min_por[i] + c2 * np.random.rand() * global_Min_por
new_por = por + v[i]
new_por = new_por / np.sum(new_por, axis=1)[:, np.newaxis]
por = ReCheck(new_por, oriData=por)
蒙特卡洛采用完全随机的方式。这里同样的设置总的次数相等。然后再通过不满足对应的期望收益标准的方案就去掉的方式。
random_por = np.random.random((m * MTime, n))
random_por = random_por / np.sum(random_por, axis=1)[:, np.newaxis] # 归一random_
def ReCheck_abandon(newData):
ansData = newData.copy()
i, j = 0, 0
while i < len(newData):
if np.matmul(newData[i], mu.T) >= EV:
ansData[j] = newData[i]
j += 1
i += 1
return ansData[:j]
random_por = ReCheck_abandon(random_por)
val = np.zeros(m * MTime)[:len(random_por)]
for i, p in enumerate(random_por):
val[i] = CalVal(p)
plt.plot(val)
plt.plot([global_Min_val for i in range(len(val))])