Skip to content

Commit

Permalink
Merge pull request #163 from curveresearch/bonding-curve
Browse files Browse the repository at this point in the history
Refactor and test bonding curve
  • Loading branch information
chanhosuh authored Jul 24, 2023
2 parents 2a6759d + aa3c1e8 commit 516a14d
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 97 deletions.
13 changes: 13 additions & 0 deletions changelog.d/20230724_120508_chanhosuh_bonding_curve.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Added
-----
- End-to-end test for the bonding curve that creates a pool
using the `make` pool factory. This covers some important
gaps in our codebase.


Changed
-------
- Refactored the bonding curve function, separating the core logic
from the plotting functionality.
- Created `tools` module to house the bonding curve and in anticipation
of further tools, e.g. orderbook.
2 changes: 1 addition & 1 deletion curvesim/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Package to simulate Curve pool."""
__all__ = ["autosim", "bonding_curve", "order_book", "__version__", "__version_info__"]

from ._bonding_curve import bonding_curve
from ._order_book import order_book
from .sim import autosim
from .tools import bonding_curve
from .version import __version__, __version_info__
90 changes: 0 additions & 90 deletions curvesim/_bonding_curve/__init__.py

This file was deleted.

5 changes: 5 additions & 0 deletions curvesim/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"Handy functions for the user interacting with Curvesim pools."

__all__ = ["bonding_curve"]

from .bonding_curve import bonding_curve
97 changes: 97 additions & 0 deletions curvesim/tools/bonding_curve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
Contains the bonding_curve function, which computes a pool's bonding
curve and current reserves for each pair of coins and optionally
plots the curves using Matplotlib.
"""
from itertools import combinations

import matplotlib.pyplot as plt
from numpy import linspace

from curvesim.pool import CurveMetaPool

D_UNIT = 10**18


def bonding_curve(pool, *, truncate=0.0005, resolution=1000, plot=False):
"""
Computes and optionally plots a pool's bonding curve and current reserves.
Parameters
----------
pool : CurvePool or CurveMetaPool
The pool object for which the bonding curve is computed.
truncate : float, optional (default=0.0005)
Determines where to truncate the bonding curve. The truncation point is given
by D*truncate, where D is the total supply of tokens in the pool.
resolution : int, optional (default=1000)
The number of points to compute along the bonding curve.
plot : bool, optional (default=False)
Plots the bonding curves using Matplotlib.
Returns
-------
pair_to_curve : dict
Dictionary with coin index pairs as keys and lists of corresponding reserves
as values. Each list of reserves is a list of pairs, where each pair consists
of the reserves for the first and second coin of the corresponding pair.
Example
--------
>>> import curvesim
>>> pool_address = "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7"
>>> pool = curvesim.pool.get(pool_address)
>>> pair_to_curve = curvesim.bonding_curve(pool, plot=True)
"""

if isinstance(pool, CurveMetaPool):
combos = [(0, 1)]
else:
combos = combinations(range(pool.n), 2)

D = pool.D()
xp = pool._xp() # pylint: disable=protected-access

pair_to_curve = {}
for (i, j) in combos:
truncated_D = int(D * truncate)
x_max = pool.get_y(j, i, truncated_D, xp)
xs = linspace(truncated_D, x_max, resolution).round()

curve = []
for x in xs:
y = pool.get_y(i, j, int(x), xp)
curve.append((x, y))
curve = [(x / D_UNIT, y / D_UNIT) for x, y in curve]
pair_to_curve[(i, j)] = curve

if plot:
labels = pool.coin_names
if not labels:
labels = [f"Coin {str(label)}" for label in range(pool.n)]

_plot_bonding_curve(pair_to_curve, labels, xp)

return pair_to_curve


def _plot_bonding_curve(pair_to_curve, labels, xp):
n = len(pair_to_curve)
_, axs = plt.subplots(1, n, constrained_layout=True)
if n == 1:
axs = [axs]

for pair, ax in zip(pair_to_curve, axs):
curve = pair_to_curve[pair]
xs, ys = zip(*curve)
ax.plot(xs, ys, color="black")

i, j = pair
ax.scatter(xp[i] / D_UNIT, xp[j] / D_UNIT, s=40, color="black")
ax.set_xlabel(labels[i])
ax.set_ylabel(labels[j])

plt.show()
12 changes: 6 additions & 6 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ Curve Pools
:members:


.. Pool Plots
.. ----------
..
.. .. _poolviewersapi:
..
.. .. autofunction:: curvesim.bonding_curve
Pool Plots
----------

.. _poolviewersapi:

.. autofunction:: curvesim.bonding_curve
.. .. autofunction:: curvesim.order_book
Expand Down
80 changes: 80 additions & 0 deletions test/integration/test_bonding_curve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import curvesim
from curvesim import bonding_curve


def test_bonding_curve_stableswap():
"""Simple test of the bonding curve for a regular stableswap."""
A = 2000
balances = [96930673769101734848937206, 96029665968769, 94203880672841]
rates = [10**18, 10**30, 10**30]
pool = curvesim.pool.make(A, balances, 3, rates=rates)
pair_to_curve = bonding_curve(pool, resolution=5)
expected_result = {
(0, 1): [
(143582.10515040305, 207709896.10151905),
(52035160.60424256, 140938067.54743478),
(103926739.10333471, 89033886.31117772),
(155818317.6024269, 37171084.50615266),
(207709896.10151905, 143582.1051504031),
],
(0, 2): [
(143582.10515040305, 205740351.86334246),
(51542774.54469842, 139604657.24397892),
(102941966.98424643, 88192864.27513982),
(154341159.42379445, 36822438.40404793),
(205740351.86334246, 143582.10515040308),
],
(1, 2): [
(143582.10515040305, 204771181.49157524),
(51300481.95175662, 138945948.269023),
(102457381.79836284, 87776447.07614492),
(153614281.64496905, 36648317.74309717),
(204771181.49157527, 143582.10515040296),
],
}
assert pair_to_curve == expected_result


def test_bonding_curve_metapool():
"""Simple test of the bonding curve for a regular stableswap.
Note: test data was generated via
pool_address = "0x4e43151b78b5fbb16298C1161fcbF7531d5F8D93"
pool = curvesim.pool.get(pool_address)
basepool = pool.basepool
pair_to_curve = bonding_curve(pool, resolution=5)
"""
pool_address = "0x4e43151b78b5fbb16298C1161fcbF7531d5F8D93"
pool = curvesim.pool.get(pool_address)
basepool = pool.basepool
pair_to_curve = bonding_curve(pool, resolution=5)

A = 1500
rates = [10**18, 10**30]
balances = [350744085115649212803306457, 141003714500628]
bp_tokens = 491124709934878945923137105
basepool = curvesim.pool.make(A, balances, 2, rates=rates, tokens=bp_tokens)

A = 1500
balances = [7059917, 88935085280709722288137]
rate_multiplier = 10**34
pool = curvesim.pool.make(
A,
balances,
2,
rate_multiplier=rate_multiplier,
basepool=basepool,
)
pair_to_curve = bonding_curve(pool, resolution=5)
expected_result = {
(0, 1): [
(79.81988656375063, 182748.88552045962),
(45747.08629503773, 113904.53710112568),
(91414.35270351169, 68226.56650379577),
(137081.61911198567, 22614.30606286462),
(182748.88552045965, 79.81988656375057),
]
}

assert pair_to_curve == expected_result

0 comments on commit 516a14d

Please sign in to comment.