Skip to main content
IBM Quantum Platform

Quantum Portfolio Optimizer: una funzione Qiskit di Global Data Quantum

Consulta la documentazione dell'API

Note

Le funzioni Qiskit sono una funzione sperimentale disponibile solo per gli utenti di IBM Quantum® Premium Plan, Flex Plan e On-Prem (tramite IBM Quantum Platform API) Plan. Sono in stato di anteprima e sono soggetti a modifiche.


Panoramica

Il Quantum Portfolio Optimizer è una funzione di Qiskit che affronta il problema dell'ottimizzazione dinamica del portafoglio, un problema standard in finanza che mira a riequilibrare gli investimenti periodici in un insieme di attività, per massimizzare i rendimenti e minimizzare i rischi. Utilizzando tecniche di ottimizzazione quantistica all'avanguardia, questa funzione semplifica il processo in modo che gli utenti, senza alcuna competenza in materia di calcolo quantistico, possano beneficiare dei suoi vantaggi nel trovare traiettorie di investimento ottimali. Ideale per i gestori di portafoglio, i ricercatori di finanza quantitativa e i singoli investitori, questo strumento consente di effettuare test retrospettivi delle strategie di trading nell'ambito dell'ottimizzazione del portafoglio.


Descrizione funzione

La funzione Quantum Portfolio Optimizer utilizza l'algoritmo Variational Quantum Eigensolver (VQE) per risolvere un problema di Quadratic Unconstrained Binary Optimization (QUBO), affrontando problemi di ottimizzazione dinamica del portafoglio. Gli utenti devono semplicemente fornire i dati relativi ai prezzi degli asset e definire i vincoli di investimento, quindi la funzione esegue il processo di ottimizzazione quantistica che restituisce un insieme di traiettorie di investimento ottimizzate.

Il processo si articola in quattro fasi principali. In primo luogo, i dati di input vengono mappati su un problema compatibile con la meccanica quantistica, 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 quindi eseguito sull'hardware quantistico e, infine, i risultati vengono sottoposti a post-elaborazione per fornire le traiettorie di investimento ottimali. Il sistema include inoltre una post-elaborazione sensibile al rumore ( basata su SQD) per ottimizzare la qualità del risultato finale.

Questa funzione Qiskit si basa sul manoscritto pubblicato da Global Data Quantum.

Visualizzazione del flusso di lavoro della funzione

Introduzione

Effettua l'autenticazione utilizzando la tua chiave API e seleziona la funzione Qiskit come indicato di seguito. (Questo frammento di codice presuppone che tu abbia già salvato il tuo account nell'ambiente locale.)

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Verify that you have access to the function
catalog.list()

Output:

[QiskitFunction(qunova/hivqe-chemistry),
 QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),
 QiskitFunction(algorithmiq/tem),
 QiskitFunction(qedma/qesem),
 QiskitFunction(multiverse/singularity),
 QiskitFunction(ibm/circuit-function),
 QiskitFunction(q-ctrl/optimization-solver),
 QiskitFunction(colibritd/quick-pde),
 QiskitFunction(q-ctrl/performance-management),
 QiskitFunction(kipu-quantum/iskay-quantum-optimizer)]
# Access function
dpo_solver = catalog.load("global-data-quantum/quantum-portfolio-optimizer")

Esempio: ottimizzazione dinamica del portafoglio con sette attività

Questo esempio mostra come eseguire la funzione di ottimizzazione dinamica del portafoglio (DPO) e come regolarne le impostazioni per ottenere prestazioni ottimali. Include passaggi dettagliati per la regolazione fine dei parametri per ottenere i risultati desiderati.

Questo caso prevede sette asset, quattro fasi temporali e quattro qubit di risoluzione, per un fabbisogno totale di 112 qubit.

1. Leggi le risorse incluse nel portfolio

Se tutte le risorse del portafoglio sono memorizzate in una cartella in un percorso specifico, è possibile caricarle in un pandas.DataFrame e convertirle in un oggetto in formato dict utilizzando 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 utilizzati in questo esempio, mostrando l'evoluzione del prezzo di chiusura giornaliero delle attività dal 1° gennaio al 1° settembre del 2023.

In questo esempio, per garantire l'uniformità tra le date, abbiamo riempito i giorni di non negoziazione con il prezzo di chiusura della data precedente disponibile. Applichiamo questa fase perché gli asset selezionati provengono da mercati diversi con giorni di negoziazione variabili, rendendo essenziale la standardizzazione del set di dati per garantire la coerenza.

Visualizzazione dei dati storici degli asset

2. Definire il problema

Definire 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. Definire le impostazioni dell'ottimizzatore e dell'ansatz (facoltativo)

Definire i requisiti specifici per il processo di ottimizzazione, compresa 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 è basata su precedenti esperimenti che hanno dimostrato che questo valore produce un'ottimizzazione stabile ed efficiente.

Nel caso dell'Ansatz delle Ampiezze Reali, è possibile seguire una relazione lineare tra population_size e il numero di qubit nel circuito. Come regola empirica approssimativa, si raccomanda di utilizzare un minimo di population_size ~ 0.8 * n_qubits per l'ansatz real_amplitudes .

Si prevede che le Ampiezze reali ottimizzate avranno una migliore performance di ottimizzazione rispetto al surrogato delle Ampiezze reali. Tuttavia, il numero di variabili da ottimizzare in questo surrogato aumenta molto più rapidamente che nel caso delle ampiezze reali (si veda il manoscritto ). Pertanto, per problemi di grandi dimensioni, l'Ampiezza reale ottimizzata richiede un maggior numero di esecuzioni del circuito. L'Ampiezza reale ottimizzata è probabilmente utile per i problemi che richiedono fino a 100 qubit, ma si raccomanda di prestare attenzione quando si impostano i parametri population_size . Come esempio di questa scalabilità in population_size, la tabella precedente mostra che per un problema a 84 qubit, l'ottimizzazione delle ampiezze reali richiede 120 population_size, mentre per un problema a 56 qubit è sufficiente una 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 specifica. Di seguito si utilizza l'ansatz di 'Tailored' .

ansatz_settings = {
    "ansatz": "tailored",
    "multiple_passmanager": False,
}

4. Eseguire 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. Recuperare i risultati

La funzione restituisce un dizionario contenente le traiettorie di investimento ordinate dal valore più basso a quello più alto in base al valore della funzione obiettivo (vedere la sezione "Output" della documentazione dell'API). Questa serie di risultati consente di individuare il percorso con il costo più basso e le relative valutazioni di investimento. Inoltre, consente di analizzare diverse traiettorie, facilitando la selezione di quelle che meglio rispondono a esigenze o obiettivi specifici. Questa flessibilità garantisce che le opzioni possano essere personalizzate in base a una varietà di preferenze o situazioni.

Iniziare 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"]

Output:

{'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, utilizzando i metadati, è possibile accedere ai risultati di tutte le strategie campionate. È quindi possibile analizzare ulteriormente le traiettorie alternative restituite dall'ottimizzatore. Per farlo, si legge il dizionario memorizzato in dpo_result['metadata']['all_samples_metrics'], che contiene non solo informazioni aggiuntive sulla strategia ottimale, ma anche dettagli sulle altre strategie candidate valutate durante l'ottimizzazione.

L'esempio seguente mostra come leggere queste informazioni utilizzando pandas per estrarre le metriche chiave associate alla strategia ottimale. Questi includono la deviazione di restrizione, lo Sharpe Ratio e il corrispondente rendimento dell'investimento.

# 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']}")

Output:

Minimum Objective Cost Found: -3.78
Best Solution:
  - Restriction Deviation: 40.0
  - Sharpe Ratio: 24.82
  - Return: 0.46

6. Analisi delle prestazioni

Infine, analizzate le prestazioni dell'applicazione di ottimizzazione. In particolare, confrontate i vostri risultati, ottenuti nell'esempio precedente, con una linea di base casuale per valutare l'efficacia del nostro approccio. Se l'algoritmo quantistico produce in modo dimostrabile e costante risultati con valori di costo inferiori, indica un processo di ottimizzazione efficace.

La figura presenta le distribuzioni di probabilità dei costi obiettivo. Per generare queste distribuzioni, prendere l'elenco dei costi obiettivo dal risultato della funzione e contare le occorrenze di ciascun valore di costo (valori arrotondati alla seconda cifra decimale). Quindi, aggiornare la colonna del conteggio unendo i conteggi dei valori arrotondati identici. Si noti che, per un migliore confronto visivo, i conteggi delle occorrenze sono stati normalizzati in modo che ciascuna distribuzione sia visualizzata tra 0 e 1.

Visualizzazione della soluzione del problema di ottimizzazione

Come mostrato nella figura (linea solida blu), la distribuzione dei costi per il nostro approccio Variational Quantum Eigensolver (post-processato con SQD) si concentra nettamente sui valori più bassi del costo obiettivo, indicando buone prestazioni di ottimizzazione. Al contrario, la linea di base rumorosa presenta una distribuzione più ampia, centrata sui valori di costo più elevati. La linea verticale grigia tratteggiata rappresenta il valore medio della distribuzione casuale, evidenziando ulteriormente la coerenza della funzione nel restituire strategie di investimento ottimizzate. Per un ulteriore confronto, la linea nera tratteggiata nella figura corrisponde alla soluzione ottenuta con l'ottimizzatore Gurobi (versione gratuita). Tutti questi risultati sono ulteriormente approfonditi nei benchmark che seguono per l'esempio "Mixed Assets" valutato con l'ansatz "Tailored".

Benchmark

Questa funzione è stata testata con diverse configurazioni di qubit di risoluzione, circuiti di ansatz e raggruppamenti di asset di vari settori: un mix di asset diversi (Set 1), derivati del petrolio (Set 2) e IBEX35 (Set 3). Per maggiori dettagli, consultare la tabella seguente.

Imposta
Data
Asset
Set 101/01/20238801.T, CL=F, GBPJPY=X, ITX.MC, META, TMBMKDE-10Y, XS2239553048
Set 201/06/2023CL=F, BZ=F, HO=F, NG=F, XOM, RB=F, 2222.SR
Serie 301/11/2022ACS.MC, ITX.MC, FER.MC, ELE.MC, SCYR.MC, AENA.MC, AMS.MC

Per valutare la qualità della soluzione sono state utilizzate due metriche chiave.

  1. Il costo obiettivo, che misura l'efficienza dell'ottimizzazione confrontando il valore della funzione costo di ogni esperimento con i risultati di Gurobi (versione gratuita).
  2. Il rapporto di Sharpe, che cattura il rendimento corretto per il rischio di ciascun portafoglio, offrendo una visione della performance finanziaria delle soluzioni.

Insieme, queste metriche consentono di valutare gli aspetti computazionali e finanziari dei portafogli generati dai quanti.

Esempio
Qubit
Ansatz
Profondità
Utilizzo del tempo di esecuzione (s)
Utilizzo totale (s)
Costo obiettivo
Sharpe
Costo obiettivo di Gurobi
Gurobi Sharpe
Attività miste (Set 1, 4 passi temporali, 4 bit)112Su misura831273513095-3.7824.82-4.2524.71
Attività miste (Set 1,4 passi temporali, 4 passi temporali, 4 bit)112Ampiezze reali3591173911903-3.3923.64-4.2524.71
Derivati del petrolio (set 2, 4 fasi temporali, 3 bit)84Ampiezze reali ottimizzate7861806350-3.7319.13-4.1921.71
IBEX35 (Set 3, 4 passi temporali, 2 bit)56Ampiezze reali ottimizzate9633143523-3.6714.48-4.1116.44

I risultati dimostrano che l'ottimizzatore quantistico, con ansatzes specifici per il problema, identifica efficacemente strategie di investimento efficienti in vari tipi di portafoglio.

Di seguito vengono illustrati in dettaglio sia la dimensione della popolazione sia il numero di generazioni specificate nel dizionario optimizer_options . Tutti gli altri parametri sono stati impostati sui valori predefiniti.

Esempio
population_size
num_generations
Portafoglio di attività miste9020
Portafoglio di attività miste9220
Portafoglio dei derivati del petrolio12020
IBEX35 Portafoglio4020

Il numero di generazioni è stato impostato a 20, valore ritenuto sufficiente per raggiungere la convergenza. Inoltre, i valori predefiniti per i parametri interni dell'ottimizzatore sono stati lasciati invariati, poiché hanno sempre fornito buone prestazioni e sono generalmente raccomandati dalla letteratura e dalle linee guida per l'implementazione.

Ottenere supporto

Se hai bisogno di aiuto, puoi inviare un'e-mail a qpo.support @ globaldataquantum.com. Nel messaggio, indicare l'ID della funzione.


Passi successivi

Suggerimenti
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.