Nota

Esta página fue generada a partir de docs/tutorials/10_qgan_option_pricing.ipynb.

Fijación de Precios de Opciones con qGANs#

Introducción#

En este cuaderno, discutimos cómo un Algoritmo Cuántico de Machine Learning, es decir, una Red Generativa Antagónica cuántica (quantum Generative Adversarial Network, qGAN), puede facilitar la fijación de precios de una opción de compra europea. Más específicamente, una qGAN se puede entrenar de manera que un circuito cuántico modele el precio al contado de un activo subyacente a una opción de compra europea. El modelo resultante se puede integrar en un algoritmo basado en Estimación de Amplitud Cuántica para evaluar la rentabilidad esperada; consulta Fijación de Precios de Opciones de Compra Europeas. Para obtener más detalles sobre el aprendizaje y la carga de distribuciones aleatorias mediante el entrenamiento de una qGAN, consulta Quantum Generative Adversarial Networks for Learning and Loading Random Distributions. Zoufal, Lucchi, Woerner. 2019.

[1]:
import matplotlib.pyplot as plt
import numpy as np

from qiskit.circuit import ParameterVector
from qiskit.circuit.library import TwoLocal
from qiskit.quantum_info import Statevector

from qiskit_algorithms import IterativeAmplitudeEstimation, EstimationProblem
from qiskit_aer.primitives import Sampler
from qiskit_finance.applications.estimation import EuropeanCallPricing
from qiskit_finance.circuit.library import NormalDistribution

Modelo de Incertidumbre#

El modelo Black-Scholes asume que el precio al contado al vencimiento \(S_T\) para una opción de compra europea tiene una distribución logarítmica normal. Por lo tanto, podemos entrenar una qGAN con muestras de una distribución logarítmica normal y usar el resultado como un modelo de incertidumbre subyacente a la opción. A continuación, construimos un circuito cuántico que carga el modelo de incertidumbre. La salida del circuito dice que

\[\big| g_{\theta}\rangle = \sum_{j=0}^{2^n-1}\sqrt{p_{\theta}^{j}} \big| j \rangle ,\]

donde las probabilidades \(p_{\theta}^{j}\), para \(j\in \left\{0, \ldots, {2^n-1} \right\}\), representan un modelo de la distribución objetivo.

[2]:
# Set upper and lower data values
bounds = np.array([0.0, 7.0])
# Set number of qubits used in the uncertainty model
num_qubits = 3

# Load the trained circuit parameters
g_params = [0.29399714, 0.38853322, 0.9557694, 0.07245791, 6.02626428, 0.13537225]

# Set an initial state for the generator circuit
init_dist = NormalDistribution(num_qubits, mu=1.0, sigma=1.0, bounds=bounds)

# construct the variational form
var_form = TwoLocal(num_qubits, "ry", "cz", entanglement="circular", reps=1)

# keep a list of the parameters so we can associate them to the list of numerical values
# (otherwise we need a dictionary)
theta = var_form.ordered_parameters

# compose the generator circuit, this is the circuit loading the uncertainty model
g_circuit = init_dist.compose(var_form)

Evaluar el Rendimiento Esperado#

Ahora, el modelo de incertidumbre entrenado se puede utilizar para evaluar el valor esperado de la función de rendimiento de la opción con Estimación de Amplitud Cuántica.

[3]:
# set the strike price (should be within the low and the high value of the uncertainty)
strike_price = 2

# set the approximation scaling for the payoff function
c_approx = 0.25

Graficar la distribución de probabilidad#

A continuación, graficamos la distribución de probabilidad entrenada y, para poder comparar, también la distribución de probabilidad objetivo.

[4]:
# Evaluate trained probability distribution
values = [
    bounds[0] + (bounds[1] - bounds[0]) * x / (2**num_qubits - 1) for x in range(2**num_qubits)
]
uncertainty_model = g_circuit.assign_parameters(dict(zip(theta, g_params)))
amplitudes = Statevector.from_instruction(uncertainty_model).data

x = np.array(values)
y = np.abs(amplitudes) ** 2

# Sample from target probability distribution
N = 100000
log_normal = np.random.lognormal(mean=1, sigma=1, size=N)
log_normal = np.round(log_normal)
log_normal = log_normal[log_normal <= 7]
log_normal_samples = []
for i in range(8):
    log_normal_samples += [np.sum(log_normal == i)]
log_normal_samples = np.array(log_normal_samples / sum(log_normal_samples))

# Plot distributions
plt.bar(x, y, width=0.2, label="trained distribution", color="royalblue")
plt.xticks(x, size=15, rotation=90)
plt.yticks(size=15)
plt.grid()
plt.xlabel("Spot Price at Maturity $S_T$ (\$)", size=15)
plt.ylabel("Probability ($\%$)", size=15)
plt.plot(
    log_normal_samples,
    "-o",
    color="deepskyblue",
    label="target distribution",
    linewidth=4,
    markersize=12,
)
plt.legend(loc="best")
plt.show()
../_images/tutorials_10_qgan_option_pricing_7_0.png

Evaluar el Rendimiento Esperado#

Ahora, el modelo de incertidumbre entrenado se puede utilizar para evaluar el valor esperado de la función de rendimiento de la opción de forma analítica y con la Estimación de Amplitud Cuántica.

[5]:
# Evaluate payoff for different distributions
payoff = np.array([0, 0, 0, 1, 2, 3, 4, 5])
ep = np.dot(log_normal_samples, payoff)
print("Analytically calculated expected payoff w.r.t. the target distribution:  %.4f" % ep)
ep_trained = np.dot(y, payoff)
print("Analytically calculated expected payoff w.r.t. the trained distribution: %.4f" % ep_trained)

# Plot exact payoff function (evaluated on the grid of the trained uncertainty model)
x = np.array(values)
y_strike = np.maximum(0, x - strike_price)
plt.plot(x, y_strike, "ro-")
plt.grid()
plt.title("Payoff Function", size=15)
plt.xlabel("Spot Price", size=15)
plt.ylabel("Payoff", size=15)
plt.xticks(x, size=15, rotation=90)
plt.yticks(size=15)
plt.show()
Analytically calculated expected payoff w.r.t. the target distribution:  1.0611
Analytically calculated expected payoff w.r.t. the trained distribution: 0.9805
../_images/tutorials_10_qgan_option_pricing_9_1.png
[6]:
# construct circuit for payoff function
european_call_pricing = EuropeanCallPricing(
    num_qubits,
    strike_price=strike_price,
    rescaling_factor=c_approx,
    bounds=bounds,
    uncertainty_model=uncertainty_model,
)
[7]:
# set target precision and confidence level
epsilon = 0.01
alpha = 0.05

problem = european_call_pricing.to_estimation_problem()
# construct amplitude estimation
ae = IterativeAmplitudeEstimation(
    epsilon_target=epsilon, alpha=alpha, sampler=Sampler(run_options={"shots": 100, "seed": 75})
)
[8]:
result = ae.estimate(problem)
[9]:
conf_int = np.array(result.confidence_interval_processed)
print("Exact value:        \t%.4f" % ep_trained)
print("Estimated value:    \t%.4f" % (result.estimation_processed))
print("Confidence interval:\t[%.4f, %.4f]" % tuple(conf_int))
Exact value:            0.9805
Estimated value:        1.0138
Confidence interval:    [0.9883, 1.0394]
[10]:
import qiskit.tools.jupyter

%qiskit_version_table
%qiskit_copyright

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0.dev0+c626be7
qiskit_algorithms0.2.0
qiskit_aer0.12.0
qiskit_optimization0.6.0
qiskit_finance0.4.0
qiskit_ibm_provider0.6.1
System information
Python version3.9.7
Python compilerGCC 7.5.0
Python builddefault, Sep 16 2021 13:09:58
OSLinux
CPUs2
Memory (Gb)5.778430938720703
Fri Aug 18 16:26:01 2023 EDT

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

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.

[ ]: