| Folio.py |
# Modeling a small LP problem to perform portfolio optimization.
#
# (C) 2025 Fair Isaac Corporation
import xpress as xp
# Problem data
NSHARES = 10
RET = [5, 17, 26, 12, 8, 9, 7, 6, 31, 21]
RISK = [1, 2, 3, 8, 9]
NA = [0, 1, 2, 3]
p = xp.problem(name="Folio")
# VARIABLES.
frac = p.addVariables(NSHARES, ub=0.3, name="frac")
# CONSTRAINTS.
# Limit the percentage of high-risk values.
p.addConstraint(xp.Sum(frac[i] for i in RISK) <= 1/3)
# Minimum amount of North-American values.
p.addConstraint(xp.Sum(frac[i] for i in NA) >= 0.5)
# Spend all the capital.
p.addConstraint(xp.Sum(frac) == 1)
# Objective: maximize total return.
p.setObjective(xp.Sum(frac[i] * RET[i] for i in range(NSHARES)), sense=xp.maximize)
# Solve.
p.optimize()
# Print problem status.
print(f"Problem status: \n\t Solve status: {p.attributes.solvestatus.name} \n\t Sol status: \
{p.attributes.solstatus.name}")
# Solution printing.
print("Total return:", p.attributes.objval)
sol = p.getSolution(frac)
for i in range(NSHARES):
print(f"{frac[i].name} : {sol[i]*100:.2f} %")
|
|
| FolioMIP1.py |
# Modeling a small MIP problem to perform portfolio optimization.
# - Limiting the total number of assets.
#
# (C) 2025 Fair Isaac Corporation
import xpress as xp
# Problem data
MAXNUM = 4
NSHARES = 10
RET = [5, 17, 26, 12, 8, 9, 7, 6, 31, 21]
RISK = [1, 2, 3, 8, 9]
NA = [0, 1, 2, 3]
p = xp.problem(name="Folio")
# VARIABLES.
frac = p.addVariables(NSHARES, ub=0.3, name="frac")
buy = p.addVariables(NSHARES, vartype=xp.binary, name="buy")
# CONSTRAINTS.
# Limit the percentage of high-risk values.
p.addConstraint(xp.Sum(frac[i] for i in RISK) <= 1/3)
# Minimum amount of North-American values.
p.addConstraint(xp.Sum(frac[i] for i in NA) >= 0.5)
# Spend all the capital.
p.addConstraint(xp.Sum(frac) == 1)
# Limit the total number of assets.
p.addConstraint(xp.Sum(buy) <= MAXNUM)
# Linking the variables.
p.addConstraint(frac[i] <= buy[i] for i in range(NSHARES))
# Objective: maximize total return.
p.setObjective(xp.Sum(frac[i] * RET[i] for i in range(NSHARES)), sense=xp.maximize)
# Solve.
p.optimize()
# Print problem status.
print(f"Problem status: \n\t Solve status: {p.attributes.solvestatus.name} \n\t Sol status: \
{p.attributes.solstatus.name}")
# Solution printing.
print("Total return:", p.attributes.objval)
sol = p.getSolution(frac)
for i in range(NSHARES):
print(f"{frac[i].name} : {sol[i]*100:.2f} %")
|
|
| FolioMIP2.py |
# Modeling a small MIP problem to perform portfolio optimization.
# - Imposing a minimum investment per share.
#
# (C) 2025 Fair Isaac Corporation
import xpress as xp
# Problem data
MAXNUM = 4
NSHARES = 10
RET = [5, 17, 26, 12, 8, 9, 7, 6, 31, 21]
RISK = [1, 2, 3, 8, 9]
NA = [0, 1, 2, 3]
p = xp.problem(name="Folio")
# VARIABLES.
frac = p.addVariables(NSHARES, vartype=xp.semicontinuous, threshold=0.1, ub=0.3, name="frac")
# CONSTRAINTS.
# Limit the percentage of high-risk values.
p.addConstraint(xp.Sum(frac[i] for i in RISK) <= 1/3)
# Minimum amount of North-American values.
p.addConstraint(xp.Sum(frac[i] for i in NA) >= 0.5)
# Spend all the capital.
p.addConstraint(xp.Sum(frac) == 1)
# Objective: maximize total return.
p.setObjective(xp.Sum(frac[i] * RET[i] for i in range(NSHARES)), sense=xp.maximize)
# Solve.
p.optimize()
# Print problem status.
print(f"Problem status: \n\t Solve status: {p.attributes.solvestatus.name} \n\t Sol status: \
{p.attributes.solstatus.name}")
# Solution printing.
print("Total return:", p.attributes.objval)
sol = p.getSolution(frac)
for i in range(NSHARES):
print(f"{frac[i].name} : {sol[i]*100:.2f} %")
|
|
| FolioQC.py |
# Modeling a small QC problem to perform portfolio optimization.
# - Maximize return with limit on variance.
#
# (C) 2025 Fair Isaac Corporation
import xpress as xp
import csv
# Read the CSV file and store each row as a 2D list
file_path = 'Data/foliocppqp.csv'
VAR = []
with open(file_path, 'r') as file:
reader = csv.reader(file)
for row in reader:
VAR.append([float(value) for value in row])
# Problem data
MAXVAR = 0.55
MAXNUM = 4
NSHARES = 10
RET = [5, 17, 26, 12, 8, 9, 7, 6, 31, 21]
NA = [0, 1, 2, 3]
p = xp.problem(name="Folio")
# VARIABLES.
frac = p.addVariables(NSHARES, ub=0.3, name="frac")
# CONSTRAINTS.
# Minimum amount of North-American values.
p.addConstraint(xp.Sum(frac[i] for i in NA) >= 0.5)
# Spend all the capital.
p.addConstraint(xp.Sum(frac) == 1)
# Limit variance.
variance = [frac[s]*frac[t]*VAR[s][t] for s in range(NSHARES) for t in range(NSHARES)]
p.addConstraint(xp.Sum(variance) <= MAXVAR)
# Objective: maximize total return.
p.setObjective(xp.Sum(frac[i] * RET[i] for i in range(NSHARES)), sense=xp.maximize)
# Solve.
p.optimize()
# Print problem status.
print(f"Problem status: \n\t Solve status: {p.attributes.solvestatus.name} \n\t Sol status: \
{p.attributes.solstatus.name}")
# Solution printing.
print(f"With a max variance of {MAXVAR} total return is {p.attributes.objval}")
sol = p.getSolution(frac)
for i in range(NSHARES):
print(f"{frac[i].name} : {sol[i]*100:.2f} %")
|
|
| FolioQP.py |
# Modeling a small QP problem to perform portfolio optimization.
# 1. QP: minimize variance.
# 2. MIQP: limited number of assets.
#
# (C) 2025 Fair Isaac Corporation
import xpress as xp
import csv
# Read the CSV file and store each row as a 2D list
file_path = 'Data/foliocppqp.csv'
VAR = []
with open(file_path, 'r') as file:
reader = csv.reader(file)
for row in reader:
VAR.append([float(value) for value in row])
# Problem data
TARGET = 9
MAXNUM = 4
NSHARES = 10
RET = [5, 17, 26, 12, 8, 9, 7, 6, 31, 21]
NA = [0, 1, 2, 3]
# *******FIRST PROBLEM: UNLIMITED NUMBER OF ASSETS********
p = xp.problem(name="Folio")
# VARIABLES.
frac = p.addVariables(NSHARES, ub=0.3, name="frac")
# CONSTRAINTS.
# Minimum amount of North-American values.
p.addConstraint(xp.Sum(frac[i] for i in NA) >= 0.5)
# Spend all the capital.
p.addConstraint(xp.Sum(frac) == 1)
# Target yield.
p.addConstraint(xp.Sum(frac[i] * RET[i] for i in range(NSHARES)) >= TARGET)
# Objective: minimize mean variance.
variance = [frac[s]*frac[t]*VAR[s][t] for s in range(NSHARES) for t in range(NSHARES)]
p.setObjective(xp.Sum(variance))
# Solve.
p.optimize()
# Print problem status.
print(f"Problem status: \n\t Solve status: {p.attributes.solvestatus.name} \n\t Sol status: \
{p.attributes.solstatus.name}")
# Solution printing.
print(f"With a target of {TARGET} minimum variance is {p.attributes.objval}")
sol = p.getSolution(frac)
for i in range(NSHARES):
print(f"{frac[i].name} : {sol[i]*100:.2f} %")
# *******SECOND PROBLEM: LIMIT NUMBER OF ASSETS********
buy = p.addVariables(NSHARES, vartype=xp.binary, name="buy")
# CONSTRAINTS.
# Minimum amount of North-American values.
p.addConstraint(xp.Sum(frac[i] for i in NA) >= 0.5)
# Limit the total number of assets.
p.addConstraint(xp.Sum(buy) <= MAXNUM)
# Linking the variables.
p.addConstraint(frac[i] <= buy[i] for i in range(NSHARES))
# Solve.
p.optimize()
# Print problem status.
print(f"Problem status: \n\t Solve status: {p.attributes.solvestatus.name} \n\t Sol status: \
{p.attributes.solstatus.name}")
# Solution printing.
print(f"With a target of {TARGET} minimum variance is {p.attributes.objval}")
sol = p.getSolution(frac)
for i in range(NSHARES):
print(f"{frac[i].name} : {sol[i]*100:.2f} %")
|
|
| FolioHeuristic.py |
# Using a heuristic solution to perform portfolio optimization.
#
# (C) 2025 Fair Isaac Corporation
import xpress as xp
# Problem data
MAXNUM = 4
NSHARES = 10
RET = [5, 17, 26, 12, 8, 9, 7, 6, 31, 21]
RISK = [1, 2, 3, 8, 9]
NA = [0, 1, 2, 3]
def printSolution(prob, name):
# Solution printing.
print(f"Total return {name}:", prob.attributes.objval)
sol = prob.getSolution()
for i in range(NSHARES):
print(f"{frac[i].name} : {sol[i] * 100:.2f} %")
def solveHeuristic(prob):
# Disable automatic cuts.
prob.controls.cutstrategy = 0
# Switch presolve off.
prob.controls.presolve = 0
prob.controls.mippresolve = 0
# Get feasibility tolerance.
tol = prob.controls.feastol
prob.lpOptimize()
# Save the current basis.
rowstat, colstat = prob.getBasis()
# Fix all variables 'buy' for which `frac' is at 0 relatively close to 1
fsol = prob.getSolution(frac) # get the solution values of `frac'
for i in range(NSHARES):
if fsol[i] < tol:
buy[i].lb = 0
buy[i].ub = 0
elif fsol[i] > 0.2 - tol:
buy[i].lb = 1
buy[i].ub = 1
prob.mipOptimize()
print(f"Problem status: \n\t Solve status: {p.attributes.solvestatus.name} \n\t Sol status: \
{p.attributes.solstatus.name}")
printSolution(prob, "Heuristic solution")
# Reset variables to their original bounds.
for i in range(NSHARES):
if fsol[i] < tol or fsol[i] > 0.2 - tol:
buy[i].lb = 0
buy[i].ub = 1
# Load basis.
prob.loadBasis(rowstat, colstat)
# Set cutoff to the best known solution.
prob.controls.mipabscutoff = prob.attributes.objval - tol
p = xp.problem(name="Folio")
# VARIABLES.
frac = p.addVariables(NSHARES, ub=0.3, name="frac")
buy = p.addVariables(NSHARES, vartype=xp.binary, name="buy")
# CONSTRAINTS.
# Limit the percentage of high-risk values.
p.addConstraint(xp.Sum(frac[i] for i in RISK) <= 1/3)
# Minimum amount of North-American values.
p.addConstraint(xp.Sum(frac[i] for i in NA) >= 0.5)
# Spend all the capital.
p.addConstraint(xp.Sum(frac) == 1)
# Limit the total number of assets.
p.addConstraint(xp.Sum(buy) <= MAXNUM)
# Linking the variables.
p.addConstraint(frac[i] <= buy[i] for i in range(NSHARES))
# Objective: maximize total return.
p.setObjective(xp.Sum(frac[i] * RET[i] for i in range(NSHARES)), sense=xp.maximize)
# Solve with heuristic.
solveHeuristic(p)
# Solve original problem.
p.optimize()
# Print problem status.
print(f"Problem status: \n\t Solve status: {p.attributes.solvestatus.name} \n\t Sol status: \
{p.attributes.solstatus.name}")
printSolution(p, "Exact Solve")
|
|