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
!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", # Bruuched de 44-Zeiche API_KEY, wo ihr usem IBM Quantum Platform Home-Dashboard erstellt und gschpeicheret händ
)
# Griifed uf d'Function zue
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
# Lischt vo IBEX 35-Symbole
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]
# Mached e volle Datums-Index, au mit Wuchenänd
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
# Reindexiered, zum Wuchenänd mitizschliesse
data = data.reindex(full_index)
# Fülled fehlendi Wärt (für Wuchenänd oder Fiirtig) dür forward/backward fill
data.ffill(inplace=True)
data.bfill(inplace=True)
series_list.append(data)
# Kombiniered alli Serie in eim einzige DataFrame
df = pd.concat(series_list, axis=1)
# Konvertiered de Index zu String für Konsistänz
df.index = df.index.astype(str)
# Konvertiered DataFrame zu 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, # maximali Investitione pro Asset isch 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.
# Holed d'Resultat vom Job
dpo_result = dpo_job.result()
# Zeiged d'Lösigs-Strategie
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
# Holed Resultat vom Job
dpo_result = dpo_job.result()
# Konvertiered Metadate zu emne DataFrame, ohni 'session_id'
df = pd.DataFrame(dpo_result["metadata"]["all_samples_metrics"])
# Findet d'minimale objektive Choschte
min_cost = df["objective_costs"].min()
print(f"Minimum Objective Cost Found: {min_cost:.2f}")
# Extrachiered d'Reihe mit de tüüfschte Choschte
best_row = df[df["objective_costs"] == min_cost].iloc[0]
# Zeiged d'Resultat, wo mit de beschte Lösig verbunde sind
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):
"""
Plottet normalisierti Resultat für zwöi Stichprobe-Resultat.
Parameters:
dpo_x (array-like): X-Wärt für d'VQE Post-processed-Kurve.
dpo_y_normalized (array-like): Y-Wärt (normalisiert) für d'VQE Post-processed-Kurve.
random_x (array-like): X-Wärt für d'Noise (Random)-Kurve.
random_y_normalized (array-like): Y-Wärt (normalisiert) für d'Noise (Random)-Kurve.
"""
plt.figure(figsize=(6, 3))
plt.tick_params(axis="both", which="major", labelsize=12)
# Definiered eigeni Farbe
colors = ["#4823E8", "#9AA4AD"]
# Plottet DPO-Resultat
(line1,) = plt.plot(
dpo_x, dpo_y_normalized, label="VQE Postprocessed", color=colors[0]
)
line1.set_path_effects(
[patheffects.withStroke(linewidth=3, foreground="white")]
)
# Plottet Zufälligi Resultat
(line2,) = plt.plot(
random_x, random_y_normalized, label="Noise (Random)", color=colors[1]
)
line2.set_path_effects(
[patheffects.withStroke(linewidth=3, foreground="white")]
)
# Setzed X-Achse-Ticks uf 5 Einheite-Schritte
plt.gca().xaxis.set_major_locator(MultipleLocator(5))
# Achse-Beschriftige und Legende
plt.xlabel("Objective cost", fontsize=14)
plt.ylabel("Normalized Counts", fontsize=14)
# Füeged DOCPlex-Referänzlinie hinzue
plt.axvline(
x=-4.11, color="black", linestyle="--", linewidth=1, label="DOCPlex"
) # DOCPlex-Wärt
plt.ylim(bottom=0)
plt.legend()
# Passed Layout aa
plt.tight_layout()
plt.show()
import numpy as np
from collections import defaultdict
# ================================
# SCHRITT 1: DPO-CHOSCHTE-VERTEILIG
# ================================
# Extrachiered Date us DPO-Resultat
counts_list = dpo_result["metadata"]["all_samples_metrics"][
"objective_costs"
] # Lischt, wie oft jedi Lösig vorgcho isch
cost_list = dpo_result["metadata"]["all_samples_metrics"][
"counts"
] # Lischt vo de dazuehörige objektive Funktions-Wärt (Choschte)
# Rundet Choschte uf ei Dezimal und akkumuliered Counts für jedi einzigartige Choschte
dpo_counter = defaultdict(int)
for cost, count in zip(cost_list, counts_list):
rounded_cost = round(cost, 1)
dpo_counter[rounded_cost] += count
# Bereiteted Date fürs Plotte vor
dpo_x = sorted(dpo_counter.keys()) # Sortierti Lischt vo Choschte-Wärt
dpo_y = [dpo_counter[c] for c in dpo_x] # Dazuehörigi Counts
# Normalisiered d'Counts uf de Beriich [0, 1] für bessere Vergliich
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
]
# ================================
# SCHRITT 2: ZUFÄLLIGI CHOSCHTE-VERTEILIG
# ================================
# Läsed d'QUBO-Matrix
qubo = np.array(dpo_result["metadata"]["qubo"])
bitstring_length = qubo.shape[0]
num_random_samples = 100_000 # Aazahl vo zufällige Stichprobe zum generiere
random_cost_counter = defaultdict(int)
# Generiered zufälligi Bitstrings und berächned ihri Choschte
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
# Bereiteted zufälligi Date fürs Plotte vor
random_x = sorted(random_cost_counter.keys())
random_y = [random_cost_counter[c] for c in random_x]
# Normalisiered d'zufälligi Choschte-Verteilig
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
]
# ================================
# SCHRITT 3: PLOTTE
# ================================
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