Portuguese, Brazilian
Idiomas
English
Japanese
German
Korean
Portuguese, Brazilian
French
Shortcuts

Nota

Esta página foi gerada, a partir de tutorials/finance/01_portfolio_optimization.ipynb.

Execute interativamente no IBM Quantum lab.

Otimização de Portfólio

Introdução

Este tutorial mostra como resolver o seguinte problema de otimização de portfólio de média-variância para \(n\) ativos:

\(\begin{aligned} \min_{x \in \{0, 1\}^n} q x^T \Sigma x - \mu^T x\\ \text{subject to: } 1^T x = B \end{aligned}\)

onde utilizamos a seguinte notação:

  • \(x \in \{0, 1\}^n\) denota um vetor de decisões binárias variáveis, que indica quais ativos escolher (\(x[i] = 1\)) e quais ativos não escolher (\(x[i] = 0\)),

  • \(\mu \in \mathbb{R}^n\) define os retornos esperados para os ativos,

  • \(\Sigma \in \mathbb{R}^{n \times n}\) especifica as covariâncias entre os ativos,

  • \(q > 0\) controla o apetite de risco do tomador de decisão,

  • e \(B\) denota o orçamento, ou seja, o número de ativos a serem selecionados de \(n\).

Assumimos as seguintes simplificações: - todos os ativos têm o mesmo preço (normalizado para 1), - todo o orçamento \(B\) tem de ser gasto, ou seja, devemos selecionar exatamente \(B\) ativos.

A restrição de igualdade :math:` 1 ^ T x = B ` é mapeada para um termo de pênalti :math:` (1 ^ T x-B) ^ 2 ` que é escalonado por um parâmetro e subtraído da função objetiva. O problema resultante pode ser mapeado para um Hamiltonian cujo estado fundamental corresponde à solução ideal. Este notebook mostra como utilizar o Variational Quantum Eigensolver (VQE) ou o Quantum Approximate Optimization Algorithm (QAOA) para encontrar a solução ideal para um determinado conjunto de parâmetros.

Experimentos realizados em hardware quântico real para este problema são relatados por exemplo no seguinte artigo: Improving Variational Quantum Optimization using CVaR. Barkoutsos et al. 2019.

[1]:
from qiskit import Aer
from qiskit.circuit.library import TwoLocal
from qiskit.aqua import QuantumInstance
from qiskit.finance.applications.ising import portfolio
from qiskit.optimization.applications.ising.common import sample_most_likely
from qiskit.finance.data_providers import RandomDataProvider
from qiskit.aqua.algorithms import VQE, QAOA, NumPyMinimumEigensolver
from qiskit.aqua.components.optimizers import COBYLA
import numpy as np
import matplotlib.pyplot as plt
import datetime

[Optional] Configuração do token para executar o experimento em um dispositivo real

Caso deseje executar o experimento em um dispositivo real, você precisa configurar a sua conta primeiro.

Nota: Se você ainda não armazenou seu token, use IBMQ.save_account('MY_API_TOKEN') para armazená-lo primeiro.

Definindo uma instância do problema

Aqui, uma instância da Operator é criada para o nosso Hamiltoniano. Neste caso, os paulis são de um Hamiltoniano de Ising traduzido do problema de portfólio. Utilizamos um problema de portfólio aleatório para este notebook. É fácil estender isto para usar dados financeiros reais como ilustrado aqui: Carregando e Processando Dados de Séries Temporais do Mercado de Ações

[2]:
# set number of assets (= number of qubits)
num_assets = 4

# Generate expected return and covariance matrix from (random) time-series
stocks = [("TICKER%s" % i) for i in range(num_assets)]
data = RandomDataProvider(tickers=stocks,
                 start=datetime.datetime(2016,1,1),
                 end=datetime.datetime(2016,1,30))
data.run()
mu = data.get_period_return_mean_vector()
sigma = data.get_period_return_covariance_matrix()
[3]:
# plot sigma
plt.imshow(sigma, interpolation='nearest')
plt.show()
../../_images/tutorials_finance_01_portfolio_optimization_6_0.png
[4]:
q = 0.5                   # set risk factor
budget = num_assets // 2  # set budget
penalty = num_assets      # set parameter to scale the budget penalty term

qubitOp, offset = portfolio.get_operator(mu, sigma, q, budget, penalty)

Definimos alguns métodos utilitários para imprimir os resultados em um formato agradável.

[5]:
def index_to_selection(i, num_assets):
    s = "{0:b}".format(i).rjust(num_assets)
    x = np.array([1 if s[i]=='1' else 0 for i in reversed(range(num_assets))])
    return x

def print_result(result):
    selection = sample_most_likely(result.eigenstate)
    value = portfolio.portfolio_value(selection, mu, sigma, q, budget, penalty)
    print('Optimal: selection {}, value {:.4f}'.format(selection, value))

    eigenvector = result.eigenstate if isinstance(result.eigenstate, np.ndarray) else result.eigenstate.to_matrix()
    probabilities = np.abs(eigenvector)**2
    i_sorted = reversed(np.argsort(probabilities))
    print('\n----------------- Full result ---------------------')
    print('selection\tvalue\t\tprobability')
    print('---------------------------------------------------')
    for i in i_sorted:
        x = index_to_selection(i, num_assets)
        value = portfolio.portfolio_value(x, mu, sigma, q, budget, penalty)
        probability = probabilities[i]
        print('%10s\t%.4f\t\t%.4f' %(x, value, probability))

NumPyMinimumEigensolver (como referência clássica)

Vamos resolver o problema. Primeiro, classicamente…

Podemos, agora, utilizar o Operator que criamos acima, sem considerar as particularidades de como foi criado. Definimos o algoritmo como o NumPyMinimumEigensolver, para que possamos ter uma referência clássica. O problema está configurado como ‘ising’. Um backend não é necessário, uma vez que isto é computado classicamente, e não usando computação quântica. O resultado é retornado como um dicionário.

[6]:
exact_eigensolver = NumPyMinimumEigensolver(qubitOp)
result = exact_eigensolver.run()

print_result(result)
Optimal: selection [0 1 0 1], value -0.0005

----------------- Full result ---------------------
selection       value           probability
---------------------------------------------------
 [0 1 0 1]      -0.0005         1.0000
 [1 1 1 1]      16.0040         0.0000
 [0 1 1 1]      4.0013          0.0000
 [1 0 1 1]      4.0052          0.0000
 [0 0 1 1]      0.0025          0.0000
 [1 1 0 1]      4.0023          0.0000
 [1 0 0 1]      0.0034          0.0000
 [0 0 0 1]      4.0007          0.0000
 [1 1 1 0]      4.0033          0.0000
 [0 1 1 0]      0.0007          0.0000
 [1 0 1 0]      0.0045          0.0000
 [0 0 1 0]      4.0018          0.0000
 [1 1 0 0]      0.0016          0.0000
 [0 1 0 0]      3.9988          0.0000
 [1 0 0 0]      4.0027          0.0000
 [0 0 0 0]      16.0000         0.0000

Solução usando VQE

Agora, podemos usar o Variational Quantum Eigensolver (VQE) para resolver o problema. Vamos especificar o otimizador e a forma variacional a serem usados.

Observação: Você pode mudar para serviços internos diferentes fornecendo o nome do serviço.

[7]:
backend = Aer.get_backend('statevector_simulator')
seed = 50

cobyla = COBYLA()
cobyla.set_options(maxiter=500)
ry = TwoLocal(qubitOp.num_qubits, 'ry', 'cz', reps=3, entanglement='full')
vqe = VQE(qubitOp, ry, cobyla)
vqe.random_seed = seed

quantum_instance = QuantumInstance(backend=backend, seed_simulator=seed, seed_transpiler=seed)

result = vqe.run(quantum_instance)

print_result(result)
Optimal: selection [0. 0. 1. 1.], value 0.0025

----------------- Full result ---------------------
selection       value           probability
---------------------------------------------------
 [0 0 1 1]      0.0025          0.7519
 [0 1 1 0]      0.0007          0.2480
 [1 1 0 1]      4.0023          0.0000
 [1 1 0 0]      0.0016          0.0000
 [1 0 0 1]      0.0034          0.0000
 [0 1 0 1]      -0.0005         0.0000
 [1 1 1 0]      4.0033          0.0000
 [1 0 0 0]      4.0027          0.0000
 [1 0 1 1]      4.0052          0.0000
 [1 0 1 0]      0.0045          0.0000
 [0 0 1 0]      4.0018          0.0000
 [0 1 0 0]      3.9988          0.0000
 [0 0 0 1]      4.0007          0.0000
 [0 1 1 1]      4.0013          0.0000
 [0 0 0 0]      16.0000         0.0000
 [1 1 1 1]      16.0040         0.0000

Solução usando QAOA

Também mostramos, aqui, um resultado usando o Algoritmo de Otimização Aproximada Quântico (QAOA). Este é outro algoritmo variacional e ele usa uma forma variacional interna, que é criada com base no problema.

[8]:
backend = Aer.get_backend('statevector_simulator')
seed = 50

cobyla = COBYLA()
cobyla.set_options(maxiter=250)
qaoa = QAOA(qubitOp, cobyla, 3)

qaoa.random_seed = seed

quantum_instance = QuantumInstance(backend=backend, seed_simulator=seed, seed_transpiler=seed)

result = qaoa.run(quantum_instance)

print_result(result)
Optimal: selection [0. 1. 0. 1.], value -0.0005

----------------- Full result ---------------------
selection       value           probability
---------------------------------------------------
 [0 1 0 1]      -0.0005         0.1673
 [0 1 1 0]      0.0007          0.1670
 [1 1 0 0]      0.0016          0.1668
 [0 0 1 1]      0.0025          0.1666
 [1 0 0 1]      0.0034          0.1663
 [1 0 1 0]      0.0045          0.1661
 [1 1 1 1]      16.0040         0.0000
 [0 0 0 0]      16.0000         0.0000
 [0 1 1 1]      4.0013          0.0000
 [0 1 0 0]      3.9988          0.0000
 [1 1 0 1]      4.0023          0.0000
 [1 0 1 1]      4.0052          0.0000
 [1 0 0 0]      4.0027          0.0000
 [1 1 1 0]      4.0033          0.0000
 [0 0 0 1]      4.0007          0.0000
 [0 0 1 0]      4.0018          0.0000
[9]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
Qiskit0.19.1
Terra0.14.1
Aer0.5.1
Ignis0.3.0
Aqua0.7.0
IBM Q Provider0.7.0
System information
Python3.7.4 (default, Aug 13 2019, 15:17:50) [Clang 4.0.1 (tags/RELEASE_401/final)]
OSDarwin
CPUs6
Memory (Gb)16.0
Fri Jul 17 17:36:55 2020 CEST

This code is a part of Qiskit

© Copyright IBM 2017, 2020.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

[ ]: