Mached dynamischi Portfolio-Optimierig mit em Portfolio Optimizer vo Global Data Quantum
Qiskit Functions sind es experimentells Feature und nume für IBM Quantum® Premium Plan, Flex Plan und On-Prem (über IBM Quantum Platform API) Plan-Benutzer verfüegbar. Si sind im Preview-Status und chönd sich no ändere.
Verbruchs-Schätzig: Öppe 55 Minute uf emne Heron r2-Prozessor. (Hinwys: Das isch nume e Schätzig. D'wirklechi Ziit chan andersch usfalle.)
Hingergrund
S'dynamisch Portfolio-Optimierigs-Problem het s'Ziil, di optimal Investitions-Strategie über mehreri Ziitperiode z'finde, zum de erwarteti Rendite vom Portfolio z'maximiere und d'Risike z'minimiere — oft under bestimmte Bedingige wie Budget, Transaktions-Choschte oder Risiko-Aversion. Andersch als d'Standard Portfolio-Optimierig, wo nume ei Momänt für s'Rebalanciere vom Portfolio betrachtet, berücksichtigt di dynamisch Versione d'Veränderige vom Wärt vo de Assets und passt d'Investitione genau aa, wie sich d'Performance vo de Assets über d'Ziit veränderet.
Das Tutorial da zeigt, wie mer dynamischi Portfolio-Optimierig mit de Quantum Portfolio Optimizer Qiskit Function chönd mache. Speziell zeige mer, wie mer die Application-Function chönd nutze, zum es Investitions-Verteiligs-Problem über mehreri Ziit-Schritte z'löse.
De Aasatz formuliert d'Portfolio-Optimierig als es Multi-Objective Quadratic Unconstrained Binary Optimization (QUBO) Problem. Speziell formuliere mer d'QUBO-Funktion so, dass sie vier verschideni Ziilsetzige gliichziitig optimiert:
- Maximiere d'Rendite-Funktion
- Minimiere s'Risiko vo de Investitione
- Minimiere d'Transaktions-Choschte
- Halted euch a d'Investitions-Beschränkige, formuliert in emne zuesätzliche Term zum Minimiere vo .
Zämegfasst formuliere mer d'QUBO-Funktion so wo de Risiko-Aversions-Koeffizient isch und de Beschränkigs-Verstärkigs-Koeffizient (Lagrange-Multiplikator). Di expliziti Formulierig findet mer in Gl. (15) vo eusere Veröffentlichung [1].
Mer löset das mit enere hybride Quantum-Klassische Methode, wo uf em Variational Quantum Eigensolver (VQE) basiert. In däm Setup schätzt de Quanteschaltkreis d'Choschte-Funktion, während d'klassischi Optimierig mit em Differential Evolution-Algorithmus dureführt wird, was es möglech macht, d'Lösigs-Landschaft effizient z'dursuoche. D'Aazahl vo Qubits, wo mer bruuched, hangt vo drüü Hauptfaktore ab: d'Aazahl vo Assets na, d'Aazahl vo Ziit-Periode nt und d'Bit-Uflösig für d'Darstellig vo de Investitione nq. Konkret isch di minimali Aazahl vo Qubits in euserem Problem na*nt*nq.
In däm Tutorial konzentriere mer eus uf d'Optimierig vo emne regionale Portfolio, wo uf em spanische IBEX 35-Index basiert. Speziell bruuched mer es Sibe-Asset-Portfolio wie i de Tabälle da:
| IBEX 35 Portfolio | ACS.MC | ITX.MC | FER.MC | ELE.MC | SCYR.MC | AENA.MC | AMS.MC |
|---|
Mer rebalanciere eus Portfolio in vier Ziit-Schritte, jewiils 30 Täg usenand, aafangend am 1. November 2022. Jedi Investitions-Variable wird mit zwöi Bits kodiert. Das füehrt zumne Problem, wo 56 Qubits bruucht, zum's z'löse.
Mer bruuched de Optimized Real Amplitudes-Aasatz, e aagpassti und hardware-effizienti Adaptierig vom Standard Real Amplitudes-Aasatz, speziell aagpasst, zum d'Performance für die Art vo finanzielle Optimierig z'verbessere.
D'Quante-Usführig wird uf em ibm_torino-Backend dureführt. Für e detaillierti Erklärig vo de Problem-Formulierig, Methodologii und Performance-Evaluierig luegt na de veröffentlichte Manuskript [1].
Vorussetzige
# Added by doQumentation — required packages for this notebook
!pip install -q numpy
!pip install qiskit-ibm-catalog
!pip install pandas
!pip install matplotlib
!pip install yfinance
Iirichtig
Zum de Quantum Portfolio Optimizer z'nutze, wähled s'Function-Objekt über de Qiskit Functions Catalog uus. Ihr bruuched es IBM Quantum Premium Plan- oder Flex Plan-Konto mit enere Lizänz vo Global Data Quantum, zum die Function z'nutze.
Zerscht authentifiziered euch mit euem API-Schlüssel. Dänn laaded s'gwünschti Function-Objekt usem Qiskit Functions Catalog. Da griifed ihr uf d'quantum_portfolio_optimizer-Function usem Catalog zue, indem ihr d'QiskitFunctionsCatalog-Klass bruuched. Die Function erlaubt es eus, de vordefinierti Quantum Portfolio Optimization-Solver z'nutze.
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(
channel="ibm_quantum_platform",
instance="INSTANCE_CRN",
token="YOUR_API_KEY", # Use the 44-character API_KEY you created and saved from the IBM Quantum Platform Home dashboard
)
# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")
Schritt 1: Läsed s'Input-Portfolio
In däm Schritt laade mer historischi Date für di sibe usgwählte Assets usem IBEX 35-Index, speziell vom 1. November 2022 bis 1. April 2023.
Mer holed d'Date über d'Yahoo Finance-API und konzentriere eus uf d'Schlusskurse. D'Date werded dänn so verarbeitet, dass alli Assets di gliich Aazahl vo Täg mit Date händ. Fehlendi Date (Nüt-Handels-Täg) werded passend behandlet, so dass alli Assets uf de gliiche Datüme usgrichtet sind.
D'Date sind in emne DataFrame mit konsistänter Formatierig für alli Assets strukturiert.
import yfinance as yf
import pandas as pd
# List of IBEX 35 symbols
symbols = [
"ACS.MC",
"ITX.MC",
"FER.MC",
"ELE.MC",
"SCYR.MC",
"AENA.MC",
"AMS.MC",
]
start_date = "2022-11-01"
end_date = "2023-4-01"
series_list = []
symbol_names = [symbol.replace(".", "_") for symbol in symbols]
# Create a full date index including weekends
full_index = pd.date_range(start=start_date, end=end_date, freq="D")
for symbol, name in zip(symbols, symbol_names):
print(f"Downloading data for {symbol}...")
data = yf.download(symbol, start=start_date, end=end_date)["Close"]
data.name = name
# Reindex to include weekends
data = data.reindex(full_index)
# Fill missing values (for example, weekends or holidays) by forward/backward fill
data.ffill(inplace=True)
data.bfill(inplace=True)
series_list.append(data)
# Combine all series into a single DataFrame
df = pd.concat(series_list, axis=1)
# Convert index to string for consistency
df.index = df.index.astype(str)
# Convert DataFrame to dictionary
assets = df.to_dict()
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
[*********************100%***********************] 1 of 1 completed
Downloading data for ACS.MC...
Downloading data for ITX.MC...
Downloading data for FER.MC...
Downloading data for ELE.MC...
Downloading data for SCYR.MC...
Downloading data for AENA.MC...
Downloading data for AMS.MC...
Schritt 2: Definiered d'Problem-Iigabe
D'Parameter, wo mer bruuched, zum s'QUBO-Problem z'definiere, werded im qubo_settings-Dictionary konfiguriert. Mer definieret d'Aazahl vo Ziit-Schritte (nt), d'Aazahl vo Bits für d'Investitions-Spezifikatione (nq) und s'Ziit-Fänschter für jede Ziit-Schritt (dt). Zuesätzlich setze mer d'maximali Investitione pro Asset, de Risiko-Aversions-Koeffizient, d'Transaktions-Gebühr und de Beschränkigs-Koeffizient (lueged na eusem Paper für Details über d'Problem-Formulierig). Die Iistellige erlaubed es eus, s'QUBO-Problem a s'spezielli Investitions-Szenario aazpasse.
qubo_settings = {
"nt": 4,
"nq": 2,
"dt": 30,
"max_investment": 5, # maximum investment per asset is 2**nq/max_investment = 80%
"risk_aversion": 1000.0,
"transaction_fee": 0.01,
"restriction_coeff": 1.0,
}
S'optimizer_settings-Dictionary konfiguriert de Optimierigs-Prozäss, mit Parameter wie num_generations für d'Aazahl vo Iteratione und population_size für d'Aazahl vo Kandidate-Lösige pro Generatione. Anderi Iistellige kontrolliered Aspäkte wie d'Rekombinations-Rate, paralleli Jobs, Batch-Grössi und Mutations-Beriich. Zuesätzlich definiered d'Primitive-Iistellige wie estimator_shots, estimator_precision und sampler_shots d'Quante-Estimator- und Sampler-Konfiguratione für de Optimierigs-Prozäss.
optimizer_settings = {
"de_optimizer_settings": {
"num_generations": 20,
"population_size": 40,
"recombination": 0.4,
"max_parallel_jobs": 5,
"max_batchsize": 4,
"mutation_range": [0.0, 0.25],
},
"optimizer": "differential_evolution",
"primitive_settings": {
"estimator_shots": 25_000,
"estimator_precision": None,
"sampler_shots": 100_000,
},
}
Di gsamt Aazahl vo Schaltkreise hangt vo de optimizer_settings-Parameter ab und wird berechnet als (num_generations + 1) * population_size.
S'ansatz_settings-Dictionary konfiguriert de Quante-Schaltkreis-Aasatz. De ansatz-Parameter spezifiziert d'Nutzig vom "optimized_real_amplitudes"-Aasatz, wo es hardware-effiziente Aasatz isch, wo für finanzielli Optimierigs-Probleme entwicklet worde isch. Zuesätzlich isch d'multiple_passmanager-Iistellig aktiviert, zum mehreri Pass-Manager (inklusiv de Standard lokal Qiskit Pass-Manager und de Qiskit AI-aatriibeni Transpiler-Service) während em Optimierigs-Prozäss z'erlauben, was d'Gsamt-Performance und Effiziänz vo de Schaltkreis-Usführig verbesseret.
ansatz_settings = {
"ansatz": "optimized_real_amplitudes",
"multiple_passmanager": False,
}
Zletsch führe mer d'Optimierig us, indem mer d'dpo_solver.run()-Funktion usführed und di vorbereitet Iigabe dürgevend. Das umfasst s'Asset-Date-Dictionary (assets), d'QUBO-Konfiguration (qubo_settings), Optimierigs-Parameter (optimizer_settings) und d'Quante-Schaltkreis-Aasatz-Iistellige (ansatz_settings). Zuesätzlich spezifiziered mer d'Usführigs-Details wie s'Backend und ob mer Post-Processing uf d'Resultat aanwänded. Das startet de dynamisch Portfolio-Optimierigs-Prozäss uf em usgwählte Quante-Backend.
dpo_job = dpo_solver.run(
assets=assets,
qubo_settings=qubo_settings,
optimizer_settings=optimizer_settings,
ansatz_settings=ansatz_settings,
backend_name="ibm_torino",
previous_session_id=[],
apply_postprocess=True,
)
Schritt 3: Analysiered d'Optimierigs-Resultat
In däm Abschnitt extrahiered mer und zeiged d'Lösig mit de tüüfschte objektive Choschte us de Optimierigs-Resultat. Näbe de minimale objektive Choschte präsentiered mer au Schlüssel-Metrike, wo mit de dazuehörige Lösig verbunde sind, wie d'Beschränkigs-Abwiichig, Sharpe-Ratio und Investitions-Rendite.
# Get the results of the job
dpo_result = dpo_job.result()
# Show the solution strategy
dpo_result["result"]
{'time_step_0': {'ACS.MC': 0.11764705882352941,
'ITX.MC': 0.20588235294117646,
'FER.MC': 0.38235294117647056,
'ELE.MC': 0.058823529411764705,
'SCYR.MC': 0.0,
'AENA.MC': 0.058823529411764705,
'AMS.MC': 0.17647058823529413},
'time_step_1': {'ACS.MC': 0.11428571428571428,
'ITX.MC': 0.14285714285714285,
'FER.MC': 0.2,
'ELE.MC': 0.02857142857142857,
'SCYR.MC': 0.42857142857142855,
'AENA.MC': 0.0,
'AMS.MC': 0.08571428571428572},
'time_step_2': {'ACS.MC': 0.0,
'ITX.MC': 0.09375,
'FER.MC': 0.3125,
'ELE.MC': 0.34375,
'SCYR.MC': 0.0,
'AENA.MC': 0.0,
'AMS.MC': 0.25},
'time_step_3': {'ACS.MC': 0.3939393939393939,
'ITX.MC': 0.09090909090909091,
'FER.MC': 0.12121212121212122,
'ELE.MC': 0.18181818181818182,
'SCYR.MC': 0.0,
'AENA.MC': 0.0,
'AMS.MC': 0.21212121212121213}}
import pandas as pd
# Get results from the job
dpo_result = dpo_job.result()
# Convert metadata to a DataFrame, excluding 'session_id'
df = pd.DataFrame(dpo_result["metadata"]["all_samples_metrics"])
# Find the minimum objective cost
min_cost = df["objective_costs"].min()
print(f"Minimum Objective Cost Found: {min_cost:.2f}")
# Extract the row with the lowest cost
best_row = df[df["objective_costs"] == min_cost].iloc[0]
# Display the results associated with the best solution
print("Best Solution:")
print(f" - Restriction Deviation: {best_row['rest_breaches']}%")
print(f" - Sharpe Ratio: {best_row['sharpe_ratios']:.2f}")
print(f" - Return: {best_row['returns']:.2f}")
Minimum Objective Cost Found: -3.67
Best Solution:
- Restriction Deviation: 40.0%
- Sharpe Ratio: 14.54
- Return: 0.28
De folgend Code zeigt, wie mer d'Choschte-Verteilig vo emne Optimierigs-Algorithmus mit enere zufällige Stichprobe-Verteilig chönd visualisiere und vergliiche. Ähnlich erforsche mer d'Landschaft vo de QUBO-objektive Funktion (wo usem Function-Output glade werded chan), indem mer sie mit zufällige Investitione evaluiered. Mer plotted beidi Verteilige, normalisiert in Amplitude, für e eifachere Vergliich, wie sich de Optimierigs-Prozäss vo zufällige Stichprobe in Bezug uf Choschte ungerscheidet. Zuesätzlich wird s'Resultat, wo mer mit DOCPlex übercho händ, als gstrichleti vertikali Referänzlinie mitufgnoh, zum als klassischi Benchmark z'diene. Mer bruuched d'gratis Versione vo DOCPlex — d'IBM® Open-Source-Bibliothek für mathematischi Optimierig in Python — zum s'gliich Problem klassisch z'löse.
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
import matplotlib.patheffects as patheffects
def plot_normalized(dpo_x, dpo_y_normalized, random_x, random_y_normalized):
"""
Plots normalized results for two sampling results.
Parameters:
dpo_x (array-like): X-values for the VQE Post-processed curve.
dpo_y_normalized (array-like): Y-values (normalized) for the VQE Post-processed curve.
random_x (array-like): X-values for the Noise (Random) curve.
random_y_normalized (array-like): Y-values (normalized) for the Noise (Random) curve.
"""
plt.figure(figsize=(6, 3))
plt.tick_params(axis="both", which="major", labelsize=12)
# Define custom colors
colors = ["#4823E8", "#9AA4AD"]
# Plot DPO results
(line1,) = plt.plot(
dpo_x, dpo_y_normalized, label="VQE Postprocessed", color=colors[0]
)
line1.set_path_effects(
[patheffects.withStroke(linewidth=3, foreground="white")]
)
# Plot Random results
(line2,) = plt.plot(
random_x, random_y_normalized, label="Noise (Random)", color=colors[1]
)
line2.set_path_effects(
[patheffects.withStroke(linewidth=3, foreground="white")]
)
# Set X-axis ticks to increment by 5 units
plt.gca().xaxis.set_major_locator(MultipleLocator(5))
# Axis labels and legend
plt.xlabel("Objective cost", fontsize=14)
plt.ylabel("Normalized Counts", fontsize=14)
# Add DOCPLEX reference line
plt.axvline(
x=-4.11, color="black", linestyle="--", linewidth=1, label="DOCPlex"
) # DOCPlex value
plt.ylim(bottom=0)
plt.legend()
# Adjust layout
plt.tight_layout()
plt.show()
import numpy as np
from collections import defaultdict
# ================================
# STEP 1: DPO COST DISTRIBUTION
# ================================
# Extract data from DPO results
counts_list = dpo_result["metadata"]["all_samples_metrics"][
"objective_costs"
] # List of how many times each solution occurred
cost_list = dpo_result["metadata"]["all_samples_metrics"][
"counts"
] # List of corresponding objective function values (costs)
# Round costs to one decimal and accumulate counts for each unique cost
dpo_counter = defaultdict(int)
for cost, count in zip(cost_list, counts_list):
rounded_cost = round(cost, 1)
dpo_counter[rounded_cost] += count
# Prepare data for plotting
dpo_x = sorted(dpo_counter.keys()) # Sorted list of cost values
dpo_y = [dpo_counter[c] for c in dpo_x] # Corresponding counts
# Normalize the counts to the range [0, 1] for better comparison
dpo_min = min(dpo_y)
dpo_max = max(dpo_y)
dpo_y_normalized = [
(count - dpo_min) / (dpo_max - dpo_min) for count in dpo_y
]
# ================================
# STEP 2: RANDOM COST DISTRIBUTION
# ================================
# Read the QUBO matrix
qubo = np.array(dpo_result["metadata"]["qubo"])
bitstring_length = qubo.shape[0]
num_random_samples = 100_000 # Number of random samples to generate
random_cost_counter = defaultdict(int)
# Generate random bitstrings and calculate their cost
for _ in range(num_random_samples):
x = np.random.randint(0, 2, size=bitstring_length)
cost = float(x @ qubo @ x.T)
rounded_cost = round(cost, 1)
random_cost_counter[rounded_cost] += 1
# Prepare random data for plotting
random_x = sorted(random_cost_counter.keys())
random_y = [random_cost_counter[c] for c in random_x]
# Normalize the random cost distribution
random_min = min(random_y)
random_max = max(random_y)
random_y_normalized = [
(count - random_min) / (random_max - random_min) for count in random_y
]
# ================================
# STEP 3: PLOTTING
# ================================
plot_normalized(dpo_x, dpo_y_normalized, random_x, random_y_normalized)
De Graph zeigt, wie de Quantum Portfolio Optimizer konsischtänt optimierti Investitions-Strategie zruggit.
Referänze
Tutorial-Umfrag
Nehmed euch bitte e Minute Ziit, zum Feedback über das Tutorial z'gäh. Euri Iisichte hälfed eus, euses Content-Aabot und User-Experience z'verbessere. Link zur Umfrag