Skip to content

Commit

Permalink
CPC v3.5 (as_dicts, as_df, minrw, price estimate)
Browse files Browse the repository at this point in the history
  • Loading branch information
sklbancor committed Apr 22, 2024
1 parent 38d86c1 commit 825ff7c
Showing 1 changed file with 37 additions and 23 deletions.
60 changes: 37 additions & 23 deletions fastlane_bot/tools/cpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
NOTE: this class is not part of the API of the Carbon protocol, and you must expect breaking
changes even in minor version updates. Use at your own risk.
"""
__VERSION__ = "3.4"
__DATE__ = "23/Jan/2024"
__VERSION__ = "3.5"
__DATE__ = "22/Apr/2023"

from dataclasses import dataclass, field, asdict, InitVar
from .simplepair import SimplePair as Pair
Expand Down Expand Up @@ -836,7 +836,7 @@ def from_univ3(cls, Pmarg, uniL, uniPa, uniPb, pair, cid, fee, descr, params=Non
constr="uv3",
params=params,
)

SOLIDLY_PRICE_SPREAD = 0.06 # 0.06 gives pretty good results for m=2.6
@classmethod
def from_solidly(
Expand Down Expand Up @@ -982,6 +982,9 @@ def from_solidly(
print("[cpc::from_solidly] returning curve directly is deprecated; prepare to accept a list of curves in the future")
return result

# minimun range width (pa/pb-1) for carbon curves and sqrt thereof
CARBON_MIN_RANGEWIDTH = 1e-6

@classmethod
def from_carbon(
cls,
Expand All @@ -999,6 +1002,7 @@ def from_carbon(
descr=None,
params=None,
isdydx=True,
minrw=None,
):
"""
constructor: from a single Carbon order (see class docstring for other parameters) (1)
Expand All @@ -1011,6 +1015,7 @@ def from_carbon(
:B: alternative to pa, pb: B = sqrt(pb) in dy/dy
:tkny: token y
:isdydx: if True prices in dy/dx, if False in quote direction of the pair
:minrw: minimum perc width (pa/pb-1) of range (default CARBON_MIN_RANGEWIDTH)
NOTE 1: that ALL parameters are mandatory, except that EITHER pa, bp OR A, B
must be given but not both; we do not correct for incorrect assignment of
Expand All @@ -1028,7 +1033,10 @@ def from_carbon(
# assert not fee is None, "fee must not be None"
# assert not cid is None, "cid must not be None"
# assert not descr is None, "descr must not be None"


if minrw is None:
minrw = cls.CARBON_MIN_RANGEWIDTH

# if yint is None:
# yint = y
assert y <= yint, "y must be <= yint"
Expand Down Expand Up @@ -1067,15 +1075,21 @@ def from_carbon(
if not tkny == tknq:
pa, pb = 1 / pa, 1 / pb

# zero-width ranges are somewhat extended for numerical stability
# small and zero-width ranges are extended for numerical stability
pa0, pb0 = pa, pb
if pa/pb-1 < minrw:
pa = pb = sqrt(pa*pb)
assert pa == pb, "just making sure"
if pa == pb:
pa *= 1.0000001
pb /= 1.0000001
# pa *= 1.0000001
# pb /= 1.0000001
rw_multiplier = sqrt(1+minrw)
pa *= rw_multiplier
pb /= rw_multiplier

# validation
if not pa > pb:
raise cls.CPCValidationError(f"pa > pb required ({pa}, {pb})")
if not pa/pb - 1 >= minrw*0.99:
raise cls.CPCValidationError(f"pa +> pb required ({pa}, {pb}, {pa/pb-1}, {minrw})")

# finally set A, B
A = sqrt(pa) - sqrt(pb)
Expand All @@ -1094,7 +1108,7 @@ def from_carbon(
yasym_times_A = yint * B
kappa_times_A = yint**2 / A

params0 = dict(y=y, yint=yint, A=A0, B=B, pa=pa0, pb=pb0)
params0 = dict(y=y, yint=yint, A=A0, B=B, pa=pa0, pb=pb0, minrw=minrw)
if params is None:
params = AttrDict(params0)
else:
Expand Down Expand Up @@ -1805,13 +1819,15 @@ def scale(self, tkn):
"""returns the scale of tkn"""
return self.tokenscale.scale(tkn)

def asdicts(self):
def as_dicts(self):
"""returns list of dictionaries representing the curves"""
return [c.asdict() for c in self.curves]

def asdf(self):
asdicts = as_dicts # legacy name

def as_df(self):
"""returns pandas dataframe representing the curves"""
return pd.DataFrame.from_dict(self.asdicts()).set_index("cid")
asdf = as_df # legacy name

@classmethod
def from_dicts(cls, dicts, *, tokenscale=None):
Expand Down Expand Up @@ -2531,7 +2547,7 @@ def xystats(self, curves=None):
PE_DATA = "data"

def price_estimate(
self, *, tknq=None, tknb=None, pair=None, result=None, raiseonerror=True
self, *, tknq=None, tknb=None, pair=None, result=None, raiseonerror=True, verbose=False
):
"""
calculates price estimate in the reference token as base token
Expand All @@ -2544,6 +2560,7 @@ def price_estimate(
:PE_PAIR: slashpair
:PE_CURVES: curves
:PE_DATA: prices, weights
:verbose: whether to print some progress
:returns: price (quote per base)
"""
assert tknq is not None and tknb is not None or pair is not None, (
Expand All @@ -2570,6 +2587,8 @@ def price_estimate(
# return dict(curves=tuple(crvs), rcurves=tuple(rcrvs))
return tuple(acurves)
data = tuple((r[1], sqrt(r[2])) for r in acurves)
if verbose:
print(f"[price_estimate] {tknq}/{tknb} {len(data)} curves")
if not len(data) > 0:
if raiseonerror:
raise ValueError(f"no curves found for {tknq}/{tknb}")
Expand Down Expand Up @@ -2620,13 +2639,13 @@ def price_estimates(
tknqs = [t.strip() for t in tknqs.split(",")]
if isinstance(tknbs, str):
tknbs = [t.strip() for t in tknbs.split(",")]
# print(f"[price_estimates] tknqs [{len(tknqs)}], tknbs [{len(tknbs)}]")
# print(f"[price_estimates] tknqs [{len(tknqs)}] = {tknqs} , tknbs [{len(tknbs)}]] = {tknbs} ")
if verbose:
print(f"[price_estimates] tknqs [{len(tknqs)}] = {tknqs} , tknbs [{len(tknbs)}] = {tknbs} ")
resulttp = self.PE_PAIR if pairs else None
result = np.array(
[
[
self.price_estimate(tknb=b, tknq=q, raiseonerror=False, result=resulttp)
self.price_estimate(tknb=b, tknq=q, raiseonerror=False, result=resulttp, verbose=verbose)
for b in tknbs
]
for q in tknqs
Expand Down Expand Up @@ -2696,12 +2715,7 @@ def price_estimates(
}
# print("[price_estimates] result", result)
if not len(missing) == 0:
raise ValueError(
f"no price found for {len(missing)} pairs",
result,
missing,
len(missing),
)
raise ValueError(f"no price found for {len(missing)} pairs", missing, result)

#print(f"[price_estimates] DONE [{time.time()-start_time:.2f}s]")
if unwrapsingle and len(tknqs) == 1:
Expand Down

0 comments on commit 825ff7c

Please sign in to comment.