Quantum Portfolio Optimizer: Eine Qiskit Function von Global Data Quantum
Siehe die API-Referenz
Qiskit Functions sind ein experimentelles Feature, das ausschließlich für Nutzer des IBM Quantum® Premium Plan, Flex Plan und On-Prem (über die IBM Quantum Platform API) Plan verfügbar ist. Sie befinden sich im Vorschau-Release-Status und können sich ändern.
Übersicht
Der Quantum Portfolio Optimizer ist eine Qiskit Function, die das dynamische Portfolio-Optimierungsproblem löst – ein klassisches Finanzproblem, bei dem es darum geht, periodische Investitionen auf eine Reihe von Vermögenswerten aufzuteilen, um Renditen zu maximieren und Risiken zu minimieren. Durch den Einsatz modernster Quantenoptimierungstechniken vereinfacht diese Function den Prozess so weit, dass Nutzerinnen und Nutzer ohne Kenntnisse im Quantencomputing von ihren Vorteilen bei der Suche nach optimalen Investitionstrajektorien profitieren können. Ideal für Portfoliomanager, Forscher im Bereich der quantitativen Finanzen und Einzelanleger – dieses Tool ermöglicht das Backtesting von Handelsstrategien in der Portfolio-Optimierung.
Funktionsbeschreibung
Die Quantum Portfolio Optimizer Function verwendet den Variational Quantum Eigensolver (VQE) Algorithmus, um ein Quadratic Unconstrained Binary Optimization (QUBO) Problem zu lösen und damit dynamische Portfolio-Optimierungsprobleme zu adressieren. Nutzerinnen und Nutzer müssen lediglich die Kursdaten der Vermögenswerte bereitstellen und die Investitionsbeschränkung definieren – die Function führt dann den Quantenoptimierungsprozess durch und gibt eine Reihe optimierter Investitionstrajektorien zurück.
Der Prozess besteht aus vier Hauptphasen. Zunächst werden die Eingabedaten auf ein quantenkompatibles Problem abgebildet, das QUBO des dynamischen Portfolio-Optimierungsproblems wird konstruiert und in einen Quantenoperator (Ising-Hamiltonoperator) transformiert. Anschließend werden das Eingabeproblem und der VQE-Algorithmus für den Einsatz auf Quantenhardware angepasst. Danach wird der VQE-Algorithmus auf der Quantenhardware ausgeführt, und schließlich werden die Ergebnisse nachbearbeitet, um die optimalen Investitionstrajektorien bereitzustellen. Das System umfasst auch eine rauschbewusste (SQD-basierte) Nachbearbeitung, um die Ausgabequalität zu maximieren.
Diese Qiskit Function basiert auf dem veröffentlichten Manuskript von Global Data Quantum.
Einstieg
Authentifiziere dich mit deinem API-Schlüssel und wähle die Qiskit Function wie folgt aus. (Dieses Snippet setzt voraus, dass du bereits dein Konto gespeichert hast in deiner lokalen Umgebung.)
# Added by doQumentation — required packages for this notebook
!pip install -q pandas qiskit-ibm-catalog
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")
Beispiel: Dynamische Portfolio-Optimierung mit sieben Vermögenswerten
Dieses Beispiel zeigt, wie die dynamische Portfolio-Optimierung (DPO) Function ausgeführt und ihre Einstellungen für optimale Leistung angepasst werden. Es enthält detaillierte Schritte zur Feinabstimmung der Parameter, um die gewünschten Ergebnisse zu erzielen.
Dieser Fall umfasst sieben Vermögenswerte, vier Zeitschritte und vier Auflösungs-Qubits, was einen Gesamtbedarf von 112 Qubits ergibt.
1. Lies die im Portfolio enthaltenen Vermögenswerte ein
Wenn alle Vermögenswerte des Portfolios in einem Ordner unter einem bestimmten Pfad gespeichert sind, kannst du sie mit der folgenden Funktion in ein pandas.DataFrame laden und in ein dict-Objekt konvertieren.
import os
import glob
import pandas as pd
def read_and_join_csv(file_pattern):
"""
Reads multiple CSV files matching the file pattern and combines them
into a single DataFrame.
Parameters:
file_pattern (str): The pattern to match CSV files.
Returns:
pd.DataFrame: Combined DataFrame with data from all CSV files.
"""
# Find all files matching the pattern
csv_files = glob.glob(file_pattern)
# Get the base file names without the .csv extension
file_names = [os.path.basename(f).replace(".csv", "") for f in csv_files]
# Read each CSV file into a DataFrame and set the first column as the index
df_list = [pd.read_csv(f).set_index("Unnamed: 0") for f in csv_files]
# Rename columns in each DataFrame to the base file names
for df, name in zip(df_list, file_names):
df.columns = [name]
# Combine all DataFrames into one by merging them side by side
combined_df = pd.concat(df_list, axis=1)
return combined_df
file_pattern = "route/to/folder/with/assets/data/*.csv"
assets = read_and_join_csv(file_pattern).to_dict()
In diesem Beispiel haben wir die Vermögenswerte 8801.T, CLF, GBPJPY, ITX.MC, META, TMBMKDE-10Y und XS2239553048 verwendet. Die folgende Abbildung veranschaulicht die in diesem Beispiel verwendeten Daten und zeigt die Entwicklung der täglichen Schlusskurse der Vermögenswerte vom 1. Januar bis 1. September 2023.
In diesem Beispiel haben wir Nicht-Handelstage mit dem Schlusskurs des letzten verfügbaren Datums aufgefüllt, um eine einheitliche Datumsstruktur zu gewährleisten. Dieser Schritt ist notwendig, da die ausgewählten Vermögenswerte aus verschiedenen Märkten mit unterschiedlichen Handelstagen stammen, was eine Standardisierung des Datensatzes für die Konsistenz erforderlich macht.
2. Definiere das Problem
Definiere die Spezifikationen des Problems, indem du die Parameter im qubo_settings-Dictionary konfigurierst.
qubo_settings = {
"nt": 4,
"nq": 4,
"dt": 30,
"max_investment": 25,
"risk_aversion": 1000.0,
"transaction_fee": 0.01,
"restriction_coeff": 1.0,
}
3. Definiere die Optimierer- und Ansatz-Einstellungen (Optional)
Definiere optional spezifische Anforderungen für den Optimierungsprozess, einschließlich der Auswahl des Optimierers und seiner Parameter sowie der Spezifikation des Primitives und seiner Konfigurationen.
Für den Tailored Ansatz basiert die gewählte Populationsgröße auf früheren Experimenten, die zeigten, dass dieser Wert eine stabile und effiziente Optimierung liefert.
Im Fall des Real Amplitudes Ansatzes kannst du einer linearen Beziehung zwischen der population_size und der Anzahl der Qubits im Circuit folgen. Als grobe Faustregel wird empfohlen, eine Mindest-population_size ~ 0.8 * n_qubits für den real_amplitudes-Ansatz zu verwenden.
Es wird erwartet, dass der Optimized Real Amplitudes Ansatz eine bessere Optimierungsleistung als der Real Amplitudes Ansatz erzielt. Jedoch steigt die Anzahl der zu optimierenden Variablen in diesem Ansatz viel schneller als beim Real Amplitudes Ansatz (siehe Manuskript). Daher erfordert der Optimized Real Amplitudes Ansatz bei großen Problemen mehr Circuit-Ausführungen. Der Optimized Real Amplitudes Ansatz ist wahrscheinlich für Probleme mit bis zu 100 Qubits nützlich, aber es wird empfohlen, bei der Einstellung der population_size-Parameter achtsam zu sein. Als Beispiel für diesen Skalierungseffekt zeigt die vorherige Tabelle, dass für ein 84-Qubit-Problem der Optimized Real Amplitudes Ansatz eine population_size von 120 benötigt, während für ein 56-Qubit-Problem eine population_size von 40 ausreicht.
optimizer_settings = {
"de_optimizer_settings": {
"num_generations": 20,
"population_size": 90,
"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,
},
}
Es ist auch möglich, einen bestimmten Ansatz auszuwählen. Im Folgenden wird der 'Tailored'-Ansatz verwendet.
ansatz_settings = {
"ansatz": "tailored",
"multiple_passmanager": False,
}
4. Führe das Problem aus
dpo_job = dpo_solver.run(
assets=assets,
qubo_settings=qubo_settings,
optimizer_settings=optimizer_settings,
ansatz_settings=ansatz_settings,
backend_name="<backend name>",
previous_session_id=[],
apply_postprocess=True,
)
5. Ergebnisse abrufen
Wie im Abschnitt Ausgabe der API-Referenz beschrieben, gibt die Function ein Dictionary mit den Investitionstrajektorien zurück, geordnet vom niedrigsten zum höchsten nach ihrem Zielfunktionswert. Diese Ergebnismenge ermöglicht die Identifizierung der Trajektorie mit den niedrigsten Kosten und den dazugehörigen Investitionsbewertungen. Darüber hinaus ermöglicht sie die Analyse verschiedener Trajektorien und erleichtert die Auswahl derjenigen, die am besten mit spezifischen Bedürfnissen oder Zielen übereinstimmen. Diese Flexibilität stellt sicher, dass Entscheidungen auf eine Vielzahl von Präferenzen oder Szenarien zugeschnitten werden können. Beginne damit, die resultierende Strategie darzustellen, die die niedrigsten Zielkosten im Prozess erzielt hat.
# Get the results of the job
dpo_result = dpo_job.result()
# Show the solution strategy
dpo_result["result"]
{'time_step_0': {'8801.T': 0.11764705882352941,
'ITX.MC': 0.20588235294117646,
'META': 0.38235294117647056,
'GBPJPY=X': 0.058823529411764705,
'TMBMKDE-10Y': 0.0,
'CLF': 0.058823529411764705,
'XS2239553048': 0.17647058823529413},
'time_step_1': {'8801.T': 0.11428571428571428,
'ITX.MC': 0.14285714285714285,
'META': 0.2,
'GBPJPY=X': 0.02857142857142857,
'TMBMKDE-10Y': 0.42857142857142855,
'CLF': 0.0,
'XS2239553048': 0.08571428571428572},
'time_step_2': {'8801.T': 0.0,
'ITX.MC': 0.09375,
'META': 0.3125,
'GBPJPY=X': 0.34375,
'TMBMKDE-10Y': 0.0,
'CLF': 0.0,
'XS2239553048': 0.25},
'time_step_3': {'8801.T': 0.3939393939393939,
'ITX.MC': 0.09090909090909091,
'META': 0.12121212121212122,
'GBPJPY=X': 0.18181818181818182,
'TMBMKDE-10Y': 0.0,
'CLF': 0.0,
'XS2239553048': 0.21212121212121213}}
Anschließend kannst du mithilfe der Metadaten auf die Ergebnisse aller abgetasteten Strategien zugreifen. Damit kannst du die alternativen Trajektorien, die vom Optimierer zurückgegeben wurden, weiter analysieren. Lese dazu das im Dictionary dpo_result['metadata']['all_samples_metrics'] gespeicherte Dictionary, das nicht nur zusätzliche Informationen über die optimale Strategie enthält, sondern auch Details zu den anderen Kandidatenstrategien, die während der Optimierung ausgewertet wurden.
Das folgende Beispiel zeigt, wie diese Informationen mit pandas gelesen werden können, um wichtige Metriken der optimalen Strategie zu extrahieren. Dazu gehören Beschränkungsabweichung, Sharpe Ratio und die entsprechende Investitionsrendite.
# Convert metadata to a DataFrame
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']}")
Minimum Objective Cost Found: -3.78
Best Solution:
- Restriction Deviation: 40.0
- Sharpe Ratio: 24.82
- Return: 0.46
6. Leistungsanalyse
Analysiere abschließend die Leistung deiner Optimierungsanwendung. Vergleiche dabei deine im vorherigen Beispiel erhaltenen Ergebnisse mit einer zufälligen Basislinie, um die Wirksamkeit unseres Ansatzes zu beurteilen. Wenn der Quantenalgorithmus nachweislich und konsistent Ergebnisse mit niedrigeren Kostenwerten liefert, deutet dies auf einen effektiven Optimierungsprozess hin.
Die Abbildung zeigt die Wahrscheinlichkeitsverteilungen der Zielkosten. Um diese Verteilungen zu erzeugen, nimm die Liste der Zielkosten aus dem Function-Ergebnis und zähle das Auftreten jedes Kostenwerts (auf die zweite Dezimalstelle gerundete Werte). Aktualisiere dann die Häufigkeitsspalte entsprechend, indem du die Häufigkeiten identischer gerundeter Werte zusammenführst. Beachte, dass zur besseren visuellen Vergleichbarkeit die Häufigkeitsanzahlen normiert wurden, sodass jede Verteilung zwischen 0 und 1 angezeigt wird.
Wie in der Abbildung gezeigt (blaue durchgezogene Linie), ist die Kostenverteilung für unseren Variational Quantum Eigensolver (mit SQD nachbearbeitet) stark auf niedrigere Zielfunktionswerte konzentriert, was auf eine gute Optimierungsleistung hinweist. Im Gegensatz dazu zeigt die verrauschte Basislinie eine breitere Verteilung, die um höhere Kostenwerte zentriert ist. Die graue gestrichelte vertikale Linie repräsentiert den Mittelwert der Zufallsverteilung und unterstreicht damit die Konsistenz der Function bei der Rückgabe optimierter Investitionsstrategien. Zum zusätzlichen Vergleich entspricht die schwarze gestrichelte Linie in der Abbildung der Lösung, die mit dem Gurobi-Optimierer (kostenlose Version) erzielt wurde. Alle diese Ergebnisse werden in den Benchmarks unten für das Beispiel "Mixed Assets" mit dem "Tailored"-Ansatz weiter untersucht.
Benchmarks
Diese Function wurde unter verschiedenen Konfigurationen von Auflösungs-Qubits, Ansatz-Circuits und Gruppen von Vermögenswerten aus verschiedenen Sektoren getestet: eine Mischung verschiedener Vermögenswerte (Set 1), Ölderivate (Set 2) und IBEX35 (Set 3). Weitere Details sind in der folgenden Tabelle zu finden.
| Set | Datum | Vermögenswerte |
|---|---|---|
| Set 1 | 01.01.2023 | 8801.T, CL=F, GBPJPY=X, ITX.MC, META, TMBMKDE-10Y, XS2239553048 |
| Set 2 | 01.06.2023 | CL=F, BZ=F, HO=F, NG=F, XOM, RB=F, 2222.SR |
| Set 3 | 01.11.2022 | ACS.MC, ITX.MC, FER.MC, ELE.MC, SCYR.MC, AENA.MC, AMS.MC |
Zwei Schlüsselmetriken wurden zur Bewertung der Lösungsqualität herangezogen.
- Die Zielkosten, die die Optimierungseffizienz messen, indem der Wert der Kostenfunktion aus jedem Experiment mit den Ergebnissen von Gurobi (kostenlose Version) verglichen wird.
- Die Sharpe Ratio, die die risikobereinigte Rendite jedes Portfolios erfasst und Einblicke in die finanzielle Performance der Lösungen bietet.
Zusammen bilden diese Metriken sowohl die rechnerischen als auch die finanziellen Aspekte der quantenerzeugten Portfolios ab.
| Beispiel | Qubits | Ansatz | Tiefe | Laufzeitnutzung (s) | Gesamtnutzung (s) | Zielkosten | Sharpe | Gurobi Zielkosten | Gurobi Sharpe |
|---|---|---|---|---|---|---|---|---|---|
| Mixed Assets (Set 1, 4 Zeitschritte, 4-bit) | 112 | Tailored | 83 | 12735 | 13095 | -3.78 | 24.82 | -4.25 | 24.71 |
| Mixed Assets (Set 1,4 Zeitschritte, 4 Zeitschritte, 4-bit) | 112 | Real Amplitudes | 359 | 11739 | 11903 | -3.39 | 23.64 | -4.25 | 24.71 |
| Oil Derivatives (Set 2, 4 Zeitschritte, 3-bit) | 84 | Optimized Real Amplitudes | 78 | 6180 | 6350 | -3.73 | 19.13 | -4.19 | 21.71 |
| IBEX35 (Set 3, 4 Zeitschritte, 2-bit) | 56 | Optimized Real Amplitudes | 96 | 3314 | 3523 | -3.67 | 14.48 | -4.11 | 16.44 |
Die Ergebnisse zeigen, dass der Quantenoptimierer mit problemspezifischen Ansätzen effektiv effiziente Investitionsstrategien für verschiedene Portfolio-Typen identifiziert.
Im Folgenden werden sowohl die Populationsgröße als auch die Anzahl der Generationen angegeben, die im optimizer_options-Dictionary festgelegt wurden. Alle anderen Parameter wurden auf ihre Standardwerte gesetzt.
| Beispiel | population_size | num_generations |
|---|---|---|
| Mixed Assets Portfolio | 90 | 20 |
| Mixed Assets Portfolio | 92 | 20 |
| Oil Derivatives Portfolio | 120 | 20 |
| IBEX35 Portfolio | 40 | 20 |
Die Anzahl der Generationen wurde auf 20 gesetzt, da sich dieser Wert als ausreichend erwiesen hat, um Konvergenz zu erreichen. Darüber hinaus wurden die Standardwerte für die internen Parameter des Optimierers unverändert gelassen, da sie durchgehend gute Leistung erbrachten und im Allgemeinen von der Literatur und den Implementierungsrichtlinien empfohlen werden.
Support erhalten
Wenn du Hilfe benötigst, kannst du eine E-Mail an qpo.support@globaldataquantum.com senden. Gib in deiner Nachricht die Function-Job-ID an.
Nächste Schritte
- Lies das zugehörige Forschungspapier.
-
- Besuche die API-Referenz für diese Qiskit Function.
- Beantrage Zugang zur Function, indem du dieses Formular ausfüllst.
- Probiere das Tutorial Dynamische Portfolio-Optimierung aus.