Quantum Portfolio Optimizer: Eine Qiskit Function von Global Data Quantum
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.
Eingabe
Die Eingabeargumente der Function sind in der folgenden Tabelle beschrieben. Die Vermögensdaten und andere Problemspezifikationen müssen angegeben werden; zusätzlich können die VQE-Einstellungen zur Anpassung des Optimierungsprozesses angegeben werden.
| Name | Typ | Beschreibung | Erforderlich | Standard | Beispiel |
|---|---|---|---|---|---|
| assets | json | Dictionary mit den Kursen der Vermögenswerte | Ja | - | - |
| qubo_settings | json | Einstellungen des QUBO | Ja | - | Siehe Beispiele in der Tabelle unten |
| ansatz_settings | json | Einstellungen des Ansatzes | Nein | None | Siehe Beispiele in der Tabelle unten. |
| optimizer_settings | json | Einstellungen des Optimierers | Nein | None | Siehe Beispiele in der Tabelle unten. |
| backend | str | Der Name des QPU-Backends | Nein | - | "ibm_torino" |
| previous_session_id | list of str | Liste der Session-IDs zum Abrufen von Daten aus früheren Läufen(*) | Nein | Leere Liste | ["session_id_1", "session_id_2"] |
| apply_postprocess | bool | Rauschbewusste SQD-Nachbearbeitung anwenden | Nein | True | True |
| tags | list of strings | Liste von Tags zur Identifikation des Experiments | Nein | Leere Liste | ["optimization", "quantum_computing"] |
*Um eine Ausführung fortzusetzen oder Jobs abzurufen, die in einer oder mehreren früheren Sessions verarbeitet wurden, muss die Liste der Session-IDs im Parameter previous_session_id übergeben werden. Dies ist besonders nützlich, wenn ein Optimierungsauftrag aufgrund eines Fehlers im Prozess nicht abgeschlossen werden konnte und die Ausführung beendet werden muss. Dazu müssen dieselben Argumente wie bei der ersten Ausführung sowie die previous_session_id-Liste wie beschrieben angegeben werden.
Das Laden von Daten früherer Sessions (zum Fortsetzen einer Optimierung) kann bis zu einer Stunde dauern.
assets
Die Daten müssen als JSON-Objekt strukturiert sein, das Informationen über die Schlusskurse von Finanzanlagen an bestimmten Daten speichert. Das Format ist wie folgt:
- Primärschlüssel (string): Der Name oder das Ticker-Symbol des Finanzwerts (z. B. "8801.T").
- Sekundärschlüssel (string): Das Datum im Format YYYY-MM-DD.
- Wert (number): Der Schlusskurs des Vermögenswerts am angegebenen Datum. Kurse können normiert oder nicht normiert eingegeben werden.
Beachte, dass alle Dictionaries denselben Sekundärschlüssel (Datum) haben müssen. Fehlt einem bestimmten Vermögenswert ein Datum, das bei anderen vorhanden ist, müssen die Daten aufgefüllt werden, um Konsistenz zu gewährleisten. Dies kann beispielsweise durch Verwendung des letzten verfügbaren Schlusskurses dieses Vermögenswerts erfolgen.
Beispiel
{
"8801.T": {
"2023-01-01": 2374.0,
"2023-01-02": 2374.0,
"2023-01-03": 2374.0,
"2023-01-04": 2356.5,
...
},
"AAPL": {
"2023-01-01": 145.2,
"2023-01-02": 146.5,
"2023-01-03": 147.3,
"2023-01-04": 148.1,
...
},
...
}
# Added by doQumentation — required packages for this notebook
!pip install -q pandas qiskit-ibm-catalog
{
"asset_name": {
"date": closing_value,
...
},
...
}
Die Vermögensdaten müssen mindestens die Schlusskurse bei (nt+1) * dt (siehe Abschnitt qubo_settings Eingabe) Zeitstempeln (z. B. Tagen) enthalten.
qubo_settings
Die folgende Tabelle beschreibt die Schlüssel des qubo_settings-Dictionarys. Erstelle das Dictionary, indem du die Anzahl der Zeitschritte nt, die Anzahl der Auflösungs-Qubits nq und die max_investment angibst – oder andere Standardwerte änderst.
| Name | Typ | Beschreibung | Erforderlich | Standard | Beispiel |
|---|---|---|---|---|---|
| nt | int | Anzahl der Zeitschritte | Ja | - | 4 |
| nq | int | Anzahl der Auflösungs-Qubits | Ja | - | 4 |
| max_investment | float | Maximale Anzahl investierter Währungseinheiten über alle Vermögenswerte | Ja | - | 10 |
| dt* | int | Betrachtetes Zeitfenster in jedem Zeitschritt. Die Einheit entspricht den Zeitintervallen zwischen den Schlüsseln in den Vermögensdaten | Nein | 30 | - |
| risk_aversion | float | Risikoaversionskoeffizient | Nein | 1000 | - |
| transaction_fee | float | Transaktionskostenkoeffizient | Nein | 0.01 | - |
| restriction_coeff | float | Lagrange-Multiplikator zur Durchsetzung der Problembeschränkung innerhalb der QUBO-Formulierung | Nein | 1 | - |
ansatz_settings
Um die Standardoptionen zu ändern, erstelle ein Dictionary für den Parameter ansatz_settings mit den folgenden Schlüsseln. Standardmäßig ist der Ansatz auf "real_amplitudes" gesetzt, und beide zusätzlichen Optionen (siehe folgende Tabelle) sind auf False gesetzt.
| Name | Typ | Beschreibung | Erforderlich | Standard |
|---|---|---|---|---|
| ansatz* | str | Zu verwendender Ansatz | Nein | "real_amplitudes" |
| multiple_passmanager** | bool | Aktiviert die Subroutine mit mehreren Passmanagern (nicht verfügbar für den Tailored Ansatz) | Nein | False |
| dd_enable | bool | Fügt dynamische Entkopplung hinzu | Nein | False |
* Verfügbare Ansätze
real_amplitudescyclicoptimized_real_amplitudestailored(Nur füribm_torinoBackend, 7 Assets, 4 Zeitschritte und 4 Auflösungs-Qubits)
** Wenn multiple_passmanager auf False gesetzt ist, verwendet die Function den Standard-Qiskit-Passmanager mit optimization_level=3. Wenn auf True gesetzt, vergleicht die Subroutine multiple_passmanager drei Passmanager: den vorherigen Standard-Qiskit-Passmanager, einen Passmanager, der Qubits über die Kette der nächsten Nachbarn des QPU abbildet, und die KI-Transpiler-Dienste. Anschließend wird der Passmanager mit dem geschätzten niedrigsten kumulativen Fehler ausgewählt.
optimizer_settings
Dieser Parameter ist ein Dictionary mit einigen anpassbaren Optionen des Optimierungsprozesses.
| Name | Typ | Beschreibung | Erforderlich | Standard |
|---|---|---|---|---|
| primitive_options | json | Einstellungen des Primitives | Nein | - |
| optimizer | str | Ausgewählter klassischer Optimierer | Nein | "differential_evolution" |
| optimizer_options | json | Konfiguration des Optimierers | Nein | - |
Derzeit ist die einzige verfügbare Optimiereroption "differential_evolution".
Unter den Schlüsseln primitive_options und optimizer_options werden Dictionaries mit den folgenden Parametern festgelegt:
primitive_options
| Name | Typ | Beschreibung | Erforderlich | Standard | Beispiel |
|---|---|---|---|---|---|
| sampler_shots | int | Anzahl der Shots des Samplers. | Nein | 100000 | - |
| estimator_shots | int | Anzahl der Shots des Estimators. | Nein | 25000 | - |
| estimator_precision | float | Gewünschte Präzision des Erwartungswerts. Wenn angegeben, wird die Präzision anstelle von estimator_shots verwendet. | Nein | None | 0.015625 · (1 / sqrt(4096)) |
| max_time | int oder str | Maximale Zeit, die eine Runtime-Session offen bleiben kann, bevor sie zwangsweise geschlossen wird. Kann in Sekunden (int) oder als String angegeben werden, z. B. "2h 30m 40s". Muss kleiner als das systemseitig festgelegte Maximum sein. | Nein | None | "1h 15m" |
optimizer_options
| Name | Typ | Beschreibung | Erforderlich | Standard |
|---|---|---|---|---|
| num_generations | int | Anzahl der Generationen | Nein | 20 |
| population_size | int | Größe der Population | Nein | 20 |
| mutation_range | list | Maximaler und minimaler Mutationsfaktor | Nein | [0, 0.25] |
| recombination | float | Rekombinationsfaktor | Nein | 0.4 |
| max_parallel_jobs | int | Maximale Anzahl parallel ausgeführter QPU-Jobs | Nein | 3 |
| max_batchsize | int | Maximale Batch-Größe | Nein | 200 |
-
Die Anzahl der durch die differenzielle Evolution ausgewerteten Generationen beträgt
num_generations+ 1, da die Ausgangspopulation mitgezählt wird. -
Die Gesamtanzahl der Circuits berechnet sich als
(num_generations + 1) * population_size. -
Die Verwendung einer größeren Populationsgröße und mehr Generationen verbessert im Allgemeinen die Qualität der Optimierungsergebnisse. Es wird jedoch nicht empfohlen, eine Populationsgröße von 120 und eine Generationenanzahl von mehr als 20 zu überschreiten (z. B.
120 * 21 = 2520Circuits insgesamt), da dies eine übermäßige Anzahl von Circuits erzeugt, die rechenintensiv und zeitaufwendig in der Verarbeitung sein können. -
Die Function ermöglicht es dir, eine frühere Optimierung fortzusetzen, und es ist immer möglich, die Anzahl der Generationen zu erhöhen (indem dieselbe Eingabe außer
previous_session_idund einem erhöhtennum_generationsangegeben wird).
Stelle die Einhaltung der Qiskit Runtime Job-Limits sicher.
- Sampler:
sampler_shots <= 10_000_000. - Estimator:
max_batchsize * estimator_shots * observable_size <= 10_000_000(für diese Function kommutieren alle Terme des Observablen, daher giltobservable_size=1).
Weitere Informationen findest du im Leitfaden Job-Limits.
Ausgabe
Die Function gibt zwei Dictionaries zurück: das Dictionary "result", das die besten Optimierungsergebnisse enthält, einschließlich der optimalen Lösung und der dazugehörigen minimalen Zielkosten; sowie "metadata" mit Daten aus allen während des Optimierungsprozesses erhaltenen Ergebnissen und den jeweiligen Metriken.
Das erste Dictionary konzentriert sich auf die leistungsstärkste Lösung, während das zweite detaillierte Informationen über alle Lösungen bereitstellt, einschließlich der Zielkosten und anderer relevanter Metriken.
Ausgabe-Dictionaries:
| Name | Typ | Beschreibung | Beispiel |
|---|---|---|---|
| result | dict[str, dict[str, float]] | Enthält die Investitionsstrategie über die Zeit, wobei jeder Zeitstempel den asset-spezifischen Investitionsgewichten zugeordnet ist (jedes Gewicht ist der Investitionsbetrag normiert auf den Gesamtinvestitionsbetrag). | {'time_1': {'asset_1': 0.2, 'asset_2': 0.3, ...\}, ...\} |
| metadata | dict[str, Any] | Während der Analyse erzeugte Daten, einschließlich Lösungen, Kosten und Metriken. | Siehe Beispiele unten |
Beschreibung des metadata-Dictionarys
| Name | Typ | Beschreibung | Beispiel |
|---|---|---|---|
| session_id | str | Eindeutiger Bezeichner für die IBM Quantum Session. | "d0h30qjvpqf00084fgw0" |
| all_samples_metrics | dict | Dictionary mit verschiedenen Metriken für jede nachbearbeitete Probe, z. B. Kosten oder Beschränkungen. | Siehe Beschreibung unten |
| sampler_counts | dict[str, int] | Dictionary, bei dem Schlüssel Bitketten-Darstellungen der abgetasteten Lösungen sind und die Werte deren Häufigkeiten sind. | {"101010": 3, "111000": 1\} |
| asset_order | list[str] | Liste mit der entsprechenden Investitionsreihenfolge der Vermögenswerte zu jedem Zeitschritt innerhalb der Investitionsstrategien. | ["Asset_0", "Asset_1", "Asset_3"] |
| QUBO | list[list[float]] | QUBO-Matrix des Problems. | [[-6.96e-01, 5.81e-01, -1.26e-02, 0.00e+00], ...] |
| resource_summary | dict[str, dict[str, float]] | Zusammenfassung der CPU- und QPU-Nutzungszeiten (in Sekunden) über verschiedene Prozessphasen. | {'RUNNING: EXECUTING_QPU': {'CPU_TIME': 412.84, 'QPU_TIME': 87.22\}, ...\} |
Beschreibung des all_samples_metrics-Dictionarys
| Name | Typ | Beschreibung | Beispiel |
|---|---|---|---|
| investment_trajectories | list[list] | Aus dekodierten Quantenzuständen abgeleitete Investitionsstrategien. | [[1, 2, 2], [1, 2, 1]] |
| counts | list[int] | Anzahl der Stichproben jeder Investitionstrajektorie. Index entspricht investment_trajectories. | [5, 3] |
| objective_costs | list[float] | Wert der Zielfunktion für jede Investitionstrajektorie, geordnet vom niedrigsten zum höchsten. | [0.98, 1.25] |
| sharpe_ratios | list[float] | Risikobereinigte Performance (Sharpe Ratio) für jede Investitionstrajektorie. Indexbasiert ausgerichtet. | [1.1, 0.7] |
| returns | list[float] | Erwartete Rendite für jede Investitionstrajektorie. Indexbasiert ausgerichtet. | [0.15, 0.10] |
| rest_breaches | list[float] | Maximale Beschränkungsabweichung innerhalb jeder Investitionstrajektorie. Indexbasiert ausgerichtet. | [0.0, 0.25] |
| transaction_costs | list[float] | Geschätzte Transaktionskosten für jede Investitionstrajektorie. Indexbasiert ausgerichtet. | [0.01, 0.02] |
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.)
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 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.
- Beantrage Zugang zur Function, indem du dieses Formular ausfüllst.
- Probiere das Tutorial Dynamische Portfolio-Optimierung aus.