投资优化 续

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
蒙特卡洛模拟
S0 =100. 
r =0.05
sigma =0.2 
T =1.0
M= 50
1 =250000
生成 25 万条路 每条有 50个时间步
def gen_monte():
    dt = T / M
    S = np.zeros((M + 1, I), np.float64)
    S[0] = S0
    for t in range(1, M + 1):
        rand = np.random.standard_normal(I)
        rand = (rand-rand.mean())/rand.std()
        S[t] = S[t - 1] * np.exp((r - 0.5 * sigma ** 2) * dt + sigma * np.sqrt(dt) * rand)

$\textstyle\sum_{}w_i\;=1$, $\textstyle w_i\geq\;0$

1
2
3
4
def rand_w(split=5):
    w = np.random.random(split)
    w /= np.sum(w)
    return w

预期收益 252年化

1
np.sum(rets.mean() * weight * 252)

预期投资方差

1
2
var = np.dot(weights.T, np.dot(rets.cov() * 252, weights))
std = np.sqrt(var)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
优化组合 夏普指数即预期投资组合超额收益
投资收益率超过无风险利率 rf 的部分除以预期投资组合标准差为了简单起见我们假定 rf=0 

rf =0
def statistics(weights):
    """
    Return portfolio statistics
    :param weights: weights for different securities in portfolio
    :return:
    pret:float
    expected portfolio return
    pvol:float
    expected portfolio volatility
    pret/pvol:float
    Sharpe ratio for rf=0
    """
    weights = np.array(weights)
    pret = np.sum(rets.mean() * weights) * 252
    pvol = np.sqrt(np.dot(weights.T, np.dot(rets.cov() * 252, weights)))
    return np.array([pret, pvol, pret-rf / pvol])

import scipy.optimize as sco

def min_func_sharpe(weights):
    return -statistics(weights)[2]

# 约束是所有参数(权重)的总和为1。 这可以用minimize函数的约定表达如下
cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})

# 我们还将参数值(权重)限制在0和l之间。 这些值以多个元组组成的一个元组形式提供给最小化函数:
bnds = tuple((0, 1) for x in range(noa))

# 优化函数调用中忽略的唯一输入是起始参数列表(对权重的初始猜测)。我们简单地使用平均分布:
noa * [1. / noa, ]
# [0.2, 0.2, 0.2, 0.2, 0.2]

opts = sco.minimize(min_func_sharpe, noa * [1. / noa, ], method='SLSQP', bounds=bnds, constraints=cons)

statistics(opts['x'].round(3))
# array([ 0.22201418,  0.28871174,  0.76898216])
# 预期收益率约为22.2%. 预期被动率约为28.9%, 得到的最优夏普指数为0.77

有效边界

1
2
3
4
5
6
7
8
9
10
11
目标收益率水平下波动率最小  预期收益tret

trets = np.linspace(0.0, 0.25, 50)
tvols = []
bnds = tuple((0, 1) for x in weights)
for tret in trets:
    cons = ({'type': 'eq', 'fun': lambda x: statistics(x)[0] - tret},
            {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    res = sco.minimize(min_func_port, noa * [1. / noa, ], method='SLSQP', bounds=bnds, constraints=cons)
    tvols.append(res['fun'])
tvols = np.array(tvols)

资本市场线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import scipy.interpolate as sci

ind = np.argmin(tvols)
evols = tvols[ind:]
erets = trets[ind:]

tck = sci.splrep(evols, erets)

# 通过这条数值化路径,最终可以为有效边界定义一个连续可微函数
# 和对应的一阶导数函数df(x):

def f(x):
    """
    有效边界 插值逼近
    """
    return sci.splev(x, tck, der=0)


def df(x):
    """
    有效边界函数的一阶导数。
    """
    return sci.splev(x, tck, der=1)

#我们所寻求的是函数 t(x)=α+b.x,描述穿过风险-收益空间中无风险资产、与有效边界 相切的一条值线

# 定义一个函数,返回给定参数集p=(a,b,x)
def equations(p, rf=0.01):
    eq1 = rf - p[0]
    eq2 = rf + p[1] * p[2] - f(p[2])
    eq3 = p[1]-df(p[2])
    return eq1,eq2,eq3

# 数值优化得到如下的值
#无风险利率为a = 1%时的资本市场线和相切的投资组合
opt=sco.fsolve(equations,[0.01,0.5,0.15])

1
2
3
4
5
6
cons = ({'type': 'eq', 'fun': lambda x: statistics(x)[0] - f(opt[2])},
        {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
res = sco.minimize(min_func_port, noa * [1. / noa, ],
                   method='SLSQP', bounds=bnds, constraints=cons)
res['x'].round(3)
# array([ 0.   ,  0.   ,  0.   ,  0.703,  0.297])