Quantum Portfolio Optimizer: Una Qiskit Function di Global Data Quantum
Le Qiskit Functions sono una funzionalità sperimentale disponibile solo per gli utenti dei piani IBM Quantum® Premium, Flex e On-Prem (tramite IBM Quantum Platform API). Sono in stato di anteprima e soggette a modifiche.
Panoramica
Il Quantum Portfolio Optimizer è una Qiskit Function che affronta il problema dell'ottimizzazione dinamica del portafoglio, un problema standard in finanza che mira a ribilanciare periodicamente gli investimenti su un insieme di asset, massimizzando i rendimenti e minimizzando i rischi. Grazie all'impiego di tecniche di ottimizzazione quantistica all'avanguardia, questa funzione semplifica il processo in modo che gli utenti, anche senza competenze nel calcolo quantistico, possano beneficiare dei suoi vantaggi nella ricerca di traiettorie di investimento ottimali. Ideale per gestori di portafoglio, ricercatori in finanza quantitativa e investitori individuali, questo strumento consente il back-testing di strategie di trading nell'ottimizzazione del portafoglio.
Descrizione della funzione
La funzione Quantum Portfolio Optimizer utilizza l'algoritmo Variational Quantum Eigensolver (VQE) per risolvere un problema di ottimizzazione binaria quadratica non vincolata (QUBO), affrontando i problemi di ottimizzazione dinamica del portafoglio. Gli utenti devono semplicemente fornire i dati sui prezzi degli asset e definire il vincolo di investimento; la funzione esegue quindi il processo di ottimizzazione quantistica che restituisce un insieme di traiettorie di investimento ottimizzate.
Il processo si articola in quattro fasi principali. Prima di tutto, i dati di input vengono mappati su un problema compatibile con il calcolo quantistico, costruendo il QUBO del problema di ottimizzazione dinamica del portafoglio e trasformandolo in un operatore quantistico (Hamiltoniano di Ising). Successivamente, il problema di input e l'algoritmo VQE vengono adattati per essere eseguiti sull'hardware quantistico. L'algoritmo VQE viene poi eseguito sull'hardware quantistico e, infine, i risultati vengono post-elaborati per fornire le traiettorie di investimento ottimali. Il sistema include anche un post-processing noise-aware (basato su SQD) per massimizzare la qualità dell'output.
Questa Qiskit Function è basata sul manoscritto pubblicato da Global Data Quantum.
Input
Gli argomenti di input della funzione sono descritti nella tabella seguente. I dati degli asset e le altre specifiche del problema devono essere forniti; è inoltre possibile includere le impostazioni VQE per personalizzare il processo di ottimizzazione.
| Nome | Tipo | Descrizione | Obbligatorio | Default | Esempio |
|---|---|---|---|---|---|
| assets | json | Dizionario con i prezzi degli asset | Sì | - | - |
| qubo_settings | json | Impostazioni del QUBO | Sì | - | Vedi gli esempi nella tabella seguente |
| ansatz_settings | json | Impostazioni dell'ansatz | No | None | Vedi gli esempi nella tabella seguente. |
| optimizer_settings | json | Impostazioni dell'ottimizzatore | No | None | Vedi gli esempi nella tabella seguente. |
| backend | str | Nome del backend QPU | No | - | "ibm_torino" |
| previous_session_id | list of str | Lista degli ID di sessione per recuperare dati da esecuzioni precedenti(*) | No | Lista vuota | ["session_id_1", "session_id_2"] |
| apply_postprocess | bool | Applica il post-processing SQD noise-aware | No | True | True |
| tags | list of strings | Lista di tag per identificare l'esperimento | No | Lista vuota | ["optimization", "quantum_computing"] |
*Per riprendere un'esecuzione o recuperare job elaborati in una o più sessioni precedenti, la lista degli ID di sessione deve essere passata nel parametro previous_session_id. Ciò è particolarmente utile nei casi in cui un'attività di ottimizzazione non è riuscita a completarsi a causa di un errore nel processo e l'esecuzione deve terminare. Per farlo, devi fornire gli stessi argomenti usati nell'esecuzione iniziale, insieme alla lista previous_session_id come descritto.
Il caricamento dei dati per le sessioni precedenti (per riprendere un'ottimizzazione) può richiedere fino a un'ora.
assets​
I dati devono essere strutturati come un oggetto JSON che memorizza informazioni sui prezzi di chiusura degli asset finanziari in date specifiche. Il formato è il seguente:
- Chiave primaria (stringa): Il nome o il simbolo ticker dell'asset finanziario (ad esempio, "8801.T").
- Chiave secondaria (stringa): La data nel formato YYYY-MM-DD.
- Valore (numero): Il prezzo di chiusura dell'asset nella data specificata. I prezzi possono essere inseriti normalizzati o non normalizzati.
Nota: tutti i dizionari devono avere la stessa chiave secondaria (date). Se un asset specifico non ha una data presente negli altri, i dati devono essere completati per garantire la coerenza. Ad esempio, questo può essere fatto usando l'ultimo prezzo di chiusura registrato per quell'asset.
Esempio​
{
"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,
...
},
...
}
I dati degli asset devono contenere almeno i prezzi di chiusura in (nt+1) * dt (vedi la sezione di input qubo_settings) timestamp (ad esempio, giorni).
qubo_settings​
La tabella seguente descrive le chiavi del dizionario qubo_settings. Costruisci il dizionario specificando il numero di step temporali nt, il numero di qubit di risoluzione nq e il max_investment, oppure modifica gli altri valori predefiniti.
| Nome | Tipo | Descrizione | Obbligatorio | Default | Esempio |
|---|---|---|---|---|---|
| nt | int | Numero di step temporali | Sì | - | 4 |
| nq | int | Numero di qubit di risoluzione | Sì | - | 4 |
| max_investment | float | Numero massimo di unità di valuta investite su tutti gli asset | Sì | - | 10 |
| dt* | int | Finestra temporale considerata in ogni step. L'unità corrisponde agli intervalli temporali tra le chiavi nei dati degli asset | No | 30 | - |
| risk_aversion | float | Coefficiente di avversione al rischio | No | 1000 | - |
| transaction_fee | float | Coefficiente della commissione di transazione | No | 0.01 | - |
| restriction_coeff | float | Moltiplicatore di Lagrange usato per imporre il vincolo del problema nella formulazione QUBO | No | 1 | - |
ansatz_settings​
Per modificare le opzioni predefinite, crea un dizionario per il parametro ansatz_settings con le seguenti chiavi. Per default, l'ansatz è impostato su "real_amplitudes" e entrambe le opzioni aggiuntive (vedi la tabella seguente) sono impostate su False.
| Nome | Tipo | Descrizione | Obbligatorio | Default |
|---|---|---|---|---|
| ansatz* | str | Ansatz da utilizzare | No | "real_amplitudes" |
| multiple_passmanager** | bool | Abilita la subroutine multiple passmanager (non disponibile per l'ansatz Tailored) | No | False |
| dd_enable | bool | Aggiunge il dynamical decoupling | No | False |
* Ansatz disponibili
real_amplitudescyclicoptimized_real_amplitudestailored(Solo per il backendibm_torino, 7 asset, 4 step temporali e 4 qubit di risoluzione)
** Se multiple_passmanager è impostato su False, la funzione usa il pass manager predefinito di Qiskit con optimization_level=3. Se impostato su True, la subroutine multiple_passmanager confronta tre pass manager: il precedente pass manager predefinito di Qiskit, un pass manager che mappa i qubit sulla catena di vicini più prossimi del QPU, e i servizi AI transpiler. Viene quindi selezionato il pass manager con il minore errore cumulativo stimato.
optimizer_settings​
Questo parametro è un dizionario con alcune opzioni configurabili del processo di ottimizzazione.
| Nome | Tipo | Descrizione | Obbligatorio | Default |
|---|---|---|---|---|
| primitive_options | json | Impostazioni della primitiva | No | - |
| optimizer | str | Ottimizzatore classico selezionato | No | "differential_evolution" |
| optimizer_options | json | Configurazione dell'ottimizzatore | No | - |
Attualmente, l'unica opzione di ottimizzatore disponibile è "differential_evolution".
Nelle chiavi primitive_options e optimizer_options si impostano dizionari con i seguenti parametri:
primitive_options​
| Nome | Tipo | Descrizione | Obbligatorio | Default | Esempio |
|---|---|---|---|---|---|
| sampler_shots | int | Numero di shot del Sampler. | No | 100000 | - |
| estimator_shots | int | Numero di shot dell'Estimator. | No | 25000 | - |
| estimator_precision | float | Precisione desiderata del valore atteso. Se specificata, verrà usata al posto di estimator_shots. | No | None | 0.015625 · (1 / sqrt(4096)) |
| max_time | int or str | Durata massima durante la quale una sessione di runtime può rimanere aperta prima di essere chiusa forzatamente. Può essere espressa in secondi (int) o come stringa, ad esempio "2h 30m 40s". Deve essere inferiore al massimo imposto dal sistema. | No | None | "1h 15m" |
optimizer_options​
| Nome | Tipo | Descrizione | Obbligatorio | Default |
|---|---|---|---|---|
| num_generations | int | Numero di generazioni | No | 20 |
| population_size | int | Dimensione della popolazione | No | 20 |
| mutation_range | list | Fattore di mutazione massimo e minimo | No | [0, 0.25] |
| recombination | float | Fattore di ricombinazione | No | 0.4 |
| max_parallel_jobs | int | Numero massimo di job QPU eseguiti in parallelo | No | 3 |
| max_batchsize | int | Dimensione massima del batch | No | 200 |
-
Il numero di generazioni valutate dall'evoluzione differenziale è
num_generations+ 1, poiché la popolazione iniziale è inclusa. -
Il numero totale di circuito è calcolato come
(num_generations + 1) * population_size. -
Usare una popolazione più grande e un maggior numero di generazioni migliora generalmente la qualità dei risultati dell'ottimizzazione. Tuttavia, non è consigliabile superare una dimensione di popolazione di 120 e un numero di generazioni maggiore di 20 (ad esempio,
120 * 21 = 2520circuito totali), poiché ciò genererebbe un numero eccessivo di circuito, che può essere computazionalmente costoso e richiedere molto tempo. -
La funzione ti consente di riprendere ottimizzazioni precedenti, ed è sempre possibile aumentare il numero di generazioni (fornendo lo stesso input ad eccezione di
previous_session_ide un valore aumentato dinum_generations).
Assicurati di rispettare i limiti dei job di Qiskit Runtime.
- Sampler:
sampler_shots <= 10_000_000. - Estimator:
max_batchsize * estimator_shots * observable_size <= 10_000_000(per questa funzione, tutti i termini dell'osservabile commutano, quindiobservable_size=1).
Consulta la guida sui limiti dei job per ulteriori informazioni.
Output
La funzione restituisce due dizionari: il dizionario "result", che contiene i migliori risultati dell'ottimizzazione, inclusa la soluzione ottimale e il suo costo obiettivo minimo associato; e "metadata", con i dati di tutti i risultati ottenuti durante il processo di ottimizzazione, insieme alle rispettive metriche.
Il primo dizionario si concentra sulla soluzione con le migliori prestazioni, mentre il secondo fornisce informazioni dettagliate su tutte le soluzioni, inclusi i costi obiettivo e altre metriche rilevanti.
Dizionari di output:​
| Nome | Tipo | Descrizione | Esempio |
|---|---|---|---|
| result | dict[str, dict[str, float]] | Contiene la strategia di investimento nel tempo, con ogni timestamp che mappa i pesi di investimento specifici per asset (ogni peso è l'importo di investimento normalizzato rispetto all'importo totale investito). | {'time_1': {'asset_1': 0.2, 'asset_2': 0.3, ...\}, ...\} |
| metadata | dict[str, Any] | Dati generati durante l'analisi, inclusi soluzioni, costi e metriche. | Vedi gli esempi di seguito |
Descrizione del dizionario metadata​
| Nome | Tipo | Descrizione | Esempio |
|---|---|---|---|
| session_id | str | Identificatore univoco della sessione IBM Quantum. | "d0h30qjvpqf00084fgw0" |
| all_samples_metrics | dict | Dizionario contenente varie metriche per ogni campione post-elaborato, come costi o vincoli. | Vedi la descrizione di seguito |
| sampler_counts | dict[str, int] | Dizionario in cui le chiavi sono rappresentazioni in formato bitstring delle soluzioni campionate e i valori sono i loro conteggi. | {"101010": 3, "111000": 1\} |
| asset_order | list[str] | Lista con l'ordine di investimento corrispondente degli asset per ogni step temporale all'interno delle strategie di investimento. | ["Asset_0", "Asset_1", "Asset_3"] |
| QUBO | list[list[float]] | Matrice QUBO del problema. | [[-6.96e-01, 5.81e-01, -1.26e-02, 0.00e+00], ...] |
| resource_summary | dict[str, dict[str, float]] | Riepilogo dei tempi di utilizzo CPU e QPU (in secondi) nelle diverse fasi del processo. | {'RUNNING: EXECUTING_QPU': {'CPU_TIME': 412.84, 'QPU_TIME': 87.22\}, ...\} |
Descrizione del dizionario all_samples_metrics​
| Nome | Tipo | Descrizione | Esempio |
|---|---|---|---|
| investment_trajectories | list[list] | Strategie di investimento derivate dagli stati quantistici decodificati. | [[1, 2, 2], [1, 2, 1]] |
| counts | list[int] | Numero di volte in cui ogni traiettoria di investimento è stata campionata. L'indice corrisponde a investment_trajectories. | [5, 3] |
| objective_costs | list[float] | Valore della funzione obiettivo per ogni traiettoria di investimento, ordinato dal valore più basso al più alto. | [0.98, 1.25] |
| sharpe_ratios | list[float] | Rendimento corretto per il rischio (indice di Sharpe) per ogni traiettoria di investimento. Allineato per indice. | [1.1, 0.7] |
| returns | list[float] | Rendimento atteso per ogni traiettoria di investimento. Allineato per indice. | [0.15, 0.10] |
| rest_breaches | list[float] | Deviazione massima dal vincolo all'interno di ogni traiettoria di investimento. Allineato per indice. | [0.0, 0.25] |
| transaction_costs | list[float] | Costo di transazione stimato associato a ogni traiettoria di investimento. Allineato per indice. | [0.01, 0.02] |
Per iniziare
Autenticati usando la tua chiave API e seleziona la Qiskit Function come segue. (Questo snippet presuppone che tu abbia già salvato il tuo account nell'ambiente locale.)
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")
Esempio: ottimizzazione dinamica del portafoglio con sette asset​
Questo esempio mostra come eseguire la funzione di ottimizzazione dinamica del portafoglio (DPO) e regolare le sue impostazioni per ottenere le prestazioni ottimali. Include passaggi dettagliati per la messa a punto dei parametri al fine di ottenere i risultati desiderati.
Questo caso prevede sette asset, quattro step temporali e quattro qubit di risoluzione, per un requisito totale di 112 qubit.
1. Leggi gli asset inclusi nel portafoglio.​
Se tutti gli asset del portafoglio sono memorizzati in una cartella a un percorso specifico, puoi caricarli in un pandas.DataFrame e convertirlo in un oggetto in formato dict usando la seguente funzione.
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()
Per questo esempio, abbiamo utilizzato gli asset 8801.T, CLF, GBPJPY, ITX.MC, META, TMBMKDE-10Y e XS2239553048. La figura seguente illustra i dati usati in questo esempio, mostrando l'evoluzione giornaliera del prezzo di chiusura degli asset dal 1° gennaio al 1° settembre 2023.
In questo esempio, per garantire l'uniformità tra le date, abbiamo compilato i giorni non di negoziazione con il prezzo di chiusura della data precedente disponibile. Applichiamo questo passaggio perché gli asset selezionati provengono da mercati diversi con giorni di negoziazione variabili, rendendo essenziale standardizzare il dataset per coerenza.
2. Definisci il problema.​
Definisci le specifiche del problema configurando i parametri nel dizionario qubo_settings.
qubo_settings = {
"nt": 4,
"nq": 4,
"dt": 30,
"max_investment": 25,
"risk_aversion": 1000.0,
"transaction_fee": 0.01,
"restriction_coeff": 1.0,
}
3. Definisci le impostazioni dell'ottimizzatore e dell'ansatz. (Facoltativo)​
Facoltativamente, definisci i requisiti specifici per il processo di ottimizzazione, inclusa la selezione dell'ottimizzatore e dei suoi parametri, nonché la specifica della primitiva e delle sue configurazioni.
Per il Tailored Ansatz, la dimensione della popolazione scelta si basa su esperimenti precedenti che hanno dimostrato che questo valore produce un'ottimizzazione stabile ed efficiente.
Nel caso del Real Amplitudes Ansatz, puoi seguire una relazione lineare tra population_size e il numero di qubit nel circuito. Come regola empirica approssimativa, si consiglia di usare un population_size minimo ~ 0.8 * n_qubits per l'ansatz real_amplitudes.
Si prevede che l'Optimized Real Amplitudes abbia prestazioni di ottimizzazione migliori rispetto all'ansatz Real Amplitudes. Tuttavia, il numero di variabili da ottimizzare in questo ansatz cresce molto più rapidamente rispetto al caso Real Amplitudes (vedi il manoscritto). Pertanto, per problemi di grandi dimensioni, l'Optimized Real Amplitudes richiede più esecuzioni di circuito. L'Optimized Real Amplitudes è probabilmente utile per problemi fino a 100 qubit, ma si consiglia di essere prudenti nell'impostare i parametri population_size. Come esempio di questo aumento di scala in population_size, la tabella precedente mostra che per un problema a 84 qubit, l'Optimize Real Amplitudes richiede 120 di population_size, mentre per un problema a 56 qubit è sufficiente un population_size di 40.
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,
},
}
È anche possibile scegliere un ansatz specifico. Il seguente usa l'ansatz 'Tailored'.
ansatz_settings = {
"ansatz": "tailored",
"multiple_passmanager": False,
}
4. Esegui il problema.​
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. Recupera i risultati.​
Come indicato nella sezione Output, la funzione restituisce un dizionario con le traiettorie di investimento ordinate dalla più bassa alla più alta in base al valore della funzione obiettivo. Questo insieme di risultati consente di identificare la traiettoria con il costo più basso e le relative valutazioni di investimento. Inoltre, permette l'analisi di diverse traiettorie, facilitando la selezione di quelle più adatte alle esigenze o agli obiettivi specifici. Questa flessibilità garantisce che le scelte possano essere personalizzate per adattarsi a una varietà di preferenze o scenari. Inizia presentando la strategia risultante che ha raggiunto il costo obiettivo più basso trovato durante il processo.
# 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}}
Successivamente, usando i metadati, puoi accedere ai risultati di tutte le strategie campionate. Puoi quindi analizzare ulteriormente le traiettorie alternative restituite dall'ottimizzatore. Per farlo, leggi il dizionario memorizzato in dpo_result['metadata']['all_samples_metrics'], che contiene non solo informazioni aggiuntive sulla strategia ottimale, ma anche i dettagli delle altre strategie candidate valutate durante l'ottimizzazione.
Il seguente esempio mostra come leggere queste informazioni usando pandas per estrarre le metriche chiave associate alla strategia ottimale. Queste includono la Deviazione dal Vincolo, il Rapporto di Sharpe e il rendimento di investimento corrispondente.
# 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. Analisi delle prestazioni​
Infine, analizza le prestazioni della tua applicazione di ottimizzazione. In particolare, confronta i tuoi risultati, ottenuti nell'esempio precedente, con una baseline casuale per valutare l'efficacia del nostro approccio. Se l'algoritmo quantistico produce in modo dimostrabile e coerente risultati con valori di costo inferiori, ciò indica un processo di ottimizzazione efficace.
La figura presenta le distribuzioni di probabilità dei costi obiettivo. Per generare queste distribuzioni, prendi la lista dei costi obiettivo dal risultato della funzione e conta le occorrenze di ogni valore di costo (valori arrotondati alla seconda cifra decimale). Quindi, aggiorna la colonna dei conteggi di conseguenza unendo i conteggi dei valori arrotondati identici. Nota che, per una migliore comparazione visiva, i conteggi delle occorrenze sono stati normalizzati in modo che ogni distribuzione sia visualizzata tra 0 e 1.
Come mostrato nella figura (linea continua blu), la distribuzione dei costi per il nostro approccio Variational Quantum Eigensolver (post-elaborato con SQD) è concentrata nettamente su valori di costo obiettivo più bassi, indicando buone prestazioni di ottimizzazione. Al contrario, la baseline rumorosa mostra una distribuzione più ampia, centrata intorno a valori di costo più elevati. La linea verticale tratteggiata grigia rappresenta il valore medio della distribuzione casuale, evidenziando ulteriormente la coerenza della funzione nel restituire strategie di investimento ottimizzate. Come confronto aggiuntivo, la linea tratteggiata nera nella figura corrisponde alla soluzione ottenuta con l'ottimizzatore Gurobi (versione gratuita). Tutti questi risultati vengono ulteriormente esplorati nei benchmark seguenti per l'esempio "Mixed Assets" valutato con l'ansatz "Tailored".
Benchmark
Questa funzione è stata testata in diverse configurazioni di qubit di risoluzione, circuito ansatz e raggruppamenti di asset di vari settori: una combinazione di asset diversi (Set 1), derivati del petrolio (Set 2) e IBEX35 (Set 3). Vedi ulteriori dettagli nella tabella seguente.
| Set | Data | Asset |
|---|---|---|
| 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 |
Sono state utilizzate due metriche chiave per valutare la qualità della soluzione.
- Il costo obiettivo, che misura l'efficienza dell'ottimizzazione confrontando il valore della funzione di costo di ogni esperimento con i risultati di Gurobi (versione gratuita).
- L'indice di Sharpe, che cattura il rendimento corretto per il rischio di ogni portafoglio, offrendo una visione delle prestazioni finanziarie delle soluzioni.
Insieme, queste metriche confrontano sia gli aspetti computazionali che quelli finanziari dei portafogli generati con il metodo quantistico.
| Esempio | qubit | Ansatz | Depth | Utilizzo Runtime (s) | Utilizzo totale (s) | Costo obiettivo | Sharpe | Costo obiettivo Gurobi | Sharpe Gurobi |
|---|---|---|---|---|---|---|---|---|---|
| Mixed Assets (Set 1, 4 step temporali, 4-bit) | 112 | Tailored | 83 | 12735 | 13095 | -3.78 | 24.82 | -4.25 | 24.71 |
| Mixed Assets (Set 1, 4 step temporali, 4 step temporali, 4-bit) | 112 | Real Amplitudes | 359 | 11739 | 11903 | -3.39 | 23.64 | -4.25 | 24.71 |
| Oil Derivatives (Set 2, 4 step temporali, 3-bit) | 84 | Optimized Real Amplitudes | 78 | 6180 | 6350 | -3.73 | 19.13 | -4.19 | 21.71 |
| IBEX35 (Set 3, 4 step temporali, 2-bit) | 56 | Optimized Real Amplitudes | 96 | 3314 | 3523 | -3.67 | 14.48 | -4.11 | 16.44 |
I risultati mostrano che l'ottimizzatore quantistico, con ansatz specifici per il problema, identifica efficacemente strategie di investimento efficienti in diversi tipi di portafoglio.
Di seguito sono riportati i dettagli relativi alla dimensione della popolazione e al numero di generazioni specificati nel dizionario optimizer_options. Tutti gli altri parametri sono stati impostati ai valori predefiniti.
| Esempio | population_size | num_generations |
|---|---|---|
| Mixed Assets Portfolio | 90 | 20 |
| Mixed Assets Portfolio | 92 | 20 |
| Oil Derivatives Portfolio | 120 | 20 |
| IBEX35 Portfolio | 40 | 20 |
Il numero di generazioni è stato impostato a 20, poiché questo valore è risultato sufficiente per raggiungere la convergenza. Inoltre, i valori predefiniti per i parametri interni dell'ottimizzatore sono stati lasciati invariati, poiché forniscono costantemente buone prestazioni e sono generalmente raccomandati dalla letteratura e dalle linee guida di implementazione.
Supporto
Se hai bisogno di aiuto, puoi inviare un'e-mail a qpo.support@globaldataquantum.com. Nel messaggio, fornisci l'ID del job della funzione.
Passi successivi​
- Leggi il documento di ricerca associato.
- Richiedi l'accesso alla funzione compilando questo modulo.
- Prova il tutorial Dynamic Portfolio Optimization.