Skip to main content
IBM Quantum Platform

Otimizador de portfólio quântico: uma função Qiskit da Global Data Quantum

Consulte a referência da API

Note

As funções do Qiskit são um recurso experimental disponível apenas para usuários dos planos IBM Quantum® Premium Plan, Flex Plan e On-Prem (via IBM Quantum Platform API). Eles estão no status de versão prévia e estão sujeitos a alterações.


Visão geral

O Quantum Portfolio Optimizer é uma função do Qiskit que aborda o problema de otimização dinâmica de portfólio, um problema padrão em finanças que visa reequilibrar investimentos periódicos em um conjunto de ativos, para maximizar os retornos e minimizar os riscos. Ao implantar técnicas de otimização quântica de ponta, essa função simplifica o processo para que os usuários, sem conhecimento especializado em computação quântica, possam se beneficiar de suas vantagens para encontrar trajetórias de investimento ideais. Ideal para gerentes de portfólio, pesquisadores em finanças quantitativas e investidores individuais, essa ferramenta permite o back-testing de estratégias de negociação na otimização de portfólio.


Descrição da função

A função Quantum Portfolio Optimizer usa o algoritmo Variational Quantum Eigensolver (VQE) para resolver um problema de Quadratic Unconstrained Binary Optimization (QUBO), abordando problemas de otimização dinâmica de portfólio. Os usuários só precisam fornecer os dados de preço do ativo e definir a restrição de investimento. Em seguida, a função executa o processo de otimização quântica que retorna um conjunto de trajetórias de investimento otimizadas.

O processo consiste em quatro etapas principais. Primeiro, os dados de entrada são mapeados para um problema compatível com a computação quântica, construindo o QUBO do problema de otimização dinâmica de portfólio e transformando-o em um operador quântico (Hamiltoniano de Ising). Em seguida, o problema de entrada e o algoritmo VQE são adaptados para serem executados no hardware quântico. O algoritmo VQE é então executado no hardware quântico e, por fim, os resultados passam por um pós-processamento para fornecer as trajetórias de investimento ideais. O sistema também inclui um pós-processamento sensível ao ruído ( base ado em SQD) para maximizar a qualidade do resultado final.

Esta Função Qiskit é baseada no manuscrito publicado pela Global Data Quantum.

Visualização do fluxo de trabalho da função

Introdução

Faça a autenticação usando sua chave de API e selecione a função do Qiskit da seguinte maneira. (Este trecho pressupõe que você já tenha salvo sua conta no seu ambiente local.)

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")

Exemplo: Otimização dinâmica de carteira com sete ativos

Este exemplo demonstra como executar a função de otimização dinâmica de portfólio (DPO) e ajustar suas configurações para obter o melhor desempenho. Ele inclui etapas detalhadas para o ajuste fino dos parâmetros para alcançar os resultados desejados.

Esse caso envolve sete ativos, quatro etapas de tempo e quatro qubits de resolução, resultando em um requisito total de 112 qubits.

1. Verifique os ativos incluídos na carteira

Se todos os ativos do portfólio estiverem armazenados em uma pasta em um caminho específico, você poderá carregá-los em um pandas.DataFrame e convertê-lo em um objeto no formato dict usando a seguinte função.

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()

Para este exemplo, usamos os ativos 8801.T cLF, GBPJPY, ITX.MC, META, TMBMKDE-10Y, e XS2239553048. A figura a seguir ilustra os dados usados neste exemplo, mostrando a evolução do preço de fechamento diário dos ativos de 1º de janeiro a 1º de setembro de 2023.

Neste exemplo, para garantir a uniformidade entre as datas, preenchemos os dias sem negociação com o preço de fechamento da data disponível anterior. Aplicamos essa etapa porque os ativos selecionados vêm de diferentes mercados com dias de negociação variados, o que torna essencial a padronização do conjunto de dados para fins de consistência.

Visualização dos dados históricos dos ativos

2. Defina o problema

Defina as especificações do problema configurando os parâmetros no dicionário 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. Defina as configurações do otimizador e do ansatz (opcional)

Opcionalmente, defina requisitos específicos para o processo de otimização, incluindo a seleção do otimizador e seus parâmetros, bem como a especificação da primitiva e suas configurações.

Para o Tailored Ansatz, o tamanho da população escolhido foi baseado em experimentos anteriores que mostraram que esse valor produz uma otimização estável e eficiente.

No caso do Real Amplitudes Ansatz, você pode seguir uma relação linear entre o population_size e o número de qubits no circuito. Como regra geral aproximada, recomenda-se usar um mínimo population_size ~ 0.8 * n_qubits para o real_amplitudes ansatz.

Espera-se que as Amplitudes Reais Otimizadas tenham um desempenho de otimização melhor do que o ansatz das Amplitudes Reais. No entanto, o número de variáveis a serem otimizadas nesse ansatz aumenta muito mais rapidamente do que no caso das Amplitudes Reais (consulte o manuscrito ). Assim, para problemas grandes, as amplitudes reais otimizadas exigem mais execuções de circuito. É provável que a Optimized Real Amplitudes seja útil para problemas que exijam até 100 qubits, mas é recomendável ter cuidado ao definir os parâmetros do population_size . Como exemplo desse aumento de escala em population_size, a tabela anterior mostra que, para um problema de 84 qubits, o Optimize Real Amplitudes requer 120 population_size, enquanto para um problema de 56 qubits, um population_size de 40 é suficiente.

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,
    },
}

Também é possível escolher um ansatz específico. A seguir, usamos o 'Tailored' ansatz.

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

4. Execute o 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. Obter os resultados

A função retorna um dicionário com as trajetórias de investimento ordenadas da menor para a maior, de acordo com o valor da função objetivo (consulte a seção Saída da referência da API). Este conjunto de resultados permite identificar a trajetória de menor custo e as avaliações de investimento correspondentes. Além disso, permite a análise de diferentes trajetórias, facilitando a seleção daquelas que melhor se alinham às necessidades ou objetivos específicos. Essa flexibilidade garante que as opções possam ser adaptadas para atender a uma variedade de preferências ou situações.

Comece apresentando a estratégia resultante que atingiu o menor custo objetivo encontrado durante o 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}}

Depois disso, usando os metadados, você pode acessar os resultados de todas as estratégias amostradas. Dessa forma, você pode analisar melhor as trajetórias alternativas retornadas pelo otimizador. Para fazer isso, leia o dicionário armazenado em dpo_result['metadata']['all_samples_metrics'], que contém não apenas informações adicionais sobre a estratégia ideal, mas também detalhes das outras estratégias candidatas avaliadas durante a otimização.

O exemplo a seguir mostra como ler essas informações usando o site pandas para extrair as principais métricas associadas à estratégia ideal. Isso inclui o Desvio de Restrição, o Índice de Sharpe e o retorno de investimento correspondente.

# 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. Análise de desempenho

Por último, analise o desempenho de seu aplicativo de otimização. Especificamente, compare seus resultados, obtidos no exemplo anterior, com uma linha de base aleatória para avaliar a eficácia de nossa abordagem. Se o algoritmo quântico produzir resultados demonstráveis e consistentes com valores de custo mais baixos, isso indica um processo de otimização eficaz.

A figura apresenta as distribuições de probabilidade dos custos objetivos. Para gerar essas distribuições, pegue a lista de custos objetivos do resultado da função e conte as ocorrências de cada valor de custo (valores arredondados para a segunda casa decimal). Em seguida, atualize a coluna de contagem de acordo, unindo as contagens de valores arredondados idênticos. Observe que, para uma melhor comparação visual, as contagens de ocorrências foram normalizadas de modo que cada distribuição seja exibida entre 0 e 1.

Visualização da solução do problema de otimização

Conforme mostrado na figura (linha sólida azul), a distribuição de custos da nossa abordagem Variational Quantum Eigensolver (pós-processada com SQD) está fortemente concentrada nos valores de custo objetivo mais baixos, indicando um bom desempenho de otimização. Em contrapartida, a linha de base com ruído apresenta uma distribuição mais ampla, centralizada em valores de custo mais altos. A linha vertical tracejada em cinza representa o valor médio da distribuição aleatória, destacando ainda mais a consistência da função no retorno de estratégias de investimento otimizadas. Para uma comparação adicional, a linha tracejada preta na figura corresponde à solução obtida com o otimizador Gurobi (versão gratuita). Todos esses resultados são explorados com mais detalhes nos benchmarks abaixo para o exemplo "Mixed Assets" avaliado com o ansatz "Tailored".

Referências

Essa função foi testada em diferentes configurações de qubits de resolução, circuitos ansatz e agrupamentos de ativos de vários setores: uma mistura de diferentes ativos (Conjunto 1), derivados de petróleo (Conjunto 2) e IBEX35 (Conjunto 3). Veja mais detalhes na tabela a seguir.

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

Duas métricas principais foram usadas para avaliar a qualidade da solução.

  1. O custo objetivo, que mede a eficiência da otimização comparando o valor da função de custo de cada experimento com os resultados do Gurobi (versão gratuita).
  2. O índice Sharpe, que captura o retorno ajustado ao risco de cada portfólio, oferecendo uma visão do desempenho financeiro das soluções.

Juntas, essas métricas avaliam os aspectos computacionais e financeiros dos portfólios gerados por quantum.

Exemplo
Qubits
Ansatz
Profundidade
Uso do tempo de execução (s)
Uso total (s)
Custo objetivo
Sharpe
Custo objetivo do Gurobi
Gurobi Sharpe
Ativos mistos (Conjunto 1, 4 etapas de tempo, 4 bits)112Sob medida651273513095-3.7824.82-4.2524.71
Ativos mistos (Conjunto 1, 4 etapas de tempo, 4 etapas de tempo, 4 bits)112Amplitudes reais3591173911903-3.3923.64-4.2524.71
Derivativos de petróleo (Conjunto 2, 4 etapas de tempo, 3 bits)84Amplitudes reais otimizadas7861806350-3.7319.13-4.1921.71
IBEX35 (Conjunto 3, 4 etapas de tempo, 2 bits)56Amplitudes reais otimizadas9633143523-3.6714.48-4.1116.44

Os resultados mostram que o otimizador quântico, com ansatzes específicas do problema, identifica com eficácia estratégias de investimento eficientes em vários tipos de portfólio.

A seguir, detalhamos o tamanho da população e o número de gerações especificados no dicionário optimizer_options . Todos os outros parâmetros foram definidos com seus valores padrão.

Exemplo
population_size
num_generations
Portfólio de ativos mistos10520
Portfólio de ativos mistos9220
Carteira de Derivativos de Petróleo12020
IBEX35 Portfólio4020

O número de gerações foi definido como 20, pois esse valor foi considerado suficiente para atingir a convergência. Além disso, os valores padrão dos parâmetros internos do otimizador foram mantidos inalterados, uma vez que proporcionaram consistentemente um bom desempenho e são geralmente recomendados pela literatura e pelas diretrizes de implementação.

Obtenha suporte

Se precisar de ajuda, envie um e-mail para qpo.support @ globaldataquantum.com. Em sua mensagem, forneça o ID do trabalho da função.


Próximas etapas

Recomendações
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.