蒙特卡罗方法用于投资组合优化

3
我想创建一个n列的向量,每个向量长度为x,需要满足以下条件:
i) 每个向量的第i个分量(例如,x[i])都有最小值和最大值。最小值和最大值以百分比表示。
ii) 每列的总和为1。
iii) 我想确保平均地采样整个空间。
我编写了以下程序,名为'gen_port',它接受两个向量,包含向量的下限和上限,以及要生成的随机向量数(例如,N)。
def gen_port (lower_bound, upper_bound, number):
    import random
    # Given vector description of minimum and maximum, return an array of 'number' vectors,  each of which sums to 100%
    # We generate RVs, scale them by upper and lower bounds, then normalize. 
    values = np.random.random((len(lower_bound),number))   # create big array of RVs. 
    for n in range (0,number):
        for i in range (0, len(lower_bound)):
            values[i,n] = np.float(lower_bound[i]+ values[i,n]*(upper_bound[i]-lower_bound[i]))  # scale
    return values

因此,例如,如果我正在生成由以下向量描述的10列向量:
lower_bound = [0.0,0.0,0.0,0.0]
upper_bound = [0.50,0.50,0.50,0.50] 
gen_ports(lower_bound, upper_bound, 10)

[Out]
array([[ 0.15749895,  0.21279324,  0.35603417,  0.27367365],
   [ 0.2970716 ,  0.48189552,  0.04709743,  0.17393545],
   [ 0.20367186,  0.47925996,  0.21349772,  0.10357047],
   [ 0.29129967,  0.15936119,  0.26925573,  0.28008341],
   [ 0.11058273,  0.2699138 ,  0.39068379,  0.22881968],
   [ 0.21286622,  0.39058314,  0.33895212,  0.05759852],
   [ 0.18726399,  0.37648587,  0.32808714,  0.108163  ],
   [ 0.03839954,  0.24170767,  0.40299362,  0.31689917],
   [ 0.35782691,  0.31928643,  0.24712695,  0.0757597 ],
   [ 0.25595576,  0.08776559,  0.16836131,  0.48791733]])

然而,如果下界和上界的值不相同,我希望能够填充向量。例如,如果:
[In]:
lower_bound = [0.0,0.25,0.25,0.0]
upper_bound = [0.50,0.50,0.75,1.0] 
gen_ports(lower_bound, upper_bound, 100000)

结果不等于1(以下仅包括10个样本):

[Out]:
array([[ 0.16010701,  0.31426425,  0.38776233,  0.1378664 ],
   [ 0.00360632,  0.37343983,  0.57538205,  0.0475718 ],
   [ 0.28273906,  0.2228893 ,  0.1998151 ,  0.29455654],
   [ 0.06602521,  0.21386937,  0.49896407,  0.22114134],
   [ 0.17785613,  0.33885919,  0.25276605,  0.23051864],
   [ 0.07223014,  0.19988808,  0.16398971,  0.56389207],
   [ 0.14320281,  0.14400242,  0.18276333,  0.53003144],
   [ 0.04962725,  0.2578919 ,  0.19029586,  0.50218499],
   [ 0.01619681,  0.21040566,  0.30615235,  0.46724517],
   [ 0.10905285,  0.23641745,  0.40660215,  0.24792755]])

我希望能够生成100,000个方案,以便在下限和上限定义的空间内均匀采样。但是我遇到了困难,因为当前函数会在向量被下限和上限翻译后进行归一化处理。
所以,我有一个明显的第一个问题 - 如何修改常规程序以适应大多数情况?
另外:
i)这种方法正确吗?例如,我通过此实现引入了任何偏差吗?
ii)是否有更快和/或更具“Python风格”的方法来完成此操作?对于n = 1,000,000和x = 35,需要大约15分钟。
2个回答

1
如果您不需要允许任何下限/上限要求(或者,如果下限始终为0,上限始终为1),那么答案将是众所周知的狄利克雷分布。

https://en.wikipedia.org/wiki/Dirichlet_distribution

在链接中有Python采样代码。对于最简单的情况,其中$\vec{a}=1$,还有非常简单的方法来采样狄利克雷分布,如果需要,我可以找到它。但是边界会引入额外的问题...
更新
我相信你可以使用拒绝采样,从狄利克雷分布中采样并拒绝任何不适合区间的内容,但我想效率可能会相当低。
第二次更新
找到了一个链接,其中包含Python狄利克雷采样,用于所有$\alpha=1$的情况。 生成N个均匀随机数,总和为M

谢谢,我会检查这个。正如你所指出的,我需要允许不同的上限和下限。但这是一个有用的第一步。 - GPB

-2

除非你有绝对需要使用蒙特卡罗模拟的理由,比如这是作业,否则更高效的方法是使用数值优化器,例如:

from scipy.optimize import minimize

def find_allocations(prices):
    """Find optimal allocations for a portfolio, optimizing Sharpe ratio.

    Parameters
    ----------
        prices: DataFrame, daily prices for each stock in portfolio

    Returns
    -------
        allocs: optimal allocations, as fractions that sum to 1.0
    """
    def sharpe_ratio(allocs):
        # 1e7 is arbitrary for starting portfolio value
        port_vals = (prices / prices.ix[0]) * allocs * 1e7
        returns = port_vals.pct_change()
        avg_daily_ret = returns.means(0)
        std_daily_ret = returns.std(0)
        return -(252 ** 0.5) * avg_daily_ret / std_daily_ret

    n = prices.shape[1]
    x0 = [1.0 / n] * n
    bounds = [(0.0, 1.0)] * n
    constraints = ({'type': 'eq', 'fun': lambda x: 1.0 - np.sum(np.abs(x))})
    allocs = minimize(sharpe_ratio, x0, method = 'SLSQP', 
                      bounds = bounds, constraints = constraints)
    return allocs.x

请注意,这是在最小化夏普比的负值,因此实际上是在最大化夏普比,正如您所希望的那样。根据您想要优化的内容,某些目标函数(例如最小方差限制返回相同的等额分配)具有解析解决方案。

不,这不是“作业”。我也不是在试图优化某些回报问题。我正在尝试填充一个潜在的组合表面,其中每个资产与其他资产具有独特的相关性。如果有人能回答我的问题,而不是猜测我试图解决什么问题,那就更好了。 - GPB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接