{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Quantum Finance Application on Arbitrage Opportunity Optimization\n", "\n", " Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Overview\n", "\n", "Current finance problems can be mainly tackled by three areas of quantum algorithms: quantum simulation, quantum optimization, and quantum machine learning [1,2]. Many financial problems are essentially combinatorial optimization problems, and corresponding algorithms usually have high time complexity and are difficult to implement. Due to the power of quantum computing, these complex problems are expected to be solved by quantum algorithms in the future.\n", "\n", "The Quantum Finance module of Paddle Quantum focuses on quantum optimization: how to apply quantum algorithms in real finance optimization problems. This tutorial focuses on how to use quantum algorithms to solve the arbitrage opportunity optimization problem [3].\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arbitrage Opportunity Optimization \n", "\n", "Arbitrage describes the fact that the same asset has different prices in different markets and can be traded between multiple markets to generate a positive return. That is, given a set of assets and transaction costs, it is possible to have a cycle among different markets that can generate a positive return.\n", "\n", "This problem can be formulated in the language of graph theory: given a weighted directed graph $G$ with vertex $i$ denoting $i$ th market currency, the weight of the edge from vertex $i$ to vertex $j$ denoting the exchange rate $c_{ij} $ that converts currency $i$ to currency $j$. That is if we have a currency $i$ of quantity $x_i$, then by the exchange rate, we will get a currency $j$ of quantity $c_{ij}x_i$. In general, the exchange rate is not symmetric, i.e. $c_{ij} \\neq c_{ji}$. We also assume that transaction costs (service fees etc.) are already included in the exchange rate.\n", "\n", "Given the number of tradable currency types $n$, the optimization problem is to find a cycle of the maximal profit on the given directed graph with $K (K \\leq n)$ vertices. In this finance module, users can define the number of vertices contained in the arbitrage cycle $K$ according to their requirements.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Encoding Arbitrage Opportunity Optimization Problem\n", "\n", "To transform the arbitrage opportunity optimization problem into a problem applicable for parameterized quantum circuits, we need to encode the arbitrage opportunity optimization problem into a Hamiltonian. We realize the encoding by first constructing an integer programming problem. Suppose there are $|V|=n$ vertices in graph $G$. Then for each vertex $i \\in V$, we define $n$ binary variables $x_{i,k}$, where $k \\in [0,K-1]$, such that\n", "\n", "$$\n", "x_{i, k}=\n", "\\begin{cases}\n", "1, & \\text{the order of traded to vertex } i \\text { in the arbitrage cycle is $k$}\\\\\n", "0, & \\text{otherwise}\n", "\\end{cases}.\n", "\\tag{1}\n", "$$\n", "\n", "Refering to the Travelling Salesman Problem ([TSP tutorial](./TSP_CN.ipynb)), since $G$ has $n$ vertices, we have $n^2$ variables in total, whose value we denote by a bit string $x=x_{0,0}x_{0,1}\\dots x_{n-1,K-1}$. Assume for now that the bit string $x$ represents a arbitrage cycle. Then for each edge $(i,j,w_{i,j}) \\in E$, we will have $x_{i,k} = x_{j,k+1}=1$, i.e., $x_{i,k}\\cdot x_{j,k+1}=1$, if and only if the arbitrage cycle visits vertex $i$ at time $k$ and vertex $j$ at time $k+1$. Otherwise, $x_{i,k}\\cdot x_{j,k+1}$ will be $0$. Therefore the logarithm of the profit of the cycle is:\n", "\n", "$$\n", "P(x) = - \\sum_{i,j\\in V} \\log(c_{ij}) \\sum_{k=0}^{K-1} x_{i,k}x_{j,k+1}, \\tag{2}\n", "$$\n", "\n", "For $x$ to represent a valid arbitrage cycle, the following constraint needs to be met:\n", "\n", "$$\n", "\\sum_{i=0}^{n-1} x_{i,k} = 1 \\quad \\text{and} \\quad \\sum_{k=0}^{K-1}\\sum_{(i,j)\\notin E}x_{i,k}x_{j, k+1}, \\tag{3}\n", "$$\n", "\n", "where the first equation guarantees that only one vertex is visited at each time $k$. The second term constrains that a nonexistent edge does not appear in the found arbitrage cycle. These two equations ensure that the parameterized quantum circuits find $𝑥$ as a simple loop. Then the cost function under the constraint can be formulated below:\n", "$$\n", "C_x = - P(x) + A\\sum_{k=0}^{K-1} \\left(1 - \\sum_{i=0}^{n-1} x_{i,k}\\right)^2 + A\\sum_{k=0}^{K-1}\\sum_{(i,j)\\notin E}\n", "x_{i,k}x_{j,k+1}.\n", "\\tag{4}\n", "$$\n", "In this equation, $V$ is the number of vertices of the graph, $E$ is the edge set of the graph and $K$ is the number of vertices of the most profitable cycle. Note that as we would like to maximize the $P(x)$ while ensuring $x$ represents a valid arbitrage cycle, we had better set $A$ large, at least larger than the largest weight of edges.\n", "\n", "We now need to transform the cost function $C_x$ into a Hamiltonian to realize the encoding of the arbitrage opportunity optimization problem. Each variable $x_{i,k}$ has two possible values, $0$ and $1$, corresponding to quantum states $|0\\rangle$ and $|1\\rangle$. Note that every variable corresponds to a qubit and so $n^2$ qubits are needed for solving the arbitrage opportunity optimization problem. The Pauli $Z$ operator has two eigenstates, $|0\\rangle$ and $|1\\rangle$ . Their corresponding eigenvalues are 1 and -1, respectively. So we consider encoding the cost function as a Hamiltonian using the Pauli $Z$ matrix.\n", "\n", "Now we would like to consider the mapping\n", "$$\n", "x_{i,k} \\mapsto \\frac{I-Z_{i,k}}{2}, \\tag{3}\n", "$$\n", "\n", "where $Z_{i,k} = I \\otimes I \\otimes \\ldots \\otimes Z \\otimes \\ldots \\otimes I$ with $Z$ operates on the qubit at position $(i,k)$. Under this mapping, the value of $x_{i,k}$ can be illustrated in a different way. If the qubit $(i,k)$ is in state $|1\\rangle$, then $x_{i,k} |1\\rangle = \\frac{I-Z_{i,k}}{2} |1\\rangle = 1|1\\rangle $, which means vertex $i$ is visited at time $k$. Also, for the qubit $(i,k)$ in state $|0\\rangle$, $x_{i,k}|0\\rangle = \\frac{I-Z_{i,k}}{2} |0\\rangle = 0 |0\\rangle $.\n", "\n", "Thus using the above mapping, we can transform the cost function $C_x$ into a Hamiltonian $H_C$ for the system of $n^2$ qubits and realize the quantumization of the arbitrage opportunity optimization problem. Then the ground state of $H_C$ is the optimal solution to the arbitrage opportunity optimization problem. In the following section, we will show how to use a parameterized quantum circuit to find the ground state, i.e., the eigenvector with the smallest eigenvalue." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Paddle Quantum Implementation\n", "\n", "To investigate the arbitrage opportunity optimization problem using Paddle Quantum, there are some required packages to import, which are shown below. The ``networkx`` package is the tool to handle graphs." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2021-05-17T08:00:15.901429Z", "start_time": "2021-05-17T08:00:12.708945Z" } }, "outputs": [], "source": [ "# Import packages needed\n", "import numpy as np\n", "import networkx as nx\n", "import matplotlib.pyplot as plt\n", "\n", "# Import related modules from Paddle Quantum and PaddlePaddle\n", "import paddle\n", "import paddle_quantum\n", "from paddle_quantum.ansatz import Circuit\n", "from paddle_quantum.finance import arbitrage_opportunities_hamiltonian" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we generate a weighted directed graph $G$ with $3$ vertices. For the convenience of computation, the vertices here are labeled starting from $0$.\n", "\n", "At the same time, in order to verify the solution more easily, special values are assigned for the weights in the directed graph. In practice, users can construct their own desired graphs and set the real exchange rates." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2021-05-17T08:00:16.212260Z", "start_time": "2021-05-17T08:00:15.918792Z" } }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# n is the number of vertices in the graph G\n", "n = 3\n", "nodes = [ \"JPY\", \"CNY\", \"USD\"]\n", "G = nx.DiGraph()\n", "G.add_nodes_from(nodes)\n", "edges = [(\"JPY\",\"CNY\", 0.5), (\"CNY\",\"JPY\",2), (\"CNY\",\"USD\", 0.33), (\"USD\",\"CNY\",3),(\"JPY\",\"USD\", 0.25), (\"USD\",\"JPY\",4)]\n", "G.add_weighted_edges_from(edges)\n", "\n", "# The two graphs represent the exchange rates in different directions\n", "G1 = nx.DiGraph()\n", "G1.add_nodes_from(nodes)\n", "edges1 = [(\"JPY\",\"CNY\", 0.5), (\"CNY\",\"USD\", 0.33), (\"USD\",\"JPY\",4)]\n", "G1.add_weighted_edges_from(edges1)\n", "\n", "G2 = nx.DiGraph()\n", "G2.add_nodes_from(nodes)\n", "edges2 = [(\"CNY\",\"JPY\",2), (\"USD\",\"CNY\",3),(\"JPY\",\"USD\", 0.25)]\n", "G2.add_weighted_edges_from(edges2)\n", "\n", "\n", "options = {\n", " \"with_labels\": True,\n", " \"font_weight\": \"bold\",\n", " \"font_color\": \"white\",\n", " \"node_size\": 2000,\n", " \"width\": 2\n", "}\n", "fig, ax = plt.subplots(1, 2, figsize=(15, 4))\n", "for i, a in enumerate(ax):\n", " a.axis('off')\n", " a.margins(0.20)\n", "nx.draw(G1, pos=nx.circular_layout(G1), ax=ax[0], **options)\n", "nx.drawing.nx_pylab.draw_networkx_edge_labels(G1, pos=nx.circular_layout(G1), ax=ax[0], edge_labels=nx.get_edge_attributes(G1, 'weight'))\n", "nx.draw(G2, pos=nx.circular_layout(G2), ax=ax[1], **options)\n", "nx.drawing.nx_pylab.draw_networkx_edge_labels(G2, pos=nx.circular_layout(G2), ax=ax[1], edge_labels=nx.get_edge_attributes(G2, 'weight'))\n", "plt.axis(\"off\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Encoding Hamiltonian\n", "\n", "Here we construct the Hamiltonian $H_C$ of Eq. (4) with the replacement in Eq. (5). \n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2021-05-17T08:00:16.237497Z", "start_time": "2021-05-17T08:00:16.219567Z" } }, "outputs": [], "source": [ "# Penalty parameter: in this case, it is equal to the maximum weight of the edges in the directed graph\n", "penalty = 4 \n", "# In this example, all currency types are selected within the transaction cycle\n", "K = n\n", "# Construct the Hamiltonian of arbitrage opportunity optimization\n", "hamiltonian = arbitrage_opportunities_hamiltonian(G, penalty, n, K)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calculating the loss function \n", "\n", "We adopt a parameterized quantum circuit consisting of $U_3(\\vec{\\theta})$ and $\\text{CNOT}$ gates, that can be done by calling the built-in method [`complex_entangled_layer()`](https://qml.baidu.com/api/paddle_quantum.ansatz.circuit.html#Circuit.complex_entangled_layer).\n", "\n", "After running the quantum circuit, we obtain the output circuit $|\\vec{\\theta\n", "}\\rangle$. From the output state of the circuit, we can define the loss function of the arbitrage opportunity optimization under the classical-quantum hybrid model:\n", "\n", "$$\n", "L(\\vec{\\theta}) = \\langle\\vec{\\theta}|H_C|\\vec{\\theta}\\rangle.\n", "\\tag{6}\n", "$$\n", "\n", "We then use a classical optimization algorithm to minimize this function and find the optimal parameters $\\vec{\\theta}^*$. The following code shows a complete network built with Paddle Quantum and PaddlePaddle." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2021-05-17T08:00:16.258893Z", "start_time": "2021-05-17T08:00:16.241066Z" } }, "outputs": [], "source": [ "class AONet(paddle.nn.Layer):\n", " def __init__(self, num_qubits, p, dtype=\"float64\"):\n", " super(AONet, self).__init__()\n", "\n", " self.depth = p\n", " self.num_qubits = num_qubits\n", " self.cir = Circuit(self.num_qubits)\n", " self.cir.complex_entangled_layer(depth=self.depth)\n", "\n", " def forward(self):\n", " \"\"\"\n", " Forward propagation\n", " \"\"\"\n", " # Run the quantum circuit\n", " state = self.cir(init_state)\n", " # Calculate the loss function\n", " loss = loss_func(state)\n", "\n", " return loss, self.cir" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Training the quantum neural network\n", "\n", "After defining the quantum neural network, we use the gradient descent method to update the parameters to minimize the expectation value in Eq. (6). " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "SEED = 100 # Set a global RNG seed \n", "p = 1 # Number of layers in the quantum circuit\n", "ITR = 120 # Number of training iterations\n", "LR = 0.4 # Learning rate of the optimization method based on gradient descent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we optimize the network defined above in PaddlePaddle." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "iter: 10 loss: 0.4704\n", "iter: 20 loss: 0.1302\n", "iter: 30 loss: -0.2744\n", "iter: 40 loss: -0.4700\n", "iter: 50 loss: -0.5512\n", "iter: 60 loss: -0.5684\n", "iter: 70 loss: -0.5821\n", "iter: 80 loss: -0.5833\n", "iter: 90 loss: -0.5843\n", "iter: 100 loss: -0.5847\n", "iter: 110 loss: -0.5849\n", "iter: 120 loss: -0.5849\n" ] } ], "source": [ "# Fix paddle random seed\n", "paddle.seed(SEED)\n", "# define the number of qubits need for circuit\n", "num_qubits = n * K\n", "# Building Quantum Neural Networks\n", "net = AONet(num_qubits, p)\n", "# Define initial state\n", "init_state = paddle_quantum.state.zero_state(num_qubits)\n", "# Use Adam optimizer\n", "opt = paddle.optimizer.Adam(learning_rate=LR, parameters=net.parameters())\n", "# define loss function\n", "loss_func = paddle_quantum.loss.ExpecVal(hamiltonian)\n", "# Gradient descent iteration\n", "for itr in range(1, ITR + 1):\n", " # Run the network defined above\n", " loss, cir = net()\n", " # Calculate the gradient and optimize\n", " loss.backward()\n", " opt.minimize(loss)\n", " opt.clear_grad()\n", " if itr % 10 == 0:\n", " print(\"iter: \", itr, \" loss: \", \"%.4f\"% loss.numpy())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Decoding the quantum solution\n", "\n", "After obtaining the minimum value of the loss function and the corresponding set of parameters $\\vec{\\theta}^*$, our task has not been completed. In order to obtain an approximate solution to the arbitrage opportunity optimization problem, it is necessary to decode the solution to the classical optimization problem from the quantum state $|\\vec{\\theta}^*\\rangle$ output by the circuit. To decode a quantum state, we need to measure it and then calculate the probability distribution of the measurement results, where a measurement result is a bit string that represents an answer for the arbitrage opportunity optimization problem: \n", "\n", "$$\n", "p(z) = |\\langle z|\\vec{\\theta}^*\\rangle|^2.\n", "\\tag{6}\n", "$$\n", "\n", "In the case of quantum parameterized circuits with sufficient expressiveness, the greater the probability of a certain bit string is, the greater the probability that it corresponds to an optimal solution of the arbitrage opportunity optimization problem.\n", "\n", "Paddle Quantum provides a function to read the probability distribution of the measurement results of the state output by the quantum circuit:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The bit string form of the solution: 100001010\n" ] } ], "source": [ "# Repeat the simulated measurement of the circuit output state 1024 times\n", "final_state = cir(init_state)\n", "prob_measure = final_state.measure(shots=1024)\n", "arbitrage_opportunity_route = max(prob_measure, key=prob_measure.get)\n", "print(\"The bit string form of the solution:\", arbitrage_opportunity_route)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After measurement, we have found the bit string with the highest probability of occurrence, i.e. the most profitable cycle in the form of the bit string. The binary strings are grouped every $n$ bits, and $1$ appearing at the $k$th bit in each group indicates the order that the asset is traded into this market. If the result is not valid as explained before, users can get better training results by adjusting parameters such as the random seed ``SEED``, the number of layers of the quantum circuit ``p``, the number of iterations ``ITR`` and the gradient descent optimization rate ``LR``.\n", "\n", "The following code maps the bit string back to the classic solution in the form of `dictionary`, where the `key` represents the vertex currency and the `value` represents its order, i.e. when the asset is traded into this market.\n", "\n", "Also, we calculated the positive return of the asset after the optimal arbitrage cycle." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "ExecuteTime": { "end_time": "2021-05-17T08:02:14.554317Z", "start_time": "2021-05-17T08:02:14.500593Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'JPY': 0, 'CNY': 2, 'USD': 1}\n", "Positive return rate: 1.5\n" ] } ], "source": [ "solution = {nodes[i]:t for i in range(n) for t in range(n) if arbitrage_opportunity_route[i * n + t] == '1'}\n", "print(solution)\n", "rate = sum([np.log2(G[u][v][\"weight\"]) if solution[v] == (solution[u] + 1) % n else 0 for (u, v) in G.edges])\n", "print(\"Positive return rate: \", 2**rate)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to clearly represent the most profitable cycle of the assets, a graphical representation is still chosen:\n", "* The numbers in the vertices represent the order of the transaction to this market and the letters represent currencies\n", "\n", "* The red edges represent the found optimal route." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "ExecuteTime": { "end_time": "2021-05-17T08:02:14.571954Z", "start_time": "2021-05-17T08:02:14.559634Z" } }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1MAAADnCAYAAAD7CwxiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAA4PUlEQVR4nO3deXiU9b3//+d9ZyaTjQAh7IRA2BeVYgLI4sJSScT14FrUywWX6jnaY63oOWgrHvXXWrUesRWqbYW6Yn+1aoKCcARRCIsLu0hCSNhDlJB9lvv7x5BADGGGMMlsr8d1cZXMfefOZ+LVefO635/78zEsy7IQERERERGR02IGewAiIiIiIiLhSGFKRERERESkBRSmREREREREWkBhSkREREREpAUUpkRERERERFpAYUpERERERKQFFKZERERERERaQGFKRERERESkBRSmREREREREWkBhSkREREREpAUUpkRERERERFpAYUpERERERKQFFKZERERERERaQGFKRERERESkBRSmREREREREWkBhSkREREREpAUUpkRERERERFpAYUpERERERKQFFKZERERERERaQGFKRERERESkBWzBHkCo8ngsisqqKCytoMbpwen2YI8xibOb9E1NIj0lAdM0gj1MERGRNqX6KCJynMLUMR6PxaqdpSzbdpC1u8rYcbAC0zCwmQYWFpYFhgEGBi6PhceyGNAliaw+KUwc3IVx/VJVPEREJOKoPoqINM+wLMsK9iCC6Ui1k7fXFTN/ZQGVtS6q6tyczi/EABJiY0h02Jg5IYNrMtNoH29vreGKiIi0CdVHERHfojZMVde5eSpvK2+tK8YwoMbpOeNrxttNPBZcm5nGw9lDiI+NCcBIRURE2o7qo4iI/6IyTOUXlnHvGxsor3ZS4zrzIvFjcTaT5Hg7c28YSVaflIBfX0REpDWoPoqInJ6oClO1LjdzPtjCog0lAbnT5kuc3WT6yF7MnjYUh0134UREJDSpPoqItEzUhKnKWhczXlnD1n3lrXK3rTlxNpOhPZJZcOtoEh1a70NEREKL6qOISMtFRZiqrHUx/eXPKThUSW0bFop6DptJRudEFt05VgVDRERChuqjiMiZifhNe2tdbma8siZohcI7Bg8Fhyq58dU11LrcQRmDiIjIiVQfRUTOXMSHqTkfbGHrvvKgFYp6tS4PW/aWM+eD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9mjCjz9dq/pNg9PSgjdOEWmWwpQPClOB5W/XKjs7mx49egRxpCIickpOJ6SmQnk5pKdDYaH3QSlpGXWtRMKSwpQPClOtp75rlZubS15eHtu3b290/JxzzmkIVupaiYiEmFdfhdtuO/714sVw8cXBG0+k8dW1mjz5eLhS10okaBSmfFCYajvqWomIhAmnEwYN8naj6o0ZA59/ru5Ua6irg88+O778urpWIiFDYcoHhangUNdKRCSELVwIN97Y9PVly+Cii9p+PNFGXSuRkKEw5YPCVGho7a5VTU0NcXFxgRyyiEjk+vvfvWHqxH9C2Gzef+BPmhS8cUUjda1EgkphygeFqdAT6K7Vn//8Z/7+979z8OBBnn76aaZNm6ZlfUVEfCkvh1mz4I9/hN/8Bv7zP71dEQmu3buPB6tPPlHXSqSVKUz5oDAV+vzpWt1yyy1kZ2djmmaj783Pz+e6665j9erVbN68md/97ne8++67xMfHN5zjdruJiYlps/cjIhI2HngAnn0WnnnG+3cJLa3Qtdq0aRM9e/akY8eOrTRokfBi+j5FJLT17duXn//857z//vscPnyYjz76iPvvv59BgwZRXl7Ou+++y/bt2/F4PI2+r6qqig8//JBbbrmFLl260K9fPwDKysoaznG73cybN4+xY8cyZ84cNmzY0KbvTUREpMViY2HiRPjd72DTJigqgj/9CS6/3Nul2rzZG4QnTYJOneDKK717hpWUnPRyP/zwAy+99BLjx49nzJgxfPTRR238hkRCj8KURJS4uDh++tOf8txzz7Ft2zYKCgqYO3cuP/vZz7DZbI3OLSkpYf/+/Uw6Nr+/rKyMIUOG8O233zacc/DgQfbt28fQoUNxuVw8+uijfPDBB236nkRERAKid2+480745z/h8GHvNMBf/tLboaqo8L5+553wi180nh54jMPh4KmnnmLz5s08/vjjvPTSS41mg4hEI5vvU0TCV33X6mT27dtHZWUl55xzDgAHDhygvLycPn36NJxTUlLC4cOHmT17Nunp6QC4XK5WH7eIiEirqu9a1XeuTnzW6rrrTvr8W3x8PA6HA4BevXpx+PBhampqSEhIaOvRi4QMhSmJWuXl5ezdu5fExETKy8vZsWMHKSkp9O3bt+GcsrIyPvzwQwoLC7n77rvJzs5u0uESEREJe/VdqzvvbLxK4wnq6uqIPfZc1fz58xk5cqRWwpWop2l+ErXatWtHamoqAB9//DGffvopV1xxBUDD81UXXngh69at47rrruO1115j9+7dwRquiIhI22hmRdvY2Fj27t3LtGnTOHz4MPfee+/xrtSBA1Bc3IaDFAkNClMStcaPH09aWhrdunXjL3/5C/fccw/Dhw8HaFj1z+FwkJqayk033UT//v159tlnG11DU/5ERCQaVFZW8uabbzJjxgzOP/98XnrpJQYOHOg9WFUFTz3l7W4NHw4PPgjLl3tXExSJcApTErVsNhu///3v2b9/P3/961+58MIL+dvf/saCBQsAWLVqFXv37m04/+uvv254nsqyLFwuFy+++CIjRozg4YcfZsWKFTidzmC8FRERkVZTV1fHzTffzP333899993Hr371K5JOfKbKbvcGpxNXCJw4sfEKgepaSYTSPlM+aJ+p6ON0OrHb7bz44ovMmzePzp070717d+rq6vjDH/5A9+7dAe9duunTp7N48eKG723fvj1TpkwhOzubqVOn0qNHj2C9DRGR1qd9pqKC2+3m7bffJi8vj507d2JZFrNmzeLSSy9tvMn9ifta5ebCli2NLzRsGOTkePe1GjfOr32tREKdwpQPClOyZs0adu7cydVXX43dbm90rKamhhUrVjRsGrx9+/ZGx8855xyys7PJzs7mvPPOa/L9IiJhTWEqKq1Zswabzca555576hOLimDxYm+4WroUKiuPH0tKgsmTj28anJbWuoMWaSUKUz4oTMnpKCgoaAhWy5Yto7q6uuGYulYiEnEUpsRftbXHu1Z5eU27VsOHHw9W6lpJGFGY8kFhSlrK365VTk4O5513npZcF5HwozAlLaWulUQIhSkfFKYkUNS1EpGIozAlgaCulYQxhSkfFKakNfjTtcrJyWl41kpdKxEJSQpT0hrUtZIwojDlg8KUtAV1rUQkLClMSWtT10pCnMKUDwpT0tZ8da1GjBjRaIVAda1EJGgUpqSt1XetcnPhk08ad63atYNJk44vv96rV/DGKVFDYcoHhSkJNnWtRCRkKUxJMKlrJSFAYcoHhSkJJSd2rXJzc/n2228bHVfXyjePx6KorIrC0gpqnB6cbg/2GJM4u0nf1CTSUxIwTcP3hUREYUpCi7pWZ0T1sWUUpnxQmJJQ5k/XKicnh6lTp9K9e/cgjjR4PB6LVTtLWbbt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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "label_dict = {i: str(i) + \", \" + str(t) for i, t in solution.items()}\n", "edge_color1 = [\"red\" if solution[v] == (solution[u] + 1) % n else \"black\"\n", " for (u, v) in G1.edges]\n", "edge_color2 = [\"red\" if solution[v] == (solution[u] + 1) % n else \"black\"\n", " for (u, v) in G2.edges]\n", "\n", "# Draw the optimal arbitrage cycle on the graph\n", "fig, ax = plt.subplots(1, 2, figsize=(15, 4))\n", "for i, a in enumerate(ax):\n", " a.axis('off')\n", " a.margins(0.20)\n", "nx.draw(G1, pos=nx.circular_layout(G1), labels=label_dict, edge_color=edge_color1, ax=ax[0], **options)\n", "nx.drawing.nx_pylab.draw_networkx_edge_labels(G1, pos=nx.circular_layout(G1), ax=ax[0], edge_labels=nx.get_edge_attributes(G1, 'weight'))\n", "nx.draw(G2, pos=nx.circular_layout(G2), labels=label_dict, edge_color=edge_color2, ax=ax[1], **options)\n", "nx.drawing.nx_pylab.draw_networkx_edge_labels(G2, pos=nx.circular_layout(G2), ax=ax[1], edge_labels=nx.get_edge_attributes(G2, 'weight'))\n", "plt.axis(\"off\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The left graph given above shows the edge that is not in the arbitrage cycle, and the right graph shows the optimal solution found by the algorithm." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conclusion\n", "\n", "In this tutorial, we provide a way to approximate a cycle of transactions with the maximum positive return in the arbitrage opportunity problem. For $n$ currencies, we need to use $n^2$ qubits for the optimization. We assign special values to exchange rates for testing, and users can assign those values themselves, i.e. according to the current market.\n", "\n", "In real financial markets, high return as $1.5$ in this tutorial on arbitrage is not usually available. Also, the number of currencies considered would be large with more influencing factors. This would be hard to implement. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "_______\n", "\n", "## References\n", "\n", "[1] Orus, Roman, Samuel Mugel, and Enrique Lizaso. \"Quantum computing for finance: Overview and prospects.\" [Reviews in Physics 4 (2019): 100028.](https://arxiv.org/abs/1807.03890)\n", "\n", "[2] Egger, Daniel J., et al. \"Quantum computing for Finance: state of the art and future prospects.\" [IEEE Transactions on Quantum Engineering (2020).](https://arxiv.org/abs/2006.14510)\n", "\n", "[3] Rosenberg, G. \"Finding optimal arbitrage opportunities using a quantum annealer.\" [1QB Information Technologies Write Paper (2016): 1-7.](https://1qbit.com/whitepaper/arbitrage/)" ] } ], "metadata": { "interpreter": { "hash": "3b61f83e8397e1c9fcea57a3d9915794102e67724879b24295f8014f41a14d85" }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }