Python: 优化投资组合中的权重

Python: Optimize weights in portfolio

我有以下带权重的数据框:

df = pd.DataFrame({'a': [0.1, 0.5, 0.1, 0.3], 'b': [0.2, 0.4, 0.2, 0.2], 'c': [0.3, 0.2, 0.4, 0.1],
           'd': [0.1, 0.1, 0.1, 0.7], 'e': [0.2, 0.1, 0.3, 0.4], 'f': [0.7, 0.1, 0.1, 0.1]})

然后我使用以下方法规范化每一行:

df = df.div(df.sum(axis=1), axis=0)

我想优化每行的归一化权重,使权重不小于 0 或大于 0.4。

如果权重大于 0.4,它将被裁剪为 0.4,额外的权重将按比例分配给其他条目(意味着第二大权重将获得更多权重,因此它接近到0.4,如果还有剩余权重,再分配给第三个,以此类推)。

这可以使用“优化”功能来完成吗?

谢谢。

更新:我还想为权重设置一个最小界限。在我原来的问题中,最小权重界限被自动视为零,但是,我想设置一个约束,例如最小权重至少等于 0.05。

不幸的是,我只能找到一个循环解决这个问题。当您 trim 减去多余的重量并按比例重新分配时,减重可能会超过限制。然后他们必须 trim 治疗。并且循环不断重复,直到没有价值超重。重量不足的行也是如此。

# The original data frame. No normalization yet
df = pd.DataFrame(
    {
        "a": [0.1, 0.5, 0.1, 0.3],
        "b": [0.2, 0.4, 0.2, 0.2],
        "c": [0.3, 0.2, 0.4, 0.1],
        "d": [0.1, 0.1, 0.1, 0.7],
        "e": [0.2, 0.1, 0.3, 0.4],
        "f": [0.7, 0.1, 0.1, 0.1],
    }
)


def ensure_min_weight(row: np.array, min_weight: float):
    while True:
        underweight = row < min_weight
        if not underweight.any():
            break

        missing_weight = min_weight * underweight.sum() - row[underweight].sum()
        row[~underweight] -= missing_weight / row[~underweight].sum() * row[~underweight]
        row[underweight] = min_weight


def ensure_max_weight(row: np.array, max_weight: float):
    while True:
        overweight = row > max_weight
        if not overweight.any():
            break

        excess_weight = row[overweight].sum() - (max_weight * overweight.sum())
        row[~overweight] += excess_weight / row[~overweight].sum() * row[~overweight]
        row[overweight] = max_weight


values = df.to_numpy()
normalized = values / values.sum(axis=1)[:, None]
min_weight = 0.15 # just for fun
max_weight = 0.4

for i in range(len(values)):
    row = normalized[i]
    ensure_min_weight(row, min_weight)
    ensure_max_weight(row, max_weight)

# Normalized weight
assert np.isclose(normalized.sum(axis=1), 1).all(), "Normalized weight must sum up to 1"
assert ((min_weight <= normalized) & (normalized <= max_weight)).all(), f"Normalized weight must be between {min_weight} and {max_weight}"
print(pd.DataFrame(normalized, columns=df.columns))

# Raw values
# values = normalized * values.sum(axis=1)[:, None]
# print(pd.DataFrame(values, columns=df.columns))

请注意,如果您的 min_weightmax_weight 不合逻辑,此算法将 运行 进入无限循环:尝试 min_weight = 0.4max_weight = 0.5。您应该在 2 个 ensure 函数中处理这些错误。