You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python的PuLP线性优化中添加VaR相关动态约束?

Portfolio VaR Optimization with Dynamic Constraints in PuLP

Core Problem Recap

You need to:

  • Minimize the 1-day VaR (VaR1D) of a portfolio weighted by assets A and B, where VaR1D is defined as the second smallest value in the weighted "Total" column.
  • Ensure the 10-day VaR (VaR10D) — also the second smallest value in its corresponding "Total" column — is improved from -39100 to at least -29100 (since a less negative VaR means smaller potential losses).

Key Challenge: Modeling "Second Smallest Value"

Linear programming tools like PuLP can’t directly handle ranking constraints, so we’ll use binary auxiliary variables to translate the "second smallest" condition into linear constraints. Here’s a practical breakdown:


Step 1: Set Up the Problem & Variables

First, import PuLP and define your optimization problem, plus the weight variables for assets A and B. We’ll assume full investment (weights sum to 1) and no short selling (weights ≥ 0) — adjust these if your strategy allows shorts.

import pulp
import pandas as pd  # Assuming your data is stored in DataFrames

# Initialize the problem: prioritize minimizing VaR1D
prob = pulp.LpProblem("Portfolio_VaR_Optimization", pulp.LpMinimize)

# Define weight variables for assets A and B
w_A = pulp.LpVariable("Weight_A", lowBound=0, upBound=1)
w_B = pulp.LpVariable("Weight_B", lowBound=0, upBound=1)

# Enforce full investment constraint
prob += w_A + w_B == 1, "Full_Investment_Requirement"

Step 2: Model VaR10D Constraints

First, we’ll lock in the VaR10D requirement. Let’s assume you have a DataFrame data_10D with columns A and B (your 10-day return/loss data). We’ll define a variable V10 for VaR10D, plus binary variables to track which "Total" values are ≤ V10.

# Define variable for VaR10D
V10 = pulp.LpVariable("VaR10D")

# Choose a sufficiently large M (adjust based on your data's range)
# M should be bigger than the maximum possible difference between any Total value and V10
M = 1e6

# Binary variables: y_i = 1 if the 10-day Total for scenario i ≤ V10, else 0
y_10D = [pulp.LpVariable(f"y_10D_{i}", cat="Binary") for i in range(len(data_10D))]

# Ensure exactly 2 values are ≤ V10 (guaranteeing V10 is the second smallest)
prob += pulp.lpSum(y_10D) >= 2, "VaR10D_At_Least_Second_Small"
prob += pulp.lpSum(y_10D) <= 2, "VaR10D_At_Most_Second_Small"

# Link each 10-day Total value to V10 and its binary variable
for i in range(len(data_10D)):
    total_i = w_A * data_10D["A"].iloc[i] + w_B * data_10D["B"].iloc[i]
    # If y_i=1, enforce Total_i ≤ V10; if y_i=0, this constraint is automatically satisfied
    prob += total_i <= V10 + M * (1 - y_10D[i]), f"Total10D_Upper_Bound_{i}"
    # If y_i=0, enforce Total_i ≥ V10; if y_i=1, this constraint is automatically satisfied
    prob += total_i >= V10 - M * y_10D[i], f"Total10D_Lower_Bound_{i}"

# Enforce the VaR10D target: no worse than -29100
prob += V10 >= -29100, "VaR10D_Target_Constraint"

Step 3: Model VaR1D as the Objective

Now we’ll define VaR1D (variable V1) as the second smallest value in the 1-day "Total" column, and set it as the objective to minimize. Assume your 1-day data is in data_1D.

# Define variable for VaR1D
V1 = pulp.LpVariable("VaR1D")

# Binary variables for 1-day data: z_i = 1 if the 1-day Total for scenario i ≤ V1, else 0
z_1D = [pulp.LpVariable(f"z_1D_{i}", cat="Binary") for i in range(len(data_1D))]

# Ensure V1 is the second smallest value in the 1-day Total column
prob += pulp.lpSum(z_1D) >= 2, "VaR1D_At_Least_Second_Small"
prob += pulp.lpSum(z_1D) <= 2, "VaR1D_At_Most_Second_Small"

# Link each 1-day Total value to V1 and its binary variable
for i in range(len(data_1D)):
    total_i = w_A * data_1D["A"].iloc[i] + w_B * data_1D["B"].iloc[i]
    prob += total_i <= V1 + M * (1 - z_1D[i]), f"Total1D_Upper_Bound_{i}"
    prob += total_i >= V1 - M * z_1D[i], f"Total1D_Lower_Bound_{i}"

# Set the primary objective: minimize VaR1D
prob += V1, "Minimize_VaR1D_Objective"

Step 4: Solve & Interpret Results

Run the solver and extract the optimal weights and VaR values:

# Solve the problem (msg=0 suppresses solver logs; remove to see detailed output)
prob.solve(pulp.PULP_CBC_CMD(msg=0))

# Print results
print(f"Optimal Weight for Asset A: {pulp.value(w_A):.4f}")
print(f"Optimal Weight for Asset B: {pulp.value(w_B):.4f}")
print(f"Optimized VaR1D: {pulp.value(V1):.2f}")
print(f"Optimized VaR10D: {pulp.value(V10):.2f}")

Key Notes

  • Choosing M: Make sure M is large enough to not artificially constrain your solution. A safe bet is to calculate the maximum possible range of your "Total" values (max(Total) - min(Total)) and add a buffer.
  • Short Selling: If you allow shorting, remove the lowBound=0 parameter from the weight variables.
  • Solver Options: If you need faster performance, consider using commercial solvers like Gurobi or CPLEX (PuLP supports them with additional setup).

内容的提问来源于stack exchange,提问作者Aditya Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 19:47:32