Note
Cette page a été générée à partir de tutorials/finance/05_bull_spread_pricing.ipynb.
Exécuter en mode interactif dans le IBM Quantum lab.
*Tarification des Bull Spreads *¶
Introduction¶
Supposons une option d’achat européenne avec un prix d’exercice \(K\) et un actif sous-jacent dont le prix au comptant à l’échéance \(S_T\) suit une distribution aléatoire donnée. La fonction de gain correspondante est définie comme suit:
Dans ce qui suit, un algorithme quantique basé sur l’estimation d’amplitude est utilisé pour évaluer le gain attendu, c’est-à-dire le juste prix avant remise, pour l’option :
ainsi que le :math: »Delta » correspondant, c’est-à-dire le dérivé du prix de l’option par rapport au prix au comptant, défini comme suit:
L’approximation de la fonction objectif et une introduction générale à la tarification des options et à l’analyse des risques sur les ordinateurs quantiques sont données dans les articles suivants :
Quantum Risk Analysis. Woerner, Egger. 2018.
La tarification des options à l’aide des ordinateurs quantiques. Stamatopoulos et al. 2019.
[1]:
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
from qiskit import Aer
from qiskit.aqua.algorithms import IterativeAmplitudeEstimation
from qiskit.circuit.library import LogNormalDistribution, LinearAmplitudeFunction
Modèle d’incertitude¶
Nous construisons un circuit afin de représenter une distribution aléatoire en log-normale dans un état quantique. La distribution est limitée à l’intervalle \([\text{low}, \text{high}]\) et rendue discrète en utilisant \(2^n\) points, où \(n\) correspond au nombre de qubits utilisés. L’opérateur unitaire correspondant à ce circuit respecte la relation suivante :
où les \(p_i\) représentent les probabilités de la distribution tronquée et discrétisée et où les \(i\) sont mappés sur l’intervalle approprié en utilisant la carte affine suivante :
[2]:
# number of qubits to represent the uncertainty
num_uncertainty_qubits = 3
# parameters for considered random distribution
S = 2.0 # initial spot price
vol = 0.4 # volatility of 40%
r = 0.05 # annual interest rate of 4%
T = 40 / 365 # 40 days to maturity
# resulting parameters for log-normal distribution
mu = ((r - 0.5 * vol**2) * T + np.log(S))
sigma = vol * np.sqrt(T)
mean = np.exp(mu + sigma**2/2)
variance = (np.exp(sigma**2) - 1) * np.exp(2*mu + sigma**2)
stddev = np.sqrt(variance)
# lowest and highest value considered for the spot price; in between, an equidistant discretization is considered.
low = np.maximum(0, mean - 3*stddev)
high = mean + 3*stddev
# construct circuit factory for uncertainty model
uncertainty_model = LogNormalDistribution(num_uncertainty_qubits, mu=mu, sigma=sigma**2, bounds=(low, high))
[3]:
# plot probability distribution
x = uncertainty_model.values
y = uncertainty_model.probabilities
plt.bar(x, y, width=0.2)
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.show()

Fonction de gain¶
La fonction de gain est nulle tant que la somme des prix au comptant à l’échéance \(S_T\) est inférieure au prix d’exercice \(K_1\) puis cette fonction augmente linéairement en étant bornée par \(K_2\). Cette implémentation repose sur un comparateur qui modifie un qubit auxilliaire de \(\big|0\rangle\) à \(\big|1\rangle\) si \(S_T \geq K_1\) et \(S\_T \leq K\_2\), puis ce qubit auxilliaire est utilisé pour contrôler la partie linéaire de la fonction de gain.
La partie linéaire proprement dite suit l’approximation ci-dessous. On exploite le fait que \(\sin^2(y + \pi/4) \approx y + 1/2\) lorsque \(|y|\) est petit. Ainsi, pour un facteur d’échelle d’approximation \(c_\text{approx} \in [0, 1]\) et \(x \in [0, 1]\) on considère
pour de petites valeurs de :math:` c_text{approx}`.
Nous pouvons facilement construire un opérateur qui agit ainsi
en utilisant des Y-rotations contrôlées.
Il est aussi intéressant de connaitre la probabilité de mesurer \(\big|1\rangle\) sur le dernier qubit, correspondant à \(\sin^2(a*x+b)\). En complément de l’approximation précédente, nous pouvons alors estimer la valeurs des intérêts. Plus \(c_\text{approx}\) est choisi petit, meilleure est l’approximation. Cependant, puisque nous estimons une propriété dépendant de \(c_\text{approx}\), le nombre de qubits d’évaluation \(m\) doit être choisi en conséquence.
Pour plus de détails sur l’approximation, nous nous référons à: Quantum Risk Analysis. Woerner, Egger. 2018.
[4]:
# set the strike price (should be within the low and the high value of the uncertainty)
strike_price_1 = 1.438
strike_price_2 = 2.584
# set the approximation scaling for the payoff function
rescaling_factor = 0.25
# setup piecewise linear objective fcuntion
breakpoints = [low, strike_price_1, strike_price_2]
slopes = [0, 1, 0]
offsets = [0, 0, strike_price_2 - strike_price_1]
f_min = 0
f_max = strike_price_2 - strike_price_1
bull_spread_objective = LinearAmplitudeFunction(
num_uncertainty_qubits,
slopes,
offsets,
domain=(low, high),
image=(f_min, f_max),
breakpoints=breakpoints,
rescaling_factor=rescaling_factor
)
# construct A operator for QAE for the payoff function by
# composing the uncertainty model and the objective
bull_spread = bull_spread_objective.compose(uncertainty_model, front=True)
[5]:
# plot exact payoff function (evaluated on the grid of the uncertainty model)
x = uncertainty_model.values
y = np.minimum(np.maximum(0, x - strike_price_1), strike_price_2 - strike_price_1)
plt.plot(x, y, '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()

[6]:
# evaluate exact expected value (normalized to the [0, 1] interval)
exact_value = np.dot(uncertainty_model.probabilities, y)
exact_delta = sum(uncertainty_model.probabilities[np.logical_and(x >= strike_price_1, x <= strike_price_2)])
print('exact expected value:\t%.4f' % exact_value)
print('exact delta value: \t%.4f' % exact_delta)
exact expected value: 0.5695
exact delta value: 0.9291
Evaluation du paiement attendu¶
[7]:
# set target precision and confidence level
epsilon = 0.01
alpha = 0.05
# construct amplitude estimation
ae = IterativeAmplitudeEstimation(epsilon=epsilon, alpha=alpha,
state_preparation=bull_spread,
objective_qubits=[num_uncertainty_qubits],
post_processing=bull_spread_objective.post_processing)
[8]:
result = ae.run(quantum_instance=Aer.get_backend('qasm_simulator'), shots=100)
[9]:
conf_int = np.array(result['confidence_interval'])
print('Exact value: \t%.4f' % exact_value)
print('Estimated value:\t%.4f' % result['estimation'])
print('Confidence interval: \t[%.4f, %.4f]' % tuple(conf_int))
Exact value: 0.5695
Estimated value: 0.5694
Confidence interval: [0.5639, 0.5750]
Evaluation du Delta¶
Le Delta est un peu plus simple à évaluer que la fonction de gain. Tout comme pour la fonction de gain, nous utilisons un circuit comparateur et un qubit ancillaire pour identifier les cas où :math:``K_1 leq S_T leq K_2`. Cependant, puisque nous ne sommes intéressés que par la probabilité que cette condition soit vraie, nous pouvons utiliser le qubit auxilliaire directement comme qubit objectif pour obtenir l’estimation d’amplitude sans approximations supplémentaires.
[10]:
# setup piecewise linear objective fcuntion
breakpoints = [low, strike_price_1, strike_price_2]
slopes = [0, 0, 0]
offsets = [0, 1, 0]
f_min = 0
f_max = 1
bull_spread_delta_objective = LinearAmplitudeFunction(
num_uncertainty_qubits,
slopes,
offsets,
domain=(low, high),
image=(f_min, f_max),
breakpoints=breakpoints,
) # no approximation necessary, hence no rescaling factor
# construct the A operator by stacking the uncertainty model and payoff function together
bull_spread_delta = bull_spread_delta_objective.compose(uncertainty_model, front=True)
[11]:
# set target precision and confidence level
epsilon = 0.01
alpha = 0.05
# construct amplitude estimation
ae_delta = IterativeAmplitudeEstimation(epsilon=epsilon, alpha=alpha,
state_preparation=bull_spread_delta,
objective_qubits=[num_uncertainty_qubits])
[12]:
result_delta = ae_delta.run(quantum_instance=Aer.get_backend('qasm_simulator'), shots=100)
[13]:
conf_int = np.array(result_delta['confidence_interval'])
print('Exact delta: \t%.4f' % exact_delta)
print('Estimated value:\t%.4f' % result_delta['estimation'])
print('Confidence interval: \t[%.4f, %.4f]' % tuple(conf_int))
Exact delta: 0.9291
Estimated value: 0.9290
Confidence interval: [0.9264, 0.9316]
[14]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | None |
Terra | 0.16.0.dev0+28d8c6a |
Aer | 0.6.1 |
Ignis | 0.5.0.dev0+470d8cc |
Aqua | 0.8.0.dev0+ce81016 |
IBM Q Provider | 0.8.0 |
System information | |
Python | 3.7.7 (default, May 6 2020, 04:59:01) [Clang 4.0.1 (tags/RELEASE_401/final)] |
OS | Darwin |
CPUs | 2 |
Memory (Gb) | 16.0 |
Fri Oct 16 11:03:18 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.
[ ]: