diff --git a/fastlane_bot/tests/__init__.py b/fastlane_bot/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/fastlane_bot/tests/nbtest/_data/NBTEST_002_Curves.csv.gz b/fastlane_bot/tests/nbtest/_data/NBTEST_002_Curves.csv.gz new file mode 100644 index 000000000..486f5046c Binary files /dev/null and b/fastlane_bot/tests/nbtest/_data/NBTEST_002_Curves.csv.gz differ diff --git a/fastlane_bot/tests/nbtest/_data/NBTest_006-augmented.csv.gz b/fastlane_bot/tests/nbtest/_data/NBTest_006-augmented.csv.gz new file mode 100644 index 000000000..fc525c8ae Binary files /dev/null and b/fastlane_bot/tests/nbtest/_data/NBTest_006-augmented.csv.gz differ diff --git a/fastlane_bot/tests/nbtest/_data/NBTest_006.csv.gz b/fastlane_bot/tests/nbtest/_data/NBTest_006.csv.gz new file mode 100644 index 000000000..585e10021 Binary files /dev/null and b/fastlane_bot/tests/nbtest/_data/NBTest_006.csv.gz differ diff --git a/fastlane_bot/tests/nbtest/_data/README.md b/fastlane_bot/tests/nbtest/_data/README.md new file mode 100644 index 000000000..14320b5fb --- /dev/null +++ b/fastlane_bot/tests/nbtest/_data/README.md @@ -0,0 +1,19 @@ +# NBTest Data + +All data referred to by NBTest notebooks is stored in this directory. It is copied into the respective directory in the test area by the `run_tests` script. Currently the data will be accessed differently in the notebooks and in the actual tests. + +- **Notebooks**. In the notebooks the data can be accessed via a relative path `_data\mydata.csv`. + +- **Tests**. In the actual tests the data must be imported via the relative path `fastlane_bot/tests/nbtest/_data/mydata.csv` + + +Example + + try: + with open("_data/mydata.csv", "r") as f: + data = f.read() + except: + with open("fastlane_bot/tests/nbtest/_data/mydata.csv", "r") as f: + data = f.read() + + \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_002_CPCandOptimizer.py b/fastlane_bot/tests/nbtest/test_002_CPCandOptimizer.py index 3fea5f4d5..996f96285 100644 --- a/fastlane_bot/tests/nbtest/test_002_CPCandOptimizer.py +++ b/fastlane_bot/tests/nbtest/test_002_CPCandOptimizer.py @@ -9,16 +9,14 @@ from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, CPCContainer, T, CPCInverter, Pair -#from fastlane_bot.tools.simplepair import SimplePair -from fastlane_bot.tools.optimizer import CPCArbOptimizer, F -#import carbon.tools.tokenscale as ts +from fastlane_bot.tools.optimizer import CPCArbOptimizer, F, MargPOptimizer, SimpleOptimizer +from fastlane_bot.tools.analyzer import CPCAnalyzer print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Pair)) print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) -#print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(ts.TokenScale)) print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPCArbOptimizer)) from fastlane_bot.testing import * -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("3.0", __VERSION__) @@ -26,12 +24,66 @@ try: - df = pd.read_csv("../nbtest_data/NBTEST_002_Curves.csv.gz") + market_df = pd.read_csv("_data/NBTEST_002_Curves.csv.gz") except: - df = pd.read_csv("fastlane_bot/tests/nbtest_data/NBTEST_002_Curves.csv.gz") -CCmarket = CPCContainer.from_df(df) + market_df = pd.read_csv("fastlane_bot/tests/nbtest/_data/NBTEST_002_Curves.csv.gz") +CCmarket = CPCContainer.from_df(market_df) +# ------------------------------------------------------------ +# Test 002 +# File test_002_CPCandOptimizer.py +# Segment description +# ------------------------------------------------------------ +def test_description(): +# ------------------------------------------------------------ + + d = CCmarket.bycid("167").description().splitlines() + d0 = """ + cid = 167 [167] + primary = WETH/DAI [WETH/DAI] + pp = 1,826.764318 DAI per WETH + pair = DAI/WETH [DAI/WETH] + tknx = 3,967,283.591895 DAI [virtual: 3,967,283.592] + tkny = 2,171.754481 WETH [virtual: 2,171.754] + p = 0.0005474159913752679 [min=None, max=None] WETH per DAI + fee = 0.003 + descr = sushiswap_v2 DAI/WETH 0.003 + """.strip().splitlines() + d0 = [l.strip() for l in d0] + assert d == d0 + for l in d0: + print(l) + + +# ------------------------------------------------------------ +# Test 002 +# File test_002_CPCandOptimizer.py +# Segment bycids +# ------------------------------------------------------------ +def test_bycids(): +# ------------------------------------------------------------ + + CC = CCmarket + + assert len(CC.bycids()) == len(CC) + assert type(CC.bycids()) == type(CC) + assert type(CC.bycids(ascc=False)) == tuple + for c in CC: + assert isinstance(c.cid, str), f"{c.cid} is not of type str" + cids = [c.cid for c in CC] + assert raises(CC.bycids, include="foo", endswith="bar") == 'include and endswith cannot be used together' + assert raises(CC.bycids,"167, 168, 169") + CC1 = CC.bycids(["167", "168", "169"]) + assert len(CC1) == 3 + assert [c.cid for c in CC1] == ['167', '168', '169'] + CC2 = CC.bycids(endswith="11") + assert len(CC2) == 5 + assert [c.cid for c in CC2] == ['211', '311', '411', '511', '611'] + CC3 = CC.bycids(endswith="11", exclude=['311', '411']) + assert [c.cid for c in CC3] == ['211', '511', '611'] + + # ------------------------------------------------------------ # Test 002 # File test_002_CPCandOptimizer.py @@ -535,7 +587,7 @@ def test_price_estimates_in_optimizer(): CCfm += CPC.from_pk(p=pp, k=k, pair=pair, cid = f"mkt-{ctr}") ctr += 1 - O = CPCArbOptimizer(CCfm) + O = MargPOptimizer(CCfm) assert O.MO_PSTART == O.MO_P tknq = "WETH" df = O.margp_optimizer(tknq, result=O.MO_PSTART) @@ -711,13 +763,13 @@ def test_new_cpc_features_in_v2(): def test_real_data_and_retrieval_of_curves(): # ------------------------------------------------------------ - try: - df = pd.read_csv("../nbtest_data/NBTEST_002_Curves.csv.gz") - except: - df = pd.read_csv("fastlane_bot/tests/nbtest_data/NBTEST_002_Curves.csv.gz") - CC = CPCContainer.from_df(df) + # try: + # df = pd.read_csv("../nbtest_data/NBTEST_002_Curves.csv.gz") + # except: + # df = pd.read_csv("fastlane_bot/tests/nbtest_data/NBTEST_002_Curves.csv.gz") + CC = CPCContainer.from_df(market_df) assert len(CC) == 459 - assert len(CC) == len(df) + assert len(CC) == len(market_df) assert len(CC.pairs()) == 326 assert len(CC.tokens()) == 141 assert CC.tokens_s @@ -735,7 +787,7 @@ def test_real_data_and_retrieval_of_curves(): cids = [c.cid for c in CC.bypairs(CC.fp(onein="WBTC"))] assert len(cids) == len(CC1) assert CC.bycid("bla") is None - assert not CC.bycid(191) is None + assert not CC.bycid("191") is None assert raises(CC.bycids, ["bla"]) assert len(CC.bycids(cids)) == len(cids) assert len(CC.bytknx("WETH")) == 46 @@ -1065,8 +1117,8 @@ def test_simple_optimizer(): assert iseq([c.p for c in CC0][-1], 2000) # + - O = CPCArbOptimizer(CC) - O0 = CPCArbOptimizer(CC0) + O = SimpleOptimizer(CC) + O0 = SimpleOptimizer(CC0) func = O.simple_optimizer(result=O.SO_DXDYVECFUNC) func0 = O0.simple_optimizer(result=O.SO_DXDYVECFUNC) funcs = O.simple_optimizer(result=O.SO_DXDYSUMFUNC) @@ -1158,7 +1210,7 @@ def test_optimizer_plus_inverted_curves(): # CC.plot() # - - O = CPCArbOptimizer(CC) + O = SimpleOptimizer(CC) r = O.simple_optimizer() print(f"Arbitrage gains: {-r.valx:.4f} {r.tknxp} [time={r.time:.4f}s]") assert iseq(r.result, -1.3194573866437527) @@ -1248,7 +1300,7 @@ def test_tradeinstructions(): for i in range(10) ] tild = TI.to_dicts(til) - tildf = TI.to_df(til) + tildf = TI.to_df(til, robj=None) assert len(tild) == 10 assert len(tildf) == 10 assert tild[0] == { @@ -1286,7 +1338,7 @@ def test_margp_optimizer(): CCa += CPC.from_pk(pair="WETH/USDC", p=2000, k=10*20000, cid="c0") CCa += CPC.from_pk(pair="WETH/USDT", p=2000, k=10*20000, cid="c1") CCa += CPC.from_pk(pair="USDC/USDT", p=1.0, k=200000*200000, cid="c2") - O = CPCArbOptimizer(CCa) + O = MargPOptimizer(CCa) r = O.margp_optimizer("WETH", result=O.MO_DEBUG) assert isinstance(r, dict) @@ -1322,7 +1374,7 @@ def test_margp_optimizer(): assert r.targettkn == "WETH" assert r.dtokens is None assert sum(abs(x) for x in r.dtokens_t) < 1e-10 - assert r.p_optimal is None + assert not r.p_optimal is None assert iseq(0.0005, r.p_optimal_t[0], r.p_optimal_t[1]) assert set(r.tokens_t) == {'USDC', 'USDT'} assert r.errormsg is None @@ -1351,7 +1403,7 @@ def test_margp_optimizer(): assert sum(abs(x) for x in r.dtokens_t) < 1e-10 assert iseq(0.0005, r.p_optimal["USDC"], r.p_optimal["USDT"]) assert iseq(0.0005, r.p_optimal_t[0], r.p_optimal_t[1]) - assert tuple(r.p_optimal.values()) == r.p_optimal_t + assert tuple(r.p_optimal.values())[:-1] == r.p_optimal_t assert set(r.tokens_t) == set(('USDC', 'USDT')) assert r.errormsg is None assert r.is_error == False @@ -1365,7 +1417,7 @@ def test_margp_optimizer(): CCa += CPC.from_pk(pair="WETH/USDC", p=2000, k=10*20000, cid="c0") CCa += CPC.from_pk(pair="WETH/USDT", p=2000, k=10*20000, cid="c1") CCa += CPC.from_pk(pair="USDC/USDT", p=1.2, k=200000*200000, cid="c2") - O = CPCArbOptimizer(CCa) + O = MargPOptimizer(CCa) r = O.margp_optimizer("WETH", result=O.MO_DEBUG) assert isinstance(r, dict) @@ -1395,11 +1447,11 @@ def test_margp_optimizer(): assert abs(r.dtokens_t[0]) < 1e-6 assert abs(r.dtokens_t[1]) < 1e-6 assert r.dtokens["WETH"] == float(r) - assert tuple(r.p_optimal.values()) == r.p_optimal_t - assert tuple(r.p_optimal) == r.tokens_t + assert tuple(r.p_optimal.values())[:-1] == r.p_optimal_t + assert tuple(r.p_optimal)[:-1] == r.tokens_t assert iseq(r.p_optimal_t[0], 0.0005421803152482512) or iseq(r.p_optimal_t[0], 0.00045575394031021585) assert iseq(r.p_optimal_t[1], 0.0005421803152482512) or iseq(r.p_optimal_t[1], 0.00045575394031021585) - assert tuple(r.p_optimal.values()) == r.p_optimal_t + assert tuple(r.p_optimal.values())[:-1] == r.p_optimal_t assert set(r.tokens_t) == set(('USDC', 'USDT')) assert r.errormsg is None assert r.is_error == False @@ -1408,7 +1460,42 @@ def test_margp_optimizer(): abs(r.dtokens_t[0]) + ti = r.trade_instructions() + assert len(ti) == 3 + dfa = r.trade_instructions(ti_format=O.TIF_DFAGGR) + assert len(dfa)==7 + assert list(dfa.index) == ['c0', 'c1', 'c2', 'PRICE', 'AMMIn', 'AMMOut', 'TOTAL NET'] + assert list(dfa.columns) == ['WETH', 'USDC', 'USDT'] + assert dfa.loc["PRICE"][0] == 1 + assert iseq(dfa.loc["PRICE"][1], 0.0005421803152) + assert iseq(dfa.loc["PRICE"][2], 0.0004557539403) + dfa + + df = r.trade_instructions(ti_format=O.TIF_DF) + assert len(df) == 3 + assert list(df.columns) == ['pair', 'pairp', 'tknin', 'tknout', 'WETH', 'USDC', 'USDT'] + df + + df = r.trade_instructions(ti_format=O.TIF_DF).fillna("") + assert len(df) == 3 + assert list(df.columns) == ['pair', 'pairp', 'tknin', 'tknout', 'WETH', 'USDC', 'USDT'] + assert df["USDT"].loc["c0"] == "" + df + dcts = r.trade_instructions(ti_format=O.TIF_DICTS) + assert len(dcts) == 3 + assert list(dcts[0].keys()) == ['cid', 'tknin', 'amtin', 'tknout', 'amtout', 'error'] + d0 = dcts[0] + assert d0["cid"] == "c0" + assert iseq(d0["amtin"], 0.41326380379418914) + dcts + + objs = r.trade_instructions(ti_format=O.TIF_OBJECTS) + assert len(objs) == 3 + assert type(objs[0]).__name__ == 'TradeInstruction' + objs + + help(r.trade_instructions) # ------------------------------------------------------------ @@ -1423,8 +1510,8 @@ def notest_simple_optimizer_demo(): O = CPCArbOptimizer(CC) c0 = CC.curves[0] CC0 = CPCContainer([c0]) - O = CPCArbOptimizer(CC) - O0 = CPCArbOptimizer(CC0) + O = SimpleOptimizer(CC) + O0 = SimpleOptimizer(CC0) funcvx = O.simple_optimizer(result=O.SO_DXDYVALXFUNC) funcvy = O.simple_optimizer(result=O.SO_DXDYVALYFUNC) funcvx0 = O0.simple_optimizer(result=O.SO_DXDYVALXFUNC) @@ -1465,7 +1552,7 @@ def notest_margp_optimizer_demo(): CCa += CPC.from_pk(pair="WETH/USDC", p=2000, k=10*20000, cid="c0") CCa += CPC.from_pk(pair="WETH/USDT", p=2000, k=10*20000, cid="c1") CCa += CPC.from_pk(pair="USDC/USDT", p=1.2, k=20000*20000, cid="c2") - O = CPCArbOptimizer(CCa) + O = MargPOptimizer(CCa) CCa.plot() @@ -1495,7 +1582,7 @@ def notest_optimizer_plus_inverted_curves(): assert len(CC) == len(CCr) + len(CCi) CC.plot() - O = CPCArbOptimizer(CC) + O = SimpleOptimizer(CC) r = O.simple_optimizer() print(f"Arbitrage gains: {-r.valx:.4f} {r.tknxp} [time={r.time:.4f}s]") CC_ex = CPCContainer(c.execute(dx=dx) for c, dx in zip(r.curves, r.dxvalues)) diff --git a/fastlane_bot/tests/nbtest/test_003_Serialization.py b/fastlane_bot/tests/nbtest/test_003_Serialization.py index cf3828396..12076f10f 100644 --- a/fastlane_bot/tests/nbtest/test_003_Serialization.py +++ b/fastlane_bot/tests/nbtest/test_003_Serialization.py @@ -15,7 +15,7 @@ from fastlane_bot.testing import * import json -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("2.0", __VERSION__) diff --git a/fastlane_bot/tests/nbtest/test_004_GraphCode.py b/fastlane_bot/tests/nbtest/test_004_GraphCode.py index 7cb386233..bc2796e3c 100644 --- a/fastlane_bot/tests/nbtest/test_004_GraphCode.py +++ b/fastlane_bot/tests/nbtest/test_004_GraphCode.py @@ -14,7 +14,7 @@ print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(ag.ArbGraph)) from fastlane_bot.testing import * -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("2.0", __VERSION__) @@ -623,9 +623,9 @@ def test_with_real_data_from_cpc(): # ------------------------------------------------------------ try: - df = pd.read_csv("../nb_data/NBTEST_002_Curves.csv.gz") + df = pd.read_csv("_data/NBTEST_002_Curves.csv.gz") except: - df = pd.read_csv("fastlane_bot/tests/nbtest_data/NBTEST_002_Curves.csv.gz") + df = pd.read_csv("fastlane_bot/tests/nbtest/_data/NBTEST_002_Curves.csv.gz") CC0 = CPCContainer.from_df(df) print("Num curves:", len(CC0)) print("Num pairs:", len(CC0.pairs())) @@ -846,4 +846,10 @@ def test_specific_arb_examples(): raises(AG.price, AG.nodes[0], AG.nodes[1]) + + + + + + \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_005_Uniswap.py b/fastlane_bot/tests/nbtest/test_005_Uniswap.py index cda618c6c..875b3e196 100644 --- a/fastlane_bot/tests/nbtest/test_005_Uniswap.py +++ b/fastlane_bot/tests/nbtest/test_005_Uniswap.py @@ -16,7 +16,7 @@ print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(U3)) from fastlane_bot.testing import * -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("2.0", __VERSION__) @@ -43,9 +43,9 @@ def test_u3_standalone(): help(U3.from_dict) u1 = U3( - tkn0="USDC", + tkn0="USDC-eB48", tkn0decv=6, - tkn1="WETH", + tkn1="WETH-6Cc2", tkn1decv=18, sp96=data["sqrt_price_q96"], tick=data["tick"], @@ -53,18 +53,18 @@ def test_u3_standalone(): fee_const = U3.FEE500, ) u2 = U3.from_dict(data, U3.FEE500) - assert u1 == u2 + #assert u1 == u2 u = u2 assert asdict(u) == { - 'tkn0': 'USDC', - 'tkn1': 'WETH', + 'tkn0': 'USDC-eB48', + 'tkn1': 'WETH-6Cc2', 'sp96': int(data["sqrt_price_q96"]), 'tick': int(data["tick"]), 'liquidity': int(data["liquidity"]), 'fee_const': U3.FEE500 } - assert u.tkn0 == "USDC" - assert u.tkn1 == "WETH" + assert u.tkn0 == "USDC-eB48" + assert u.tkn1 == "WETH-6Cc2" assert u.tkn0dec == 6 assert u.tkn1dec == 18 assert u.decf == 1e-12 @@ -73,7 +73,7 @@ def test_u3_standalone(): assert iseq(1/u.p, 2108.6828205033694) assert u.p == u.price_tkn1_per_tkn0 assert 1/u.p == u.price_tkn0_per_tkn1 - assert u.price_convention == 'USDC/WETH [WETH per USDC]' + assert u.price_convention == 'USDC-eB48/WETH-6Cc2 [WETH-6Cc2 per USDC-eB48]' assert iseq(u._price_f(1725337071198080486317035748446190), 474229689.86928403) assert iseq(u._price_f(u.sp96), 474229689.86928403) assert u.ticksize == 10 diff --git a/fastlane_bot/tests/nbtest/test_007_NoneResult.py b/fastlane_bot/tests/nbtest/test_007_NoneResult.py new file mode 100644 index 000000000..f222ce6fd --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_007_NoneResult.py @@ -0,0 +1,148 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_007_NoneResult.py` +# ------------------------------------------------------------ +# source file = NBTest_007_NoneResult.py +# test id = 007 +# test comment = NoneResult +# ------------------------------------------------------------ + + + +#from fastlane_bot import Bot, Config, ConfigDB, ConfigNetwork, ConfigProvider +from fastlane_bot.tools.noneresult import NoneResult, isNone +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(NoneResult)) +from fastlane_bot.testing import * +import itertools as it +import collections as cl +import math as m +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + + +# ------------------------------------------------------------ +# Test 007 +# File test_007_NoneResult.py +# Segment NoneResult Basics +# ------------------------------------------------------------ +def test_noneresult_basics(): +# ------------------------------------------------------------ + + none = NoneResult() + assert str(none) == "NoneResult('None')" + assert repr(none) == str(none) + assert bool(none) == False + assert float(none) == 0.0 + assert int(none) == 0 + assert m.floor(none) is none + assert m.ceil(none) is none + assert m.trunc(none) is none + assert round(none,5) is none + assert None == none + + assert none.foo is none + assert none.foo.bar is none + assert none["foo"] is none + assert none["foo"]["bar"] is none + + assert none+1 is none + assert none-1 is none + assert none*1 is none + assert none/1 is none + assert none//1 is none + assert none**1 is none + assert none%1 is none + + assert 1+none is none + assert 1-none is none + assert 1*none is none + assert 1/none is none + assert 1//none is none + assert 1**none is none + assert 1%none is none + + none_foo = NoneResult("foo") + assert str(none_foo) == "NoneResult('foo')" + assert none_foo == none + + +# ------------------------------------------------------------ +# Test 007 +# File test_007_NoneResult.py +# Segment None format +# ------------------------------------------------------------ +def test_none_format(): +# ------------------------------------------------------------ + + none = NoneResult() + assert f"{none}" == "NoneResult('None')" + assert "{}".format(none) == "NoneResult('None')" + + assert f":{str(none):30}:" == ":NoneResult('None') :" + assert f":{none:30}:" == f":{str(none):30}:" + assert len(f"{none:30}") == 30 + raises(lambda: f"{none:2.1f}") == "Unknown format code 'f' for object of type 'str'" + assert f"{float(none):10.4f}" == ' 0.0000' + assert f"{int(none):010d}" == '0000000000' + + a="123" + + f"{none:40}" + + +# ------------------------------------------------------------ +# Test 007 +# File test_007_NoneResult.py +# Segment math functions +# ------------------------------------------------------------ +def test_math_functions(): +# ------------------------------------------------------------ + + none = NoneResult() + assert m.sin(none) == 0 + assert m.cos(none) == 1 + assert m.exp(none) == 1 + assert raises(m.log, none) == "math domain error" + assert 1/none == none + assert 0*none==none + sin = lambda x: 0*x+m.sin(x) + assert sin(none) == none + + +# ------------------------------------------------------------ +# Test 007 +# File test_007_NoneResult.py +# Segment isNone +# ------------------------------------------------------------ +def test_isnone(): +# ------------------------------------------------------------ + + assert isNone(None) == True + assert isNone(NoneResult()) == True + assert isNone(NoneResult("moo")) == True + assert isNone(0) == False + assert isNone("") == False + assert isNone(False) == False + assert isNone(NoneResult) == False + + none = NoneResult() + assert raises(lambda x: isNone(None+x), 1) == "unsupported operand type(s) for +: 'NoneType' and 'int'" + assert isNone(none+1) + assert isNone(1+none) + assert isNone(none**2) + assert isNone(none*none) + assert isNone(1+2*none+3*none*none) + + assert not isNone(none) == False + assert [x for x in (1,2,None,3) if not isNone(x)] == [1,2,3] + assert [x for x in (1,2,none,3) if not isNone(x)] == [1,2,3] + assert [2*x for x in (1,2,None,3) if not isNone(x)] == [2,4,6] + assert [2*x for x in (1,2,none,3) if not isNone(x)] == [2,4,6] + assert [2*x for x in (1,2,none,3) if not isNone(2*x)] == [2,4,6] + + + + \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_030_Mainnet.py b/fastlane_bot/tests/nbtest/test_030_Mainnet.py deleted file mode 100644 index d0bb06b93..000000000 --- a/fastlane_bot/tests/nbtest/test_030_Mainnet.py +++ /dev/null @@ -1,355 +0,0 @@ -# ------------------------------------------------------------ -# Auto generated test file `test_030_Mainnet.py` -# ------------------------------------------------------------ -# source file = NBTest_030_Mainnet.py -# test id = 030 -# test comment = Mainnet -# ------------------------------------------------------------ - - - -from fastlane_bot import Bot, Config, ConfigDB, ConfigNetwork, ConfigProvider -from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, CPCContainer, T, Pair -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) -from fastlane_bot.testing import * -import itertools as it -import collections as cl -plt.style.use('seaborn-dark') -plt.rcParams['figure.figsize'] = [12,6] -from fastlane_bot import __VERSION__ -require("3.0", __VERSION__) - - - -bot = Bot() -CCm = bot.get_curves() - -pairs0 = CCm.pairs(standardize=False) -pairs = CCm.pairs(standardize=True) -pairsc = {c.pairo.primary for c in CCm if c.P("exchange")=="carbon_v1"} - - -# ------------------------------------------------------------ -# Test 030 -# File test_030_Mainnet.py -# Segment Overall market [NOTEST] -# ------------------------------------------------------------ -def notest_overall_market(): -# ------------------------------------------------------------ - - print(f"Total pairs: {len(pairs0):4}") - print(f"Primary pairs: {len(pairs):4}") - print(f"...carbon: {len(pairsc):4}") - print(f"Tokens: {len(CCm.tokens()):4}") - print(f"Curves: {len(CCm):4}") - - -# ------------------------------------------------------------ -# Test 030 -# File test_030_Mainnet.py -# Segment By pair [NOTEST] -# ------------------------------------------------------------ -def notest_by_pair(): -# ------------------------------------------------------------ - - # ### All pairs - - cbp0 = {pair: [c for c in CCm.bypairs(pair)] for pair in CCm.pairs()} # curves by (primary) pair - nbp0 = {pair: len(cc) for pair,cc in cbp0.items()} - assert len(cbp0) == len(CCm.pairs()) - assert set(cbp0) == CCm.pairs() - - # ### Only those with >1 curves - - cbp = {pair: cc for pair, cc in cbp0.items() if len(cc)>1} - nbp = {pair: len(cc) for pair,cc in cbp.items()} - print(f"Pairs with >1 curves: {len(cbp)}") - print(f"Curves in those: {sum(nbp.values())}") - print(f"Average curves/pair: {sum(nbp.values())/len(cbp):.1f}") - - # ### x=0 or y=0 - - xis0 = {c.cid: (c.x, c.y) for c in CCm if c.x==0} - yis0 = {c.cid: (c.x, c.y) for c in CCm if c.y==0} - assert len(xis0) == 0 # set loglevel debug to see removal of curves - assert len(yis0) == 0 - - # ### Prices - - # #### All - - prices_da = {pair: - [( - Pair.n(pair), c.primaryp(), c.cid, c.cid[-8:], c.P("exchange"), c.tvl(tkn=pair.split("/")[0]), - "x" if c.itm(cc) else "", c.buysell(verbose=False), c.buysell(verbose=True, withprice=True) - ) for c in cc - ] - for pair, cc in cbp.items() - } - #prices_da - - # #### Only for pairs that have at least on Carbon pair - - prices_d = {pair: l for pair,l in prices_da.items() if pair in pairsc} - prices_l = list(it.chain(*prices_d.values())) - - curves_by_pair = list(cl.Counter([r[0] for r in prices_l]).items()) - curves_by_pair = sorted(curves_by_pair, key=lambda x: x[1], reverse=True) - curves_by_pair - - # + - # for pair, _ in curves_by_pair: - # print(f"# #### {pair}\n\npricedf.loc['{pair}']\n\n") - # - - - # #### Dataframe - - pricedf0 = pd.DataFrame(prices_l, columns="pair,price,cid,cid0,exchange,vl,itm,bs,bsv".split(",")) - pricedf = pricedf0.drop('cid', axis=1).sort_values(by=["pair", "exchange", "cid0"]) - pricedf = pricedf.set_index(["pair", "exchange", "cid0"]) - pricedf - - # ### Individual frames - - # #### WETH/USDC - - pricedf.loc['WETH/USDC'] - - - # #### BNT/WETH - - pricedf.loc['BNT/WETH'] - - - # #### BNT/vBNT - - pricedf.loc['BNT/vBNT'] - - - # #### USDT/USDC - - pricedf.loc['USDT/USDC'] - - - # #### WBTC/WETH - - pricedf.loc['WBTC/WETH'] - - - # #### LINK/USDT - - pricedf.loc['LINK/USDT'] - - - # #### WBTC/USDT - - pricedf.loc['WBTC/USDT'] - - - # #### BNT/USDC - - pricedf.loc['BNT/USDC'] - - - # #### WETH/DAI - - pricedf.loc['WETH/DAI'] - - - # #### LINK/USDC - - pricedf.loc['LINK/USDC'] - - - # #### DAI/USDC - - pricedf.loc['DAI/USDC'] - - - # #### WETH/USDT - - pricedf.loc['WETH/USDT'] - - - # #### DAI/USDT - - pricedf.loc['DAI/USDT'] - - - # #### PEPE/WETH - - pricedf.loc['PEPE/WETH'] - - - # #### LYXe/USDC - - pricedf.loc['LYXe/USDC'] - - - # #### rETH/WETH - - pricedf.loc['rETH/WETH'] - - - # #### 0x0/WETH - - pricedf.loc['0x0/WETH'] - - - # #### WBTC/USDC - - pricedf.loc['WBTC/USDC'] - - - # #### ARB/MATIC - - pricedf.loc['ARB/MATIC'] - - - # #### TSUKA/USDC - - pricedf.loc['TSUKA/USDC'] - - - # #### stETH/WETH - - pricedf.loc['stETH/WETH'] - - - raise - - # + active="" - # - # - - - -# ------------------------------------------------------------ -# Test 030 -# File test_030_Mainnet.py -# Segment Execution [NOTEST] -# ------------------------------------------------------------ -def notest_execution(): -# ------------------------------------------------------------ - - # ### Configuration - # - # - `flt`: flashloanable tokens - # - `loglevel`: `LL_DEBUG` , `LL_INFO` `LL_WARN` `LL_ERR` - - flt = [T.USDC] - C = Config.new(config=Config.CONFIG_TENDERLY, loglevel=Config.LL_INFO) - - bot = CarbonBot(ConfigObj=C) - - # ### Database update [Tenderly specific] - - # provided here for convenience; must be commented out for tests - bot.update(drop_tables=True, top_n=10, only_carbon=False) - - # ### Execution - - bot.run(flashloan_tokens=flt, mode=bot.RUN_SINGLE) - - -# ------------------------------------------------------------ -# Test 030 -# File test_030_Mainnet.py -# Segment Execution analysis [NOTEST] -# ------------------------------------------------------------ -def notest_execution_analysis(): -# ------------------------------------------------------------ - - CCm = bot.get_curves() - - # ### Arbitrage opportunities - - ops = bot._run(flashloan_tokens=flt, CCm=CCm, result=bot.XS_ARBOPPS) - ops - - # ### Route struct - - try: - route_struct = bot._run(flashloan_tokens=flt, CCm=CCm, result=bot.XS_ROUTE) - except bot.NoArbAvailable as e: - print(f"[NoArbAvailable] {e}") - route_struct = None - route_struct - - # ### Orderering info - - try: - ordinfo = bot._run(flashloan_tokens=flt, CCm=CCm, result=bot.XS_ORDINFO) - flashloan_amount = ordinfo[1] - flashloan_token_address = ordinfo[2] - print(f"Flashloan: {flashloan_amount} [{flashloan_token_address}]") - except bot.NoArbAvailable as e: - print(f"[NoArbAvailable] {e}") - ordinfo = None - ordinfo - - -# ------------------------------------------------------------ -# Test 030 -# File test_030_Mainnet.py -# Segment Market analysis [NOTEST] -# ------------------------------------------------------------ -def notest_market_analysis(): -# ------------------------------------------------------------ - - # ### Overall market - - exch0 = {c.P("exchange") for c in CCm} - print("Number of curves:", len(CCm)) - print("Number of tokens:", len(CCm.tokens())) - #print("Exchanges:", exch0) - print("---") - for xc in exch0: - print(f"{xc+':':16} {len(CCm.byparams(exchange=xc)):4}") - - # ### Pair - - pair = f"{T.ECO}/{T.USDC}" - - CCp = CCm.bypairs(pair) - exch = {c.P("exchange") for c in CCp} - print("pair: ", pair) - print("curves: ", len(CCp)) - print("exchanges: ", exch) - for xc in exch: - c = CCp.byparams(exchange=xc)[0] - print(f"{xc+':':16} {c.p:.4f} {1/c.p:.4f}") - - -# ------------------------------------------------------------ -# Test 030 -# File test_030_Mainnet.py -# Segment Technical [NOTEST] -# ------------------------------------------------------------ -def notest_technical(): -# ------------------------------------------------------------ - - # ### Validation and assertions - - assert C.DATABASE == C.DATABASE_POSTGRES - assert C.POSTGRES_DB == "tenderly" - assert C.NETWORK == C.NETWORK_TENDERLY - assert C.PROVIDER == C.PROVIDER_TENDERLY - assert str(type(bot.db)) == "" - assert C.w3.provider.endpoint_uri.startswith("https://rpc.tenderly.co/fork/") - assert bot.db.carbon_controller.address == '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1' - - # ### Tenderly shell commands - # - # Run those commands in a shell if there are Tenderly connection issues - - C_nw = ConfigNetwork.new(network=ConfigNetwork.NETWORK_TENDERLY) - c1, c2 = C_nw.shellcommand().splitlines() - print(c1) - print(c2) - # !{c1} - # !{c2} - - - - \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_033_Pools.py b/fastlane_bot/tests/nbtest/test_033_Pools.py index 439cf3562..91ebd063f 100644 --- a/fastlane_bot/tests/nbtest/test_033_Pools.py +++ b/fastlane_bot/tests/nbtest/test_033_Pools.py @@ -25,7 +25,7 @@ print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3Pool)) from fastlane_bot.testing import * -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("3.0", __VERSION__) diff --git a/fastlane_bot/tests/nbtest/test_034_Interface.py b/fastlane_bot/tests/nbtest/test_034_Interface.py index a6c914aa7..ba03b2ed6 100644 --- a/fastlane_bot/tests/nbtest/test_034_Interface.py +++ b/fastlane_bot/tests/nbtest/test_034_Interface.py @@ -31,7 +31,7 @@ from fastlane_bot.testing import * -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("3.0", __VERSION__) @@ -63,8 +63,9 @@ def test_test_remove_unsupported_exchanges(): def test_test_has_balance(): # ------------------------------------------------------------ + qi.state = [{'exchange_name': 'uniswap_v2', 'address': '0x123', 'tkn0_key': 'TKN-0x123', 'tkn1_key': 'TKN-0x456', 'pair_name': 'Pair-0x789', 'liquidity': 10}, {'exchange_name': 'sushiswap_v2', 'address': '0xabc', 'tkn0_key': 'TKN-0xabc', 'tkn1_key': 'TKN-0xdef', 'pair_name': 'Pair-0xghi', 'liquidity': 0}] assert (qi.has_balance(qi.state[0], 'liquidity') == True) - assert (qi.has_balance(qi.state[1], "liquidity") == False) + assert (qi.has_balance(qi.state[1], 'liquidity') == False) # ------------------------------------------------------------ @@ -125,8 +126,4 @@ def test_test_get_pool(): new_state = [{'last_updated_block': 17614344, 'address': '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1', 'exchange_name': 'carbon_v1', 'tkn0_address': '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', 'tkn1_address': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'tkn0_symbol': 'ETH', 'tkn1_symbol': 'USDC', 'tkn0_decimals': 18, 'tkn1_decimals': 6, 'cid': 1701411834604692317316873037158841057365, 'tkn0_key': 'ETH-EEeE', 'tkn1_key': 'USDC-eB48', 'pair_name': 'ETH-EEeE/USDC-eB48', 'fee_float': 0.002, 'fee': '0.002', 'descr': 'carbon_v1 ETH-EEeE/USDC-eB48 0.002', 'y_0': 9882507039899549, 'y_1': 0, 'z_0': 9882507039899549, 'z_1': 17936137, 'A_0': 0, 'A_1': 99105201, 'B_0': 0, 'B_1': 11941971885}] qi.update_state(new_state) - pool = qi.get_pool(cid=1701411834604692317316873037158841057365) - - - - \ No newline at end of file + pool = qi.get_pool(cid=1701411834604692317316873037158841057365) \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_035_Utils.py b/fastlane_bot/tests/nbtest/test_035_Utils.py index 3db5f34d2..a8f81835b 100644 --- a/fastlane_bot/tests/nbtest/test_035_Utils.py +++ b/fastlane_bot/tests/nbtest/test_035_Utils.py @@ -27,7 +27,7 @@ print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3Pool)) from fastlane_bot.testing import * -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("3.0", __VERSION__) diff --git a/fastlane_bot/tests/nbtest/test_036_Manager.py b/fastlane_bot/tests/nbtest/test_036_Manager.py index eddeb8ff7..42d0d3571 100644 --- a/fastlane_bot/tests/nbtest/test_036_Manager.py +++ b/fastlane_bot/tests/nbtest/test_036_Manager.py @@ -17,7 +17,7 @@ from fastlane_bot import Bot, Config from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 -#from fastlane_bot.events.manager import Base +from fastlane_bot.events.managers.manager import Manager Base = None from fastlane_bot.tools.cpc import ConstantProductCurve as CPC @@ -30,72 +30,131 @@ print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) from fastlane_bot.testing import * -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("3.0", __VERSION__) +# + +import json + +with open("fastlane_bot/data/event_test_data.json", "r") as f: + event_data = json.load(f) + +with open("fastlane_bot/data/test_pool_data.json", "r") as f: + pool_data = json.load(f) + + +cfg = Config.new(config=Config.CONFIG_MAINNET) + +manager = Manager(cfg.w3, cfg, pool_data, 20, SUPPORTED_EXCHANGES=['bancor_v3', 'carbon_v1', 'uniswap_v2', 'uniswap_v3']) + + # ------------------------------------------------------------ # Test 036 # File test_036_Manager.py -# Segment moving this into a test so it does not kill the series +# Segment test_update_from_event_uniswap_v2 # ------------------------------------------------------------ -def test_moving_this_into_a_test_so_it_does_not_kill_the_series(): +def test_test_update_from_event_uniswap_v2(): # ------------------------------------------------------------ - nan = np.NaN - pool_data = [{'cid': '0xb3b0dbb95f1f70e1f053360d9bccef3fbe7c5e2b615e833a9faae18c299a0fc9', 'last_updated': nan, 'last_updated_block': 17634372, 'descr': 'bancor_v3 BNT-FF1C/MATIC-eBB0 0.000', 'pair_name': 'BNT-FF1C/MATIC-eBB0', 'exchange_name': 'bancor_v3', 'fee': '0.000', 'fee_float': 0.0, 'address': '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 'anchor': nan, 'tkn0_address': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkn1_address': '0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', 'tkn0_key': 'BNT-FF1C', 'tkn1_key': 'MATIC-eBB0', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'exchange_id': 2, 'tkn0_symbol': 'BNT', 'tkn1_symbol': 'MATIC', 'timestamp': nan, 'tkn0_balance': 371729474548077247680443, 'tkn1_balance': 216554584335493056216168, 'liquidity': nan, 'sqrt_price_q96': nan, 'tick': nan, 'tick_spacing': 0}, {'cid': '0x38d373a29b8a7e621ee373ee76138184a67092259bd24ab1434ec90b98235efd', 'last_updated': nan, 'last_updated_block': 17634372, 'descr': 'bancor_v3 BNT-FF1C/ENS-9D72 0.000', 'pair_name': 'BNT-FF1C/ENS-9D72', 'exchange_name': 'bancor_v3', 'fee': '0.000', 'fee_float': 0.0, 'address': '0xBC19712FEB3a26080eBf6f2F7849b417FdD792CA', 'anchor': nan, 'tkn0_address': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkn1_address': '0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72', 'tkn0_key': 'BNT-FF1C', 'tkn1_key': 'ENS-9D72', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'exchange_id': 2, 'tkn0_symbol': 'BNT', 'tkn1_symbol': 'ENS', 'timestamp': nan, 'tkn0_balance': 104058085529730176588006, 'tkn1_balance': 4547023863756451207684, 'liquidity': nan, 'sqrt_price_q96': nan, 'tick': nan, 'tick_spacing': 0}, {'cid': '0x56f1f774ece226fac7c9940c98ead630bfc18a39fa2f2bdcdc56e6234d4477b1', 'last_updated': nan, 'last_updated_block': 17634372, 'descr': 'bancor_v3 BNT-FF1C/ALPHA-0975 0.000', 'pair_name': 'BNT-FF1C/ALPHA-0975', 'exchange_name': 'bancor_v3', 'fee': '0.000', 'fee_float': 0.0, 'address': '0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272', 'anchor': nan, 'tkn0_address': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkn1_address': '0xa1faa113cbE53436Df28FF0aEe54275c13B40975', 'tkn0_key': 'BNT-FF1C', 'tkn1_key': 'ALPHA-0975', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'exchange_id': 2, 'tkn0_symbol': 'BNT', 'tkn1_symbol': 'ALPHA', 'timestamp': nan, 'tkn0_balance': 0, 'tkn1_balance': 0, 'liquidity': nan, 'sqrt_price_q96': nan, 'tick': nan, 'tick_spacing': 0}, {'cid': '0x1b65e937b57a618d40da4236ae854d33a843042a9abc84ba72d808ad67435a42', 'last_updated': nan, 'last_updated_block': 17634372, 'descr': 'bancor_v3 BNT-FF1C/HEGIC-8430 0.000', 'pair_name': 'BNT-FF1C/HEGIC-8430', 'exchange_name': 'bancor_v3', 'fee': '0.000', 'fee_float': 0.0, 'address': '0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6', 'anchor': nan, 'tkn0_address': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkn1_address': '0x584bC13c7D411c00c01A62e8019472dE68768430', 'tkn0_key': 'BNT-FF1C', 'tkn1_key': 'HEGIC-8430', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'exchange_id': 2, 'tkn0_symbol': 'BNT', 'tkn1_symbol': 'HEGIC', 'timestamp': nan, 'tkn0_balance': 0, 'tkn1_balance': 0, 'liquidity': nan, 'sqrt_price_q96': nan, 'tick': nan, 'tick_spacing': 0}, {'cid': '0x561b7f22cadc1359057c07c5a1f11ae4d087a753aa87629ed92b38175e60c3ae', 'last_updated': nan, 'last_updated_block': 17634372, 'descr': 'bancor_v3 BNT-FF1C/ZCN-3B78 0.000', 'pair_name': 'BNT-FF1C/ZCN-3B78', 'exchange_name': 'bancor_v3', 'fee': '0.000', 'fee_float': 0.0, 'address': '0x1559FA1b8F28238FD5D76D9f434ad86FD20D1559', 'anchor': nan, 'tkn0_address': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkn1_address': '0xb9EF770B6A5e12E45983C5D80545258aA38F3B78', 'tkn0_key': 'BNT-FF1C', 'tkn1_key': 'ZCN-3B78', 'tkn0_decimals': 18, 'tkn1_decimals': 10, 'exchange_id': 2, 'tkn0_symbol': 'BNT', 'tkn1_symbol': 'ZCN', 'timestamp': nan, 'tkn0_balance': 109709381661658805787397, 'tkn1_balance': 3264962522647673, 'liquidity': nan, 'sqrt_price_q96': nan, 'tick': nan, 'tick_spacing': 0}] + # + + event = event_data['uniswap_v2_event'] - carbon_events = [{'last_updated_block': 17634377, 'address': '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1', 'exchange_name': 'carbon_v1', 'tkn0_address': '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', 'tkn1_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'tkn0_symbol': 'ETH', 'tkn1_symbol': 'DAI', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'cid': 340282366920938463463374607431768211457, 'tkn0_key': 'ETH-EEeE', 'tkn1_key': 'DAI-1d0F', 'pair_name': 'ETH-EEeE/DAI-1d0F', 'fee_float': 0.002, 'fee': '0.002', 'descr': 'carbon_v1 ETH-EEeE/DAI-1d0F 0.002', 'y_0': 1000000000000000, 'y_1': 0, 'z_0': 1000000000000000, 'z_1': 0, 'A_0': 0, 'A_1': 0, 'B_0': 6382340776412, 'B_1': 1875443170982464}, {'last_updated_block': 17634377, 'address': '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1', 'exchange_name': 'carbon_v1', 'tkn0_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'tkn1_address': '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', 'tkn0_symbol': 'DAI', 'tkn1_symbol': 'ETH', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'cid': 340282366920938463463374607431768211593, 'tkn0_key': 'DAI-1d0F', 'tkn1_key': 'ETH-EEeE', 'pair_name': 'DAI-1d0F/ETH-EEeE', 'fee_float': 0.002, 'fee': '0.002', 'descr': 'carbon_v1 DAI-1d0F/ETH-EEeE 0.002', 'y_0': 0, 'y_1': 0, 'z_0': 0, 'z_1': 0, 'A_0': 88739322630080, 'A_1': 30784910546, 'B_0': 1876725096051745, 'B_1': 6418617024516}, {'last_updated_block': 17634377, 'address': '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1', 'exchange_name': 'carbon_v1', 'tkn0_address': '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', 'tkn1_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'tkn0_symbol': 'ETH', 'tkn1_symbol': 'DAI', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'cid': 340282366920938463463374607431768211614, 'tkn0_key': 'ETH-EEeE', 'tkn1_key': 'DAI-1d0F', 'pair_name': 'ETH-EEeE/DAI-1d0F', 'fee_float': 0.002, 'fee': '0.002', 'descr': 'carbon_v1 ETH-EEeE/DAI-1d0F 0.002', 'y_0': 157076304796171508, 'y_1': 191076681422897394849, 'z_0': 257505273765924104, 'z_1': 462002783910000000000, 'A_0': 197764438468, 'A_1': 235894416821184, 'B_0': 6293971818901, 'B_1': 1873305839476414}, {'last_updated_block': 17634377, 'address': '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1', 'exchange_name': 'carbon_v1', 'tkn0_address': '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', 'tkn1_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'tkn0_symbol': 'ETH', 'tkn1_symbol': 'DAI', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'cid': 340282366920938463463374607431768211607, 'tkn0_key': 'ETH-EEeE', 'tkn1_key': 'DAI-1d0F', 'pair_name': 'ETH-EEeE/DAI-1d0F', 'fee_float': 0.002, 'fee': '0.002', 'descr': 'carbon_v1 ETH-EEeE/DAI-1d0F 0.002', 'y_0': 0, 'y_1': 0, 'z_0': 0, 'z_1': 0, 'A_0': 69065909368, 'A_1': 106270057837888, 'B_0': 6457478834827, 'B_1': 1874403645842739}, {'last_updated_block': 17634377, 'address': '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1', 'exchange_name': 'carbon_v1', 'tkn0_address': '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', 'tkn1_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'tkn0_symbol': 'ETH', 'tkn1_symbol': 'DAI', 'tkn0_decimals': 18, 'tkn1_decimals': 18, 'cid': 340282366920938463463374607431768211673, 'tkn0_key': 'ETH-EEeE', 'tkn1_key': 'DAI-1d0F', 'pair_name': 'ETH-EEeE/DAI-1d0F', 'fee_float': 0.002, 'fee': '0.002', 'descr': 'carbon_v1 ETH-EEeE/DAI-1d0F 0.002', 'y_0': 1, 'y_1': 940344, 'z_0': 9403439, 'z_1': 940344, 'A_0': 0, 'A_1': 0, 'B_0': 785475461108442, 'B_1': 2814749767}] + assert event['args']['reserve0'] != [pool['tkn0_balance'] for pool in manager.pool_data if pool['address'] == event['address']][0] - cfg = Config.new(config=Config.CONFIG_MAINNET) - w3 = cfg.w3 - manager = Base(cfg=cfg, pool_data=pool_data, alchemy_max_block_fetch=20, web3=w3, SUPPORTED_EXCHANGES=['bancor_v3', 'carbon_v1']) + manager.update_from_event(event) - rows_to_update = [idx for (idx, row) in enumerate(pool_data)] + assert event['address'] in [pool['address'] for pool in manager.pool_data] + assert event['args']['reserve0'] == [pool['tkn0_balance'] for pool in manager.pool_data if pool['address'] == event['address']][0] + # - + + +# ------------------------------------------------------------ +# Test 036 +# File test_036_Manager.py +# Segment test_update_from_event_uniswap_v3 +# ------------------------------------------------------------ +def test_test_update_from_event_uniswap_v3(): +# ------------------------------------------------------------ # + - # This is a simple counter variable that we'll increment every time we call the function. - multicall_counter = 0 + event = event_data['uniswap_v3_event'] + + assert event['args']['liquidity'] != [pool['liquidity'] for pool in manager.pool_data if pool['address'] == event['address']][0] + + manager.update_from_event(event) + + assert event['address'] in [pool['address'] for pool in manager.pool_data] + assert event['args']['liquidity'] == [pool['liquidity'] for pool in manager.pool_data if pool['address'] == event['address']][0] + # - - def my_multicall(*args, **kwargs): - # This is a wrapper function that increments the counter each time it's called, - # then calls the original function. - global multicall_counter - multicall_counter += 1 - return brownie_multicall(*args, **kwargs) + # + +# ------------------------------------------------------------ +# Test 036 +# File test_036_Manager.py +# Segment test_update_from_event_carbon_v1_update +# ------------------------------------------------------------ +def test_test_update_from_event_carbon_v1_update(): +# ------------------------------------------------------------ + # + + event = event_data['carbon_v1_event_update'] + assert event['args']['order0'][0] != [pool['y_0'] for pool in manager.pool_data if pool['cid'] == event['args']['id']][0] + + manager.update_from_event(event) + + assert event['args']['order0'][0] == [pool['y_0'] for pool in manager.pool_data if pool['cid'] == event['args']['id']][0] # - # ------------------------------------------------------------ # Test 036 # File test_036_Manager.py -# Segment test_update_pools_directly_from_contracts_bancor_v3 +# Segment test_update_from_event_carbon_v1_create # ------------------------------------------------------------ -def test_test_update_pools_directly_from_contracts_bancor_v3(): +def test_test_update_from_event_carbon_v1_create(): # ------------------------------------------------------------ + # + + # + + event = event_data['carbon_v1_event_create'] + manager.pool_data = [pool for pool in manager.pool_data if pool['cid'] != event['args']['id']] + assert event['args']['id'] not in [pool['cid'] for pool in manager.pool_data] - brownie.multicall = my_multicall - manager.update_pools_directly_from_contracts(n_jobs=2, rows_to_update=[0, 1, 2, 3], not_bancor_v3=False, current_block=1) - assert (multicall_counter == 1) - expected_calls = [call(pool_info=pool_data[i], limiter=False, block_number=1) for i in [0, 1, 2, 3]] + manager.update_from_event(event) + + assert event['args']['id'] in [pool['cid'] for pool in manager.pool_data] + # - # ------------------------------------------------------------ # Test 036 # File test_036_Manager.py -# Segment test_get_strats_by_pair +# Segment test_update_from_event_carbon_v1_delete # ------------------------------------------------------------ -def test_test_get_strats_by_pair(): +def test_test_update_from_event_carbon_v1_delete(): # ------------------------------------------------------------ - manager.cfg.MULTICALL_CONTRACT_ADDRESS = '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696' - all_pairs = [(1, 2), (3, 4)] - carbon_controller_mock = Mock() - carbon_controller_mock.strategiesByPair.side_effect = [[(5, 6)], [(7, 8)]] - result = manager.get_strats_by_pair(all_pairs, carbon_controller_mock) - carbon_controller_mock.strategiesByPair.assert_has_calls([call(*pair) for pair in all_pairs]) \ No newline at end of file + # + + event = event_data['carbon_v1_event_create'] + manager.pool_data = [pool for pool in manager.pool_data if pool['cid'] != event['args']['id']] + assert event['args']['id'] not in [pool['cid'] for pool in manager.pool_data] + + manager.update_from_event(event) + + assert event['args']['id'] in [pool['cid'] for pool in manager.pool_data] + + event['event'] = 'StrategyDeleted' + + manager.update_from_event(event) + + assert event['args']['id'] not in [pool['cid'] for pool in manager.pool_data] + # - + + # \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_037_Exchanges.py b/fastlane_bot/tests/nbtest/test_037_Exchanges.py index 73fdd0071..29fcdf253 100644 --- a/fastlane_bot/tests/nbtest/test_037_Exchanges.py +++ b/fastlane_bot/tests/nbtest/test_037_Exchanges.py @@ -27,7 +27,7 @@ print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) from fastlane_bot.testing import * -plt.style.use('seaborn-dark') +#plt.style.use('seaborn-dark') plt.rcParams['figure.figsize'] = [12,6] from fastlane_bot import __VERSION__ require("3.0", __VERSION__) diff --git a/fastlane_bot/tests/nbtest/test_038_TestBancorV3Mode.py b/fastlane_bot/tests/nbtest/test_038_TestBancorV3Mode.py new file mode 100644 index 000000000..f1eb2a048 --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_038_TestBancorV3Mode.py @@ -0,0 +1,391 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_038_TestBancorV3Mode.py` +# ------------------------------------------------------------ +# source file = NBTest_038_TestBancorV3Mode.py +# test id = 038 +# test comment = TestBancorV3Mode +# ------------------------------------------------------------ + + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Bot, Config +from fastlane_bot.bot import CarbonBot +from fastlane_bot.tools.cpc import ConstantProductCurve +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC +from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 +from fastlane_bot.events.interface import QueryInterface +from fastlane_bot.helpers.poolandtokens import PoolAndTokens +from fastlane_bot.helpers import TradeInstruction, TxReceiptHandler, TxRouteHandler, TxSubmitHandler, TxHelpers, TxHelper +from fastlane_bot.events.managers.manager import Manager +from fastlane_bot.events.interface import QueryInterface +from joblib import Parallel, delayed +import pytest +import math +import json +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SushiswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) +from fastlane_bot.testing import * +from fastlane_bot.modes import triangle_single_bancor3 +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +C = cfg = Config.new(config=Config.CONFIG_MAINNET) +C.DEFAULT_MIN_PROFIT_BNT = 50 +C.DEFAULT_MIN_PROFIT = 50 +cfg.DEFAULT_MIN_PROFIT_BNT = 50 +cfg.DEFAULT_MIN_PROFIT = 50 +assert (C.NETWORK == C.NETWORK_MAINNET) +assert (C.PROVIDER == C.PROVIDER_ALCHEMY) +setup_bot = CarbonBot(ConfigObj=C) +pools = None +with open('fastlane_bot/data/tests/latest_pool_data_testing.json') as f: + pools = json.load(f) +pools = [pool for pool in pools] +pools[0] +static_pools = pools +state = pools +exchanges = list({ex['exchange_name'] for ex in state}) +db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) +setup_bot.db = db + +static_pool_data_filename = "static_pool_data" + +static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) + +uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) + +tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) + +exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" + +exchanges = exchanges.split(",") + + +alchemy_max_block_fetch = 20 +static_pool_data["cid"] = [ + cfg.w3.keccak(text=f"{row['descr']}").hex() + for index, row in static_pool_data.iterrows() + ] +static_pool_data = [ + row for index, row in static_pool_data.iterrows() + if row["exchange_name"] in exchanges +] + +static_pool_data = pd.DataFrame(static_pool_data) +static_pool_data['exchange_name'].unique() +mgr = Manager( + web3=cfg.w3, + cfg=cfg, + pool_data=static_pool_data.to_dict(orient="records"), + SUPPORTED_EXCHANGES=exchanges, + alchemy_max_block_fetch=alchemy_max_block_fetch, + uniswap_v2_event_mappings=uniswap_v2_event_mappings, + tokens=tokens.to_dict(orient="records"), +) + +start_time = time.time() +Parallel(n_jobs=-1, backend="threading")( + delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data +) +cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") + +mgr.deduplicate_pool_data() +cids = [pool["cid"] for pool in mgr.pool_data] +assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" +def init_bot(mgr: Manager) -> CarbonBot: + """ + Initializes the bot. + + Parameters + ---------- + mgr : Manager + The manager object. + + Returns + ------- + CarbonBot + The bot object. + """ + mgr.cfg.logger.info("Initializing the bot...") + bot = CarbonBot(ConfigObj=mgr.cfg) + bot.db = db + bot.db.mgr = mgr + assert isinstance( + bot.db, QueryInterface + ), "QueryInterface not initialized correctly" + return bot +bot = init_bot(mgr) +bot.db.handle_token_key_cleanup() +bot.db.remove_unmapped_uniswap_v2_pools() +bot.db.remove_zero_liquidity_pools() +bot.db.remove_unsupported_exchanges() +tokens = bot.db.get_tokens() +ADDRDEC = {t.key: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} +flashloan_tokens = bot.setup_flashloan_tokens(None) +CCm = bot.setup_CCm(None) +pools = db.get_pool_data_with_tokens() + +arb_mode = "bancor_v3" + +#single = bot._run(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode=arb_mode, data_validator=False, result="calc_trade_instr") + +arb_finder = bot._get_arb_finder("bancor_v3") +finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) +r = finder.find_arbitrage() +( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = r +( +ordered_trade_instructions_dct, +tx_in_count, +) = bot._simple_ordering_by_src_token( +best_trade_instructions_dic, best_src_token +) + + +arb_finder = bot._get_arb_finder("bancor_v3") +finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) +r = finder.find_arbitrage() +( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = r +( +ordered_trade_instructions_dct, +tx_in_count, +) = bot._simple_ordering_by_src_token( +best_trade_instructions_dic, best_src_token +) +pool_cids = [curve['cid'] for curve in ordered_trade_instructions_dct] +first_check_pools = finder.get_exact_pools(pool_cids) + +assert(len(first_check_pools) == 3), f"[test_bancor_v3] Validation expected 3 pools, got {len(first_check_pools)}" +for pool in first_check_pools: + assert type(pool) == ConstantProductCurve, f"[test_bancor_v3] Validation pool type mismatch, got {type(pool)} expected ConstantProductCurve" + assert pool.cid in pool_cids, f"[test_bancor_v3] Validation missing pool.cid {pool.cid} in {pool_cids}" +optimal_arb = finder.get_optimal_arb_trade_amts(pool_cids, 'BNT-FF1C') +assert type(optimal_arb) == float, f"[test_bancor_v3] Optimal arb calculation type is {type(optimal_arb)} not float" +assert iseq(optimal_arb, 5003.2368760578265), f"[test_bancor_v3] Optimal arb calculation type is {optimal_arb}, expected 5003.2368760578265" + + + + + + + + + + + +# ------------------------------------------------------------ +# Test 038 +# File test_038_TestBancorV3Mode.py +# Segment Test_max_arb_trade_in_constant_product +# ------------------------------------------------------------ +def test_test_max_arb_trade_in_constant_product(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("bancor_v3") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = r + ( + ordered_trade_instructions_dct, + tx_in_count, + ) = bot._simple_ordering_by_src_token( + best_trade_instructions_dic, best_src_token + ) + pool_cids = [curve['cid'] for curve in ordered_trade_instructions_dct] + first_check_pools = finder.get_exact_pools(pool_cids) + + flt='BNT-FF1C' + + tkn0 = finder.get_tkn(pool=first_check_pools[0], tkn_num=0) + tkn1 = finder.get_tkn(pool=first_check_pools[0], tkn_num=1) + tkn2 = finder.get_tkn(pool=first_check_pools[1], tkn_num=0) + tkn5 = finder.get_tkn(pool=first_check_pools[2], tkn_num=0) + p0t0 = first_check_pools[0].x_act if tkn0 == flt else first_check_pools[0].y_act + p0t1 = first_check_pools[0].y_act if tkn0 == flt else first_check_pools[0].x_act + p2t1 = first_check_pools[2].x_act if tkn5 == flt else first_check_pools[2].y_act + p2t0 = first_check_pools[2].y_act if tkn5 == flt else first_check_pools[2].x_act + p1t0 = first_check_pools[1].x if tkn1 == tkn2 else first_check_pools[1].y + p1t1 = first_check_pools[1].y if tkn1 == tkn2 else first_check_pools[1].x + fee0 = finder.get_fee_safe(first_check_pools[0].fee) + fee1 = finder.get_fee_safe(first_check_pools[1].fee) + fee2 = finder.get_fee_safe(first_check_pools[2].fee) + optimal_arb_low_level_check = finder.max_arb_trade_in_constant_product(p0t0=p0t0, p0t1=p0t1, p1t0=p1t0, p1t1=p1t1, p2t0=p2t0, p2t1=p2t1,fee0=fee0, fee1=fee1, fee2=fee2) + optimal_arb = finder.get_optimal_arb_trade_amts(pool_cids, flt) + print(optimal_arb_low_level_check, optimal_arb) + assert iseq(optimal_arb, optimal_arb_low_level_check), f"[test_bancor_v3] Arb calculation result mismatch, pools likely ordered incorrectly" + # - + + +# ------------------------------------------------------------ +# Test 038 +# File test_038_TestBancorV3Mode.py +# Segment Test_get_fee_safe +# ------------------------------------------------------------ +def test_test_get_fee_safe(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("bancor_v3") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + ext_fee = finder.get_fee_safe(first_check_pools[1].fee) + assert type(ext_fee) == float, f"[test_bancor_v3] Testing external pool, fee type is {type(ext_fee)} not float" + assert iseq(ext_fee, 0.003), f"[test_bancor_v3] Testing external pool, fee amt is {ext_fee} not 0.003" + + +# ------------------------------------------------------------ +# Test 038 +# File test_038_TestBancorV3Mode.py +# Segment Test_combos +# ------------------------------------------------------------ +def test_test_combos(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("bancor_v3") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + flt = {'MKR-79A2', 'TRAC-0A6F', 'MONA-412A', 'WBTC-C599', 'WOO-5D4B', 'MATIC-eBB0', 'BAT-87EF', 'UOS-5C8c', 'LRC-EafD', 'NMR-6671', 'DIP-cD83', 'TEMP-1aB9', 'ICHI-A881', 'USDC-eB48', 'ENS-9D72', 'vBNT-7f94', 'ANKR-EDD4', 'UNI-F984', 'REQ-938a', 'WETH-6Cc2', 'AAVE-DaE9', 'ENJ-3B9c', 'MANA-C942', 'wNXM-2bDE', 'QNT-4675', 'RLC-7375', 'CROWN-E0fa', 'CHZ-b4AF', 'USDT-1ec7', 'DAI-1d0F', 'RPL-A51f', 'HOT-26E2', 'LINK-86CA', 'wstETH-2Ca0'} + + combos = finder.get_combos(flashloan_tokens=flt, CCm=CCm, arb_mode="bancor_v3") + assert len(combos) == 1122, "[test_bancor_v3] Different data used for tests, expected 1122 combos" + # - + + +# ------------------------------------------------------------ +# Test 038 +# File test_038_TestBancorV3Mode.py +# Segment Test_get_miniverse_combos +# ------------------------------------------------------------ +def test_test_get_miniverse_combos(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("bancor_v3") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + flt = {'MKR-79A2', 'TRAC-0A6F', 'MONA-412A', 'WBTC-C599', 'WOO-5D4B', 'MATIC-eBB0', 'BAT-87EF', 'UOS-5C8c', 'LRC-EafD', 'NMR-6671', 'DIP-cD83', 'TEMP-1aB9', 'ICHI-A881', 'USDC-eB48', 'ENS-9D72', 'vBNT-7f94', 'ANKR-EDD4', 'UNI-F984', 'REQ-938a', 'WETH-6Cc2', 'AAVE-DaE9', 'ENJ-3B9c', 'MANA-C942', 'wNXM-2bDE', 'QNT-4675', 'RLC-7375', 'CROWN-E0fa', 'CHZ-b4AF', 'USDT-1ec7', 'DAI-1d0F', 'RPL-A51f', 'HOT-26E2', 'LINK-86CA', 'wstETH-2Ca0'} + + combos = finder.get_combos(flashloan_tokens=flt, CCm=CCm, arb_mode="bancor_v3") + all_miniverses = finder.get_miniverse_combos(combos) + assert len(all_miniverses) == 146, "[test_bancor_v3] Different data used for tests, expected 146 miniverses" + # - + + +# ------------------------------------------------------------ +# Test 038 +# File test_038_TestBancorV3Mode.py +# Segment Test_get_mono_direction_carbon_curves +# ------------------------------------------------------------ +def test_test_get_mono_direction_carbon_curves(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("bancor_v3") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + + bancor_v3_curve_0 = ( + finder.CCm.bypairs(f"BNT-FF1C/WETH-6Cc2") + .byparams(exchange="bancor_v3") + .curves + ) + bancor_v3_curve_1 = ( + finder.CCm.bypairs(f"BNT-FF1C/USDC-eB48") + .byparams(exchange="bancor_v3") + .curves + ) + carbon_curves = finder.CCm.bypairs(f"USDC-eB48/WETH-6Cc2") + carbon_curves = list(set(carbon_curves)) + carbon_curves = [ + curve + for curve in carbon_curves + if curve.params.get("exchange") == "carbon_v1" + ] + miniverse = [bancor_v3_curve_0 + bancor_v3_curve_1 + carbon_curves] + max_arb_carbon = finder.run_main_flow(miniverse=miniverse[0], src_token="BNT-FF1C") + ( + profit_src_0, + trade_instructions_0, + trade_instructions_df_0, + trade_instructions_dic_0, + ) = max_arb_carbon + + mono_carbon = finder.get_mono_direction_carbon_curves(miniverse[0], trade_instructions_df=trade_instructions_df_0, token_in=None) + test_mono_carbon = finder.get_mono_direction_carbon_curves(miniverse[0], trade_instructions_df=trade_instructions_df_0, token_in='WETH-6Cc2') + # Test that get_mono_direction_carbon_curves removed two curves + assert len(test_mono_carbon) != len(mono_carbon), f"[test_bancor_v3] Issue with get_mono_direction_carbon_curves, should have removed one or more pools" + # - + + + + + + \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_039_TestMultiMode.py b/fastlane_bot/tests/nbtest/test_039_TestMultiMode.py new file mode 100644 index 000000000..4f29faff9 --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_039_TestMultiMode.py @@ -0,0 +1,277 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_039_TestMultiMode.py` +# ------------------------------------------------------------ +# source file = NBTest_039_TestMultiMode.py +# test id = 039 +# test comment = TestMultiMode +# ------------------------------------------------------------ + + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Bot, Config +from fastlane_bot.bot import CarbonBot +from fastlane_bot.tools.cpc import ConstantProductCurve +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC +from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 +from fastlane_bot.events.interface import QueryInterface +from fastlane_bot.helpers.poolandtokens import PoolAndTokens +from fastlane_bot.helpers import TradeInstruction, TxReceiptHandler, TxRouteHandler, TxSubmitHandler, TxHelpers, TxHelper +from fastlane_bot.events.managers.manager import Manager +from fastlane_bot.events.interface import QueryInterface +from joblib import Parallel, delayed +import pytest +import math +import json +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SushiswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) +from fastlane_bot.testing import * +from fastlane_bot.modes import triangle_single_bancor3 +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +C = cfg = Config.new(config=Config.CONFIG_MAINNET) +C.DEFAULT_MIN_PROFIT_BNT = 0.02 +C.DEFAULT_MIN_PROFIT = 0.02 +cfg.DEFAULT_MIN_PROFIT_BNT = 0.02 +cfg.DEFAULT_MIN_PROFIT = 0.02 +assert (C.NETWORK == C.NETWORK_MAINNET) +assert (C.PROVIDER == C.PROVIDER_ALCHEMY) +setup_bot = CarbonBot(ConfigObj=C) +pools = None +with open('fastlane_bot/data/tests/latest_pool_data_testing.json') as f: + pools = json.load(f) +pools = [pool for pool in pools] +pools[0] +static_pools = pools +state = pools +exchanges = list({ex['exchange_name'] for ex in state}) +db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) +setup_bot.db = db + +static_pool_data_filename = "static_pool_data" + +static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) + +uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) + +tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) + +exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" + +exchanges = exchanges.split(",") + + +alchemy_max_block_fetch = 20 +static_pool_data["cid"] = [ + cfg.w3.keccak(text=f"{row['descr']}").hex() + for index, row in static_pool_data.iterrows() + ] +static_pool_data = [ + row for index, row in static_pool_data.iterrows() + if row["exchange_name"] in exchanges +] + +static_pool_data = pd.DataFrame(static_pool_data) +static_pool_data['exchange_name'].unique() +mgr = Manager( + web3=cfg.w3, + cfg=cfg, + pool_data=static_pool_data.to_dict(orient="records"), + SUPPORTED_EXCHANGES=exchanges, + alchemy_max_block_fetch=alchemy_max_block_fetch, + uniswap_v2_event_mappings=uniswap_v2_event_mappings, + tokens=tokens.to_dict(orient="records"), +) + +start_time = time.time() +Parallel(n_jobs=-1, backend="threading")( + delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data +) +cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") + +mgr.deduplicate_pool_data() +cids = [pool["cid"] for pool in mgr.pool_data] +assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" +def init_bot(mgr: Manager) -> CarbonBot: + """ + Initializes the bot. + + Parameters + ---------- + mgr : Manager + The manager object. + + Returns + ------- + CarbonBot + The bot object. + """ + mgr.cfg.logger.info("Initializing the bot...") + bot = CarbonBot(ConfigObj=mgr.cfg) + bot.db = db + bot.db.mgr = mgr + assert isinstance( + bot.db, QueryInterface + ), "QueryInterface not initialized correctly" + return bot +bot = init_bot(mgr) +bot.db.handle_token_key_cleanup() +bot.db.remove_unmapped_uniswap_v2_pools() +bot.db.remove_zero_liquidity_pools() +bot.db.remove_unsupported_exchanges() +tokens = bot.db.get_tokens() +ADDRDEC = {t.key: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} +flashloan_tokens = bot.setup_flashloan_tokens(None) +CCm = bot.setup_CCm(None) +pools = db.get_pool_data_with_tokens() + +arb_mode = "multi" + + +# ------------------------------------------------------------ +# Test 039 +# File test_039_TestMultiMode.py +# Segment Test_MIN_PROFIT +# ------------------------------------------------------------ +def test_test_min_profit(): +# ------------------------------------------------------------ + + assert(cfg.DEFAULT_MIN_PROFIT_BNT <= 0.02), f"[TestMultiMode], DEFAULT_MIN_PROFIT_BNT must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_BNT}" + assert(C.DEFAULT_MIN_PROFIT_BNT <= 0.02), f"[TestMultiMode], DEFAULT_MIN_PROFIT_BNT must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_BNT}" + + +# ------------------------------------------------------------ +# Test 039 +# File test_039_TestMultiMode.py +# Segment Test_get_arb_finder +# ------------------------------------------------------------ +def test_test_get_arb_finder(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("multi") + assert arb_finder.__name__ == "FindArbitrageMultiPairwise", f"[TestMultiMode] Expected arb_finder class name name = FindArbitrageMultiPairwise, found {arb_finder.__name__}" + + +# ------------------------------------------------------------ +# Test 039 +# File test_039_TestMultiMode.py +# Segment Test_Combos_and_Tokens +# ------------------------------------------------------------ +def test_test_combos_and_tokens(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("multi") + finder2 = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_TOKENS, + ConfigObj=bot.ConfigObj, + ) + all_tokens, combos = finder2.find_arbitrage() + assert len(all_tokens) == 545, f"[TestMultiMode] Using wrong dataset, expected 545 tokens, found {len(all_tokens)}" + assert len(combos) == 3264, f"[TestMultiMode] Using wrong dataset, expected 3264 tokens, found {len(combos)}" + + +# ------------------------------------------------------------ +# Test 039 +# File test_039_TestMultiMode.py +# Segment Test_Expected_Output +# ------------------------------------------------------------ +def test_test_expected_output(): +# ------------------------------------------------------------ + + run_full = bot._run(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode=arb_mode, data_validator=False, result=bot.XS_ARBOPPS) + arb_finder = bot._get_arb_finder("multi") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + assert len(r) == 22, f"[TestMultiMode] Expected 22 arbs, found {len(r)}" + assert len(r) == len(run_full), f"[TestMultiMode] Expected arbs from .find_arbitrage - {len(r)} - to match _run - {len(run_full)}" + + +# ------------------------------------------------------------ +# Test 039 +# File test_039_TestMultiMode.py +# Segment Test_Multiple_Curves_Used +# ------------------------------------------------------------ +def test_test_multiple_curves_used(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("multi") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + multi_carbon_count = 0 + + for arb in r: + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = arb + if len(best_trade_instructions_dic) > 2: + multi_carbon_count += 1 + + assert multi_carbon_count > 0, f"[TestMultiMode] Not finding arbs with multiple Carbon curves." + # - + + +# ------------------------------------------------------------ +# Test 039 +# File test_039_TestMultiMode.py +# Segment Test_Single_Direction_Carbon_Curves +# ------------------------------------------------------------ +def test_test_single_direction_carbon_curves(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("multi") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + src_token="WBTC-C599" + wrong_direction_cids = ['4083388403051261561560495289181218537493-0', '4083388403051261561560495289181218537579-0', '4083388403051261561560495289181218537610-0', '4083388403051261561560495289181218537629-0', '4083388403051261561560495289181218537639-0', '4083388403051261561560495289181218537755-0'] + curves_before = [ConstantProductCurve(k=2290523503.4460173, x=273.1073125047371, x_act=0.07743961144774403, y_act=1814.6001096442342, pair='WBTC-C599/USDC-eB48', cid='0x8d7ac7e77704f3ac75534d5500159a7a4b7e6e23dbdca7d9a8085bdea0348d0c', fee=0.0005, descr='uniswap_v3 WBTC-C599/USDC-eB48 500', constr='pkpp', params={'exchange': 'uniswap_v3', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17675876, 'L': 47859.413948}), ConstantProductCurve(k=3675185.41145277, x=11.059038979187497, x_act=0, y_act=1385.267061, pair='WBTC-C599/USDC-eB48', cid='4083388403051261561560495289181218537493-0', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 1385.267061, 'yint': 1385.267061, 'A': 0.722593217276426, 'B': 172.62676501631972, 'pa': 30049.999999999647, 'pb': 29799.999999999665}), ConstantProductCurve(k=29672.782767383174, x=1.0315213950985431, x_act=0, y_act=3651.804716, pair='WBTC-C599/USDC-eB48', cid='4083388403051261561560495289181218537579-0', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 3651.804716, 'yint': 3651.804716, 'A': 21.199636119827687, 'B': 145.79437574886072, 'pa': 27886.999999999643, 'pb': 21255.999999999985}), ConstantProductCurve(k=6.863635116394053e+16, x=1525337.9097739116, x_act=0, y_act=4499.746836, pair='WBTC-C599/USDC-eB48', cid='4083388403051261561560495289181218537610-0', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 4499.746836, 'yint': 4499.746836, 'A': 0, 'B': 171.7556317853946, 'pa': 29499.99999999976, 'pb': 29499.99999999976}), ConstantProductCurve(k=143046.70577155304, x=2.1824671097293846, x_act=0, y_act=5742.51191, pair='WBTC-C599/USDC-eB48', cid='4083388403051261561560495289181218537629-0', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 5742.51191, 'yint': 6413.595264, 'A': 16.957530991696217, 'B': 158.11388300841884, 'pa': 30649.99999999968, 'pb': 24999.99999999996}), ConstantProductCurve(k=5459975.623181331, x=437148.88403306017, x_act=0, y_act=0.50315999, pair='USDC-eB48/WBTC-C599', cid='4083388403051261561560495289181218537629-1', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 0.50315999, 'yint': 0.50315999, 'A': 0.0002153330778227767, 'B': 0.005129891760425664, 'pa': 2.8571428571428076e-05, 'pb': 2.631578947368312e-05}), ConstantProductCurve(k=443607.9519434853, x=3.85826034424969, x_act=0, y_act=9876.976514, pair='WBTC-C599/USDC-eB48', cid='4083388403051261561560495289181218537639-0', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 9876.976514, 'yint': 9876.976514, 'A': 14.829426635724872, 'B': 157.79733838059485, 'pa': 29799.999999999665, 'pb': 24899.999999999953}), ConstantProductCurve(k=5324.625267368582, x=12680.839210183807, x_act=0, y_act=0.01198047, pair='USDC-eB48/WBTC-C599', cid='4083388403051261561560495289181218537639-1', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 0.01198047, 'yint': 0.01198047, 'A': 0.00016418343273514376, 'B': 0.0055901699437491455, 'pa': 3.311258278145614e-05, 'pb': 3.124999999999633e-05}), ConstantProductCurve(k=3316749913763783.5, x=331674.9583747572, x_act=0, y_act=1000.0, pair='WBTC-C599/USDC-eB48', cid='4083388403051261561560495289181218537755-0', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 1000.0, 'yint': 1000.0, 'A': 0, 'B': 173.63754485997586, 'pa': 30149.999999999825, 'pb': 30149.999999999825})] + curves_expected_after = [ConstantProductCurve(k=2290523503.4460173, x=273.1073125047371, x_act=0.07743961144774403, y_act=1814.6001096442342, pair='WBTC-C599/USDC-eB48', cid='0x8d7ac7e77704f3ac75534d5500159a7a4b7e6e23dbdca7d9a8085bdea0348d0c', fee=0.0005, descr='uniswap_v3 WBTC-C599/USDC-eB48 500', constr='pkpp', params={'exchange': 'uniswap_v3', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17675876, 'L': 47859.413948}), ConstantProductCurve(k=5459975.623181331, x=437148.88403306017, x_act=0, y_act=0.50315999, pair='USDC-eB48/WBTC-C599', cid='4083388403051261561560495289181218537629-1', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 0.50315999, 'yint': 0.50315999, 'A': 0.0002153330778227767, 'B': 0.005129891760425664, 'pa': 2.8571428571428076e-05, 'pb': 2.631578947368312e-05}), ConstantProductCurve(k=5324.625267368582, x=12680.839210183807, x_act=0, y_act=0.01198047, pair='USDC-eB48/WBTC-C599', cid='4083388403051261561560495289181218537639-1', fee=0.002, descr='carbon_v1 WBTC-C599/USDC-eB48 0.002', constr='carb', params={'exchange': 'carbon_v1', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17674427, 'y': 0.01198047, 'yint': 0.01198047, 'A': 0.00016418343273514376, 'B': 0.0055901699437491455, 'pa': 3.311258278145614e-05, 'pb': 3.124999999999633e-05})] + test_process_wrong_direction_pools = finder.process_wrong_direction_pools(curve_combo=curves_before, wrong_direction_cids=wrong_direction_cids) + O, profit_src, r, trade_instructions_df = finder.run_main_flow(curves=curves_expected_after, src_token="WBTC-C599", tkn0="USDC-eB48", tkn1="WBTC-C599") + + assert len(curves_before) - len(wrong_direction_cids) == len(test_process_wrong_direction_pools), f"[TestMultiMode] Wrong direction CIDs not removed correctly, started with {len(curves_before)}, removing {len(wrong_direction_cids)}, expected {len(curves_before) - len(wrong_direction_cids)} got {len(test_process_wrong_direction_pools)}" + for curve in test_process_wrong_direction_pools: + assert curve.cid not in wrong_direction_cids, f"[TestMultiMode] Failed to remove curve {curve.cid} from list of wrong direction pools" + assert iseq(profit_src, -0.059102630716552085) + # - + + + + \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_040_TestSingleMode.py b/fastlane_bot/tests/nbtest/test_040_TestSingleMode.py new file mode 100644 index 000000000..0c223325b --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_040_TestSingleMode.py @@ -0,0 +1,229 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_040_TestSingleMode.py` +# ------------------------------------------------------------ +# source file = NBTest_040_TestSingleMode.py +# test id = 040 +# test comment = TestSingleMode +# ------------------------------------------------------------ + + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Bot, Config +from fastlane_bot.bot import CarbonBot +from fastlane_bot.tools.cpc import ConstantProductCurve +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC +from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 +from fastlane_bot.events.interface import QueryInterface +from fastlane_bot.helpers.poolandtokens import PoolAndTokens +from fastlane_bot.helpers import TradeInstruction, TxReceiptHandler, TxRouteHandler, TxSubmitHandler, TxHelpers, TxHelper +from fastlane_bot.events.managers.manager import Manager +from fastlane_bot.events.interface import QueryInterface +from joblib import Parallel, delayed +import pytest +import math +import json +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SushiswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) +from fastlane_bot.testing import * +from fastlane_bot.modes import triangle_single_bancor3 +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +C = cfg = Config.new(config=Config.CONFIG_MAINNET) +C.DEFAULT_MIN_PROFIT_BNT = 0.02 +C.DEFAULT_MIN_PROFIT = 0.02 +cfg.DEFAULT_MIN_PROFIT_BNT = 0.02 +cfg.DEFAULT_MIN_PROFIT = 0.02 +assert (C.NETWORK == C.NETWORK_MAINNET) +assert (C.PROVIDER == C.PROVIDER_ALCHEMY) +setup_bot = CarbonBot(ConfigObj=C) +pools = None +with open('fastlane_bot/data/tests/latest_pool_data_testing.json') as f: + pools = json.load(f) +pools = [pool for pool in pools] +pools[0] +static_pools = pools +state = pools +exchanges = list({ex['exchange_name'] for ex in state}) +db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) +setup_bot.db = db + +static_pool_data_filename = "static_pool_data" + +static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) + +uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) + +tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) + +exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" + +exchanges = exchanges.split(",") + + +alchemy_max_block_fetch = 20 +static_pool_data["cid"] = [ + cfg.w3.keccak(text=f"{row['descr']}").hex() + for index, row in static_pool_data.iterrows() + ] +static_pool_data = [ + row for index, row in static_pool_data.iterrows() + if row["exchange_name"] in exchanges +] + +static_pool_data = pd.DataFrame(static_pool_data) +static_pool_data['exchange_name'].unique() +mgr = Manager( + web3=cfg.w3, + cfg=cfg, + pool_data=static_pool_data.to_dict(orient="records"), + SUPPORTED_EXCHANGES=exchanges, + alchemy_max_block_fetch=alchemy_max_block_fetch, + uniswap_v2_event_mappings=uniswap_v2_event_mappings, + tokens=tokens.to_dict(orient="records"), +) + +start_time = time.time() +Parallel(n_jobs=-1, backend="threading")( + delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data +) +cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") + +mgr.deduplicate_pool_data() +cids = [pool["cid"] for pool in mgr.pool_data] +assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" +def init_bot(mgr: Manager) -> CarbonBot: + """ + Initializes the bot. + + Parameters + ---------- + mgr : Manager + The manager object. + + Returns + ------- + CarbonBot + The bot object. + """ + mgr.cfg.logger.info("Initializing the bot...") + bot = CarbonBot(ConfigObj=mgr.cfg) + bot.db = db + bot.db.mgr = mgr + assert isinstance( + bot.db, QueryInterface + ), "QueryInterface not initialized correctly" + return bot +bot = init_bot(mgr) +bot.db.handle_token_key_cleanup() +bot.db.remove_unmapped_uniswap_v2_pools() +bot.db.remove_zero_liquidity_pools() +bot.db.remove_unsupported_exchanges() +tokens = bot.db.get_tokens() +ADDRDEC = {t.key: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} +flashloan_tokens = bot.setup_flashloan_tokens(None) +CCm = bot.setup_CCm(None) +pools = db.get_pool_data_with_tokens() + +arb_mode = "single" + +assert(cfg.DEFAULT_MIN_PROFIT_BNT <= 0.02), f"[TestSingleMode], DEFAULT_MIN_PROFIT_BNT must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_BNT}" +assert(C.DEFAULT_MIN_PROFIT_BNT <= 0.02), f"[TestSingleMode], DEFAULT_MIN_PROFIT_BNT must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_BNT}" + + + + +# ------------------------------------------------------------ +# Test 040 +# File test_040_TestSingleMode.py +# Segment Test_arb_mode_class +# ------------------------------------------------------------ +def test_test_arb_mode_class(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("single") + assert arb_finder.__name__ == "FindArbitrageSinglePairwise", f"[TestSingleMode] Expected arb_finder class name name = FindArbitrageSinglePairwise, found {arb_finder.__name__}" + + +# ------------------------------------------------------------ +# Test 040 +# File test_040_TestSingleMode.py +# Segment Test_tokens_and_combos +# ------------------------------------------------------------ +def test_test_tokens_and_combos(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("single") + finder2 = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_TOKENS, + ConfigObj=bot.ConfigObj, + ) + all_tokens, combos = finder2.find_arbitrage() + assert len(all_tokens) == 545, f"[TestMultiMode] Using wrong dataset, expected 545 tokens, found {len(all_tokens)}" + assert len(combos) == 3264, f"[TestMultiMode] Using wrong dataset, expected 3264 tokens, found {len(combos)}" + + # ### Test_Single_Arb_Finder_vs_run + + run_full = bot._run(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode=arb_mode, data_validator=False, result=bot.XS_ARBOPPS) + arb_finder = bot._get_arb_finder("single") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + assert len(r) == 22, f"[TestSingleMode] Expected 22 arbs, found {len(r)}" + assert len(r) == len(run_full), f"[TestSingleMode] Expected arbs from .find_arbitrage - {len(r)} - to match _run - {len(run_full)}" + + r + + +# ------------------------------------------------------------ +# Test 040 +# File test_040_TestSingleMode.py +# Segment Test_no_multi_carbon +# ------------------------------------------------------------ +def test_test_no_multi_carbon(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("single") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + multi_carbon_count = 0 + + for arb in r: + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = arb + if len(best_trade_instructions_dic) > 2: + multi_carbon_count += 1 + + assert multi_carbon_count == 0, f"[TestSingleMode] Expected arbs without multiple Carbon curves, but found {len(multi_carbon_count)}" \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_042_TestBancorV3ModeTwoHop.py b/fastlane_bot/tests/nbtest/test_042_TestBancorV3ModeTwoHop.py new file mode 100644 index 000000000..7a069d358 --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_042_TestBancorV3ModeTwoHop.py @@ -0,0 +1,465 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_042_TestBancorV3ModeTwoHop.py` +# ------------------------------------------------------------ +# source file = NBTest_042_TestBancorV3ModeTwoHop.py +# test id = 042 +# test comment = TestBancorV3ModeTwoHop +# ------------------------------------------------------------ + + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Bot, Config +from fastlane_bot.bot import CarbonBot +from fastlane_bot.tools.cpc import ConstantProductCurve +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC +from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 +from fastlane_bot.events.interface import QueryInterface +from fastlane_bot.helpers.poolandtokens import PoolAndTokens +from fastlane_bot.helpers import TradeInstruction, TxReceiptHandler, TxRouteHandler, TxSubmitHandler, TxHelpers, TxHelper +from fastlane_bot.events.managers.manager import Manager +from fastlane_bot.events.interface import QueryInterface +from joblib import Parallel, delayed +import pytest +import math +import json +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SushiswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) +from fastlane_bot.testing import * +from fastlane_bot.modes import triangle_single_bancor3 +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +C = cfg = Config.new(config=Config.CONFIG_MAINNET) +C.DEFAULT_MIN_PROFIT_BNT = 50 +C.DEFAULT_MIN_PROFIT = 50 +cfg.DEFAULT_MIN_PROFIT_BNT = 50 +cfg.DEFAULT_MIN_PROFIT = 50 +assert (C.NETWORK == C.NETWORK_MAINNET) +assert (C.PROVIDER == C.PROVIDER_ALCHEMY) +setup_bot = CarbonBot(ConfigObj=C) +pools = None +with open('fastlane_bot/data/tests/latest_pool_data_testing.json') as f: + pools = json.load(f) +pools = [pool for pool in pools] +pools[0] +static_pools = pools +state = pools +exchanges = list({ex['exchange_name'] for ex in state}) +db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) +setup_bot.db = db + +static_pool_data_filename = "static_pool_data" + +static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) + +uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) + +tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) + +exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" + +exchanges = exchanges.split(",") + + +alchemy_max_block_fetch = 20 +static_pool_data["cid"] = [ + cfg.w3.keccak(text=f"{row['descr']}").hex() + for index, row in static_pool_data.iterrows() + ] +static_pool_data = [ + row for index, row in static_pool_data.iterrows() + if row["exchange_name"] in exchanges +] + +static_pool_data = pd.DataFrame(static_pool_data) +static_pool_data['exchange_name'].unique() +mgr = Manager( + web3=cfg.w3, + cfg=cfg, + pool_data=static_pool_data.to_dict(orient="records"), + SUPPORTED_EXCHANGES=exchanges, + alchemy_max_block_fetch=alchemy_max_block_fetch, + uniswap_v2_event_mappings=uniswap_v2_event_mappings, + tokens=tokens.to_dict(orient="records"), +) + +start_time = time.time() +Parallel(n_jobs=-1, backend="threading")( + delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data +) +cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") + +mgr.deduplicate_pool_data() +cids = [pool["cid"] for pool in mgr.pool_data] +assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" +def init_bot(mgr: Manager) -> CarbonBot: + """ + Initializes the bot. + + Parameters + ---------- + mgr : Manager + The manager object. + + Returns + ------- + CarbonBot + The bot object. + """ + mgr.cfg.logger.info("Initializing the bot...") + bot = CarbonBot(ConfigObj=mgr.cfg) + bot.db = db + bot.db.mgr = mgr + assert isinstance( + bot.db, QueryInterface + ), "QueryInterface not initialized correctly" + return bot +bot = init_bot(mgr) +bot.db.handle_token_key_cleanup() +bot.db.remove_unmapped_uniswap_v2_pools() +bot.db.remove_zero_liquidity_pools() +bot.db.remove_unsupported_exchanges() +tokens = bot.db.get_tokens() +ADDRDEC = {t.key: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} +flashloan_tokens = bot.setup_flashloan_tokens(None) +CCm = bot.setup_CCm(None) +pools = db.get_pool_data_with_tokens() + +arb_mode = "b3_two_hop" + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_min_profit +# ------------------------------------------------------------ +def test_test_min_profit(): +# ------------------------------------------------------------ + + assert C.DEFAULT_MIN_PROFIT_BNT == 50, f"[test_bancor_v3_two_hop] wrong DEFAULT_MIN_PROFIT_BNT for test, expected 50, got {C.DEFAULT_MIN_PROFIT_BNT}" + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_arb_mode_class +# ------------------------------------------------------------ +def test_test_arb_mode_class(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("b3_two_hop") + assert arb_finder.__name__ == "ArbitrageFinderTriangleBancor3TwoHop", f"[test_bancor_v3_two_hop] Wrong Arb Finder class, expected ArbitrageFinderTriangleBancor3TwoHop, got {arb_finder.__name__}" + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_Trade_Merge +# ------------------------------------------------------------ +def test_test_trade_merge(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("b3_two_hop") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = r + ( + ordered_trade_instructions_dct, + tx_in_count, + ) = bot._simple_ordering_by_src_token( + best_trade_instructions_dic, best_src_token + ) + ordered_scaled_dcts = bot._basic_scaling( + ordered_trade_instructions_dct, best_src_token + ) + # Convert the trade instructions + ordered_trade_instructions_objects = bot._convert_trade_instructions( + ordered_scaled_dcts) + tx_route_handler = bot.TxRouteHandlerClass( + trade_instructions=ordered_trade_instructions_objects + ) + agg_trade_instructions = ( + tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) + if bot._carbon_in_trade_route(ordered_trade_instructions_objects) + else ordered_trade_instructions_objects + ) + # Calculate the trade instructions + calculated_trade_instructions = tx_route_handler.calculate_trade_outputs( + agg_trade_instructions + ) + assert len(calculated_trade_instructions) == 3 + # Aggregate multiple Bancor V3 trades into a single trade + calculated_trade_instructions = TxRouteHandler.aggregate_bancor_v3_trades( + calculated_trade_instructions + ) + assert len(calculated_trade_instructions) == 2 + assert calculated_trade_instructions[0].tknin != "BNT-FF1C" + assert calculated_trade_instructions[0].tknout != "BNT-FF1C" + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_get_optimal_arb_trade_amts +# ------------------------------------------------------------ +def test_test_get_optimal_arb_trade_amts(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("b3_two_hop") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = r + ( + ordered_trade_instructions_dct, + tx_in_count, + ) = bot._simple_ordering_by_src_token( + best_trade_instructions_dic, best_src_token + ) + + + pool_cids = [curve['cid'] for curve in ordered_trade_instructions_dct] + first_check_pools = finder.get_exact_pools(pool_cids) + + assert first_check_pools[0].cid == '0x7be3da0f8d0f70d8f7a84a08dd267beea4318ed1c9fb3d602b0f3a3c7bd1cf4a', f"[test_bancor_v3_two_hop] Validation, wrong first pool, expected CID: 0x7be3da0f8d0f70d8f7a84a08dd267beea4318ed1c9fb3d602b0f3a3c7bd1cf4a, got CID: {first_check_pools[0].cid}" + assert first_check_pools[1].cid == '0x748ab2bef0d97e5a044268626e6c9c104bab818605d44f650fdeaa03a3c742d2', f"[test_bancor_v3_two_hop] Validation, wrong second pool, expected CID: 0x748ab2bef0d97e5a044268626e6c9c104bab818605d44f650fdeaa03a3c742d2, got CID: {first_check_pools[1].cid}" + assert first_check_pools[2].cid == '0xb1d8cd62f75016872495dae3e19d96e364767e7d674488392029d15cdbcd7b34', f"[test_bancor_v3_two_hop] Validation, wrong third pool, expected CID: 0xb1d8cd62f75016872495dae3e19d96e364767e7d674488392029d15cdbcd7b34, got CID: {first_check_pools[2].cid}" + assert(len(first_check_pools) == 3), f"[test_bancor_v3_two_hop] Validation expected 3 pools, got {len(first_check_pools)}" + for pool in first_check_pools: + assert type(pool) == ConstantProductCurve, f"[test_bancor_v3_two_hop] Validation pool type mismatch, got {type(pool)} expected ConstantProductCurve" + assert pool.cid in pool_cids, f"[test_bancor_v3_two_hop] Validation missing pool.cid {pool.cid} in {pool_cids}" + + optimal_arb = finder.get_optimal_arb_trade_amts(pool_cids, 'DAI-1d0F') + assert type(optimal_arb) == float, f"[test_bancor_v3_two_hop] Optimal arb calculation type is {type(optimal_arb)} not float" + assert iseq(optimal_arb, 6179.168331968203), f"[test_bancor_v3_two_hop] Optimal arb calculation type is {optimal_arb}, expected 6179.168331968203" + # - + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_max_arb_trade_in_constant_product +# ------------------------------------------------------------ +def test_test_max_arb_trade_in_constant_product(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("b3_two_hop") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = r + ( + ordered_trade_instructions_dct, + tx_in_count, + ) = bot._simple_ordering_by_src_token( + best_trade_instructions_dic, best_src_token + ) + + + pool_cids = [curve['cid'] for curve in ordered_trade_instructions_dct] + first_check_pools = finder.get_exact_pools(pool_cids) + flt='DAI-1d0F' + tkn0 = flt + tkn1 = finder.get_tkn(pool=first_check_pools[0], tkn_num=1) if finder.get_tkn(pool=first_check_pools[0], tkn_num=1) != flt else finder.get_tkn(pool=first_check_pools[0], tkn_num=0) + tkn2 = finder.get_tkn(pool=first_check_pools[1], tkn_num=0) if finder.get_tkn(pool=first_check_pools[1], tkn_num=0) == tkn1 else finder.get_tkn(pool=first_check_pools[1], tkn_num=1) + tkn3 = finder.get_tkn(pool=first_check_pools[1], tkn_num=0) if finder.get_tkn(pool=first_check_pools[1], tkn_num=0) != tkn1 else finder.get_tkn(pool=first_check_pools[1], tkn_num=1) + tkn5 = finder.get_tkn(pool=first_check_pools[2], tkn_num=1) if finder.get_tkn(pool=first_check_pools[2], tkn_num=1) == flt else finder.get_tkn(pool=first_check_pools[2], tkn_num=0) + p0t0 = first_check_pools[0].x if finder.get_tkn(pool=first_check_pools[0], tkn_num=0) == flt else first_check_pools[0].y + p0t1 = first_check_pools[0].y if finder.get_tkn(pool=first_check_pools[0], tkn_num=0) == flt else first_check_pools[0].x + p1t0 = first_check_pools[1].x if tkn1 == finder.get_tkn(pool=first_check_pools[1], tkn_num=0) else first_check_pools[1].y + p1t1 = first_check_pools[1].y if tkn1 == finder.get_tkn(pool=first_check_pools[1], tkn_num=0) else first_check_pools[1].x + p2t0 = first_check_pools[2].x if finder.get_tkn(pool=first_check_pools[2], tkn_num=0) != flt else first_check_pools[2].y + p2t1 = first_check_pools[2].y if finder.get_tkn(pool=first_check_pools[2], tkn_num=0) != flt else first_check_pools[2].x + fee0 = finder.get_fee_safe(first_check_pools[0].fee) + fee1 = finder.get_fee_safe(first_check_pools[1].fee) + fee2 = finder.get_fee_safe(first_check_pools[2].fee) + optimal_arb = finder.get_optimal_arb_trade_amts(pool_cids, 'DAI-1d0F') + optimal_arb_low_level_check = finder.max_arb_trade_in_constant_product(p0t0=p0t0, p0t1=p0t1, p1t0=p1t0, p1t1=p1t1, p2t0=p2t0, p2t1=p2t1,fee0=fee0, fee1=fee1, fee2=fee2) + assert iseq(optimal_arb, optimal_arb_low_level_check), f"[test_bancor_v3_two_hop] Arb calculation result mismatch, pools likely ordered incorrectly, previous calc: {optimal_arb}, this calc: {optimal_arb_low_level_check}" + # max_arb_in = finder.max_arb_trade_in_constant_product(p0t0, p0t1, p1t0, p1t1, p2t0, p2t1, fee0=fee0, fee1=fee1, fee2=fee2) + # finder.ConfigObj.logger.info(f"\n\nfirst_check_pools: {first_check_pools}\n\nValidating trade, max_arb_in= {max_arb_in} {tkn0} -> {tkn1} -> {tkn3} -> {tkn5}, token amts: {p0t0, p0t1, p1t0, p1t1, p2t0, p2t1}, fees: {fee0, fee1, fee2}") + # - + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_get_fee_safe +# ------------------------------------------------------------ +def test_test_get_fee_safe(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("b3_two_hop") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = r + ( + ordered_trade_instructions_dct, + tx_in_count, + ) = bot._simple_ordering_by_src_token( + best_trade_instructions_dic, best_src_token + ) + + pool_cids = [curve['cid'] for curve in ordered_trade_instructions_dct] + first_check_pools = finder.get_exact_pools(pool_cids) + ext_fee = finder.get_fee_safe(first_check_pools[2].fee) + assert type(ext_fee) == float, f"[test_bancor_v3_two_hop] Testing external pool, fee type is {type(ext_fee)} not float" + assert iseq(ext_fee, 0.0005), f"[test_bancor_v3_two_hop] Testing external pool, fee amt is {ext_fee} not 0.0005" + + # - + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_combos +# ------------------------------------------------------------ +def test_test_combos(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("b3_two_hop") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + #test_2_pools = [ConstantProductCurve(k=2921921249910.464, x=2760126.9934445512, x_act=2760126.9934445512, y_act=1058618.410258, pair='BNT-FF1C/USDC-eB48', cid='0xc4771395e1389e2e3a12ec22efbb7aff5b1c04e5ce9c7596a82e9dc8fdec725b', fee=0.0, descr='bancor_v3 BNT-FF1C/USDC-eB48 0.000', constr='uv2', params={'exchange': 'bancor_v3', 'tknx_dec': 18, 'tkny_dec': 6, 'tknx_addr': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17713739}), ConstantProductCurve(k=518129588.60853314, x=6351922.348885405, x_act=6351922.348885405, y_act=81.57051679, pair='BNT-FF1C/WBTC-C599', cid='0x3885d978c125e66686e3f678ab64d5b09e61f89bf6e87c9ff66e740fd06aeefa', fee=0.0, descr='bancor_v3 BNT-FF1C/WBTC-C599 0.000', constr='uv2', params={'exchange': 'bancor_v3', 'tknx_dec': 18, 'tkny_dec': 8, 'tknx_addr': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkny_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'blocklud': 17713739}), ConstantProductCurve(k=787603837541.6204, x=5107.692365701484, x_act=4.159867948255851, y_act=336571.44633978605, pair='WBTC-C599/USDC-eB48', cid='0x49ed97db2c080b7eac91dfaa7d51d5e8ac34c4dcfbcd3e8f2ed326a2a527b959', fee=0.003, descr='uniswap_v3 WBTC-C599/USDC-eB48 3000', constr='pkpp', params={'exchange': 'uniswap_v3', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17713395, 'L': 887470.4713632})] + flt = {'MKR-79A2', 'TRAC-0A6F', 'MONA-412A', 'WBTC-C599', 'WOO-5D4B', 'MATIC-eBB0', 'BAT-87EF', 'UOS-5C8c', 'LRC-EafD', 'NMR-6671', 'DIP-cD83', 'TEMP-1aB9', 'ICHI-A881', 'USDC-eB48', 'ENS-9D72', 'vBNT-7f94', 'ANKR-EDD4', 'UNI-F984', 'REQ-938a', 'WETH-6Cc2', 'AAVE-DaE9', 'ENJ-3B9c', 'MANA-C942', 'wNXM-2bDE', 'QNT-4675', 'RLC-7375', 'CROWN-E0fa', 'CHZ-b4AF', 'USDT-1ec7', 'DAI-1d0F', 'RPL-A51f', 'HOT-26E2', 'LINK-86CA', 'wstETH-2Ca0'} + combos = finder.get_combos(flashloan_tokens=flt, CCm=CCm, arb_mode="b3_two_hop") + assert len(combos) == 1122, "[test_bancor_v3_two_hop] Different data used for tests, expected 1122 combos" + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_get_miniverse_combos +# ------------------------------------------------------------ +def test_test_get_miniverse_combos(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("b3_two_hop") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + flt = {'MKR-79A2', 'TRAC-0A6F', 'MONA-412A', 'WBTC-C599', 'WOO-5D4B', 'MATIC-eBB0', 'BAT-87EF', 'UOS-5C8c', 'LRC-EafD', 'NMR-6671', 'DIP-cD83', 'TEMP-1aB9', 'ICHI-A881', 'USDC-eB48', 'ENS-9D72', 'vBNT-7f94', 'ANKR-EDD4', 'UNI-F984', 'REQ-938a', 'WETH-6Cc2', 'AAVE-DaE9', 'ENJ-3B9c', 'MANA-C942', 'wNXM-2bDE', 'QNT-4675', 'RLC-7375', 'CROWN-E0fa', 'CHZ-b4AF', 'USDT-1ec7', 'DAI-1d0F', 'RPL-A51f', 'HOT-26E2', 'LINK-86CA', 'wstETH-2Ca0'} + combos = finder.get_combos(flashloan_tokens=flt, CCm=CCm, arb_mode="b3_two_hop") + all_miniverses = finder.get_miniverse_combos(combos) + assert len(all_miniverses) == 146, "[test_bancor_v3_two_hop] Different data used for tests, expected 146 miniverses" + + +# ------------------------------------------------------------ +# Test 042 +# File test_042_TestBancorV3ModeTwoHop.py +# Segment Test_get_mono_direction_carbon_curves +# ------------------------------------------------------------ +def test_test_get_mono_direction_carbon_curves(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("b3_two_hop") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=False, + ConfigObj=bot.ConfigObj, + ) + + bancor_v3_curve_0 = ( + finder.CCm.bypairs(f"BNT-FF1C/WETH-6Cc2") + .byparams(exchange="bancor_v3") + .curves + ) + bancor_v3_curve_1 = ( + finder.CCm.bypairs(f"BNT-FF1C/USDC-eB48") + .byparams(exchange="bancor_v3") + .curves + ) + + carbon_curves = finder.CCm.bypairs(f"USDC-eB48/WETH-6Cc2") + carbon_curves = list(set(carbon_curves)) + carbon_curves = [ + curve + for curve in carbon_curves + if curve.params.get("exchange") == "carbon_v1" + ] + miniverse = [bancor_v3_curve_0 + bancor_v3_curve_1 + carbon_curves] + max_arb_carbon = finder.run_main_flow(miniverse=miniverse[0], src_token="BNT-FF1C") + + ( + profit_src_0, + trade_instructions_0, + trade_instructions_df_0, + trade_instructions_dic_0, + ) = max_arb_carbon + mono_carbon = finder.get_mono_direction_carbon_curves(miniverse[0], trade_instructions_df=trade_instructions_df_0, token_in=None) + test_mono_carbon = finder.get_mono_direction_carbon_curves(miniverse[0], trade_instructions_df=trade_instructions_df_0, token_in='WETH-6Cc2') + # Test that get_mono_direction_carbon_curves removed two curves + assert len(test_mono_carbon) != len(mono_carbon), f"[test_bancor_v3_two_hop] Issue with get_mono_direction_carbon_curves, should have removed one or more pools" \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_043_TestEmptyCarbonOrders.py b/fastlane_bot/tests/nbtest/test_043_TestEmptyCarbonOrders.py new file mode 100644 index 000000000..7fc4ba3c7 --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_043_TestEmptyCarbonOrders.py @@ -0,0 +1,231 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_043_TestEmptyCarbonOrders.py` +# ------------------------------------------------------------ +# source file = NBTest_043_TestEmptyCarbonOrders.py +# test id = 043 +# test comment = TestEmptyCarbonOrders +# ------------------------------------------------------------ + + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Bot, Config +from fastlane_bot.bot import CarbonBot +from fastlane_bot.tools.cpc import ConstantProductCurve +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC +from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 +from fastlane_bot.events.interface import QueryInterface +from fastlane_bot.helpers.poolandtokens import PoolAndTokens +from fastlane_bot.helpers import TradeInstruction, TxReceiptHandler, TxRouteHandler, TxSubmitHandler, TxHelpers, TxHelper +from fastlane_bot.events.managers.manager import Manager +from fastlane_bot.events.interface import QueryInterface +from joblib import Parallel, delayed +from dataclasses import dataclass, asdict, field +import pytest +import math +import json +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SushiswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) +from fastlane_bot.testing import * +from fastlane_bot.modes import triangle_single_bancor3 +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +C = cfg = Config.new(config=Config.CONFIG_MAINNET) +C.DEFAULT_MIN_PROFIT_BNT = 0.02 +C.DEFAULT_MIN_PROFIT = 0.02 +cfg.DEFAULT_MIN_PROFIT_BNT = 0.02 +cfg.DEFAULT_MIN_PROFIT = 0.02 +assert (C.NETWORK == C.NETWORK_MAINNET) +assert (C.PROVIDER == C.PROVIDER_ALCHEMY) +setup_bot = CarbonBot(ConfigObj=C) +pools = None +with open('fastlane_bot/data/tests/latest_pool_data_testing.json') as f: + pools = json.load(f) +pools = [pool for pool in pools] +pools[0] +static_pools = pools +state = pools +exchanges = list({ex['exchange_name'] for ex in state}) +db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) +setup_bot.db = db + +static_pool_data_filename = "static_pool_data" + +static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) + +uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) + +tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) + +exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" + +exchanges = exchanges.split(",") + + +alchemy_max_block_fetch = 20 +static_pool_data["cid"] = [ + cfg.w3.keccak(text=f"{row['descr']}").hex() + for index, row in static_pool_data.iterrows() + ] +static_pool_data = [ + row for index, row in static_pool_data.iterrows() + if row["exchange_name"] in exchanges +] + +static_pool_data = pd.DataFrame(static_pool_data) +static_pool_data['exchange_name'].unique() +mgr = Manager( + web3=cfg.w3, + cfg=cfg, + pool_data=static_pool_data.to_dict(orient="records"), + SUPPORTED_EXCHANGES=exchanges, + alchemy_max_block_fetch=alchemy_max_block_fetch, + uniswap_v2_event_mappings=uniswap_v2_event_mappings, + tokens=tokens.to_dict(orient="records"), +) + +start_time = time.time() +Parallel(n_jobs=-1, backend="threading")( + delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data +) +cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") + +mgr.deduplicate_pool_data() +cids = [pool["cid"] for pool in mgr.pool_data] +assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" +def init_bot(mgr: Manager) -> CarbonBot: + """ + Initializes the bot. + + Parameters + ---------- + mgr : Manager + The manager object. + + Returns + ------- + CarbonBot + The bot object. + """ + mgr.cfg.logger.info("Initializing the bot...") + bot = CarbonBot(ConfigObj=mgr.cfg) + bot.db = db + bot.db.mgr = mgr + assert isinstance( + bot.db, QueryInterface + ), "QueryInterface not initialized correctly" + return bot +bot = init_bot(mgr) +bot.db.handle_token_key_cleanup() +bot.db.remove_unmapped_uniswap_v2_pools() +bot.db.remove_zero_liquidity_pools() +bot.db.remove_unsupported_exchanges() +tokens = bot.db.get_tokens() +ADDRDEC = {t.key: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} +flashloan_tokens = bot.setup_flashloan_tokens(None) +CCm = bot.setup_CCm(None) +pools = db.get_pool_data_with_tokens() + +arb_mode = "multi" + + +# ------------------------------------------------------------ +# Test 043 +# File test_043_TestEmptyCarbonOrders.py +# Segment Test_Empty_Carbon_Orders_Removed +# ------------------------------------------------------------ +def test_test_empty_carbon_orders_removed(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("multi") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=arb_finder.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = r[11] + + best_trade_instructions_dic + # Check that this gets filtered out + test_trade = [{'cid': '0x36445535fc762f6c53277a667500a41e31b51bec800e76aab33dafab75da4eaa', + 'tknin': 'WBTC-C599', + 'amtin': 0.008570336169213988, + 'tknout': 'WETH-6Cc2', + 'amtout': -0.13937506393995136, + 'error': None}, + {'cid': '9187623906865338513511114400657741709420-1', + 'tknin': 'WETH-6Cc2', + 'amtin': 0, + 'tknout': 'WBTC-C599', + 'amtout': 0, + 'error': None}, + {'cid': '9187623906865338513511114400657741709458-1', + 'tknin': 'WETH-6Cc2', + 'amtin': 0.13937506393995136, + 'tknout': 'WBTC-C599', + 'amtout': 0.008870336169213988, + 'error': None}] + + ( + ordered_trade_instructions_dct, + tx_in_count, + ) = bot._simple_ordering_by_src_token( + test_trade, best_src_token + ) + ordered_scaled_dcts = bot._basic_scaling( + ordered_trade_instructions_dct, best_src_token + ) + ordered_trade_instructions_objects = bot._convert_trade_instructions(ordered_scaled_dcts) + tx_route_handler = bot.TxRouteHandlerClass( + trade_instructions=ordered_trade_instructions_objects + ) + agg_trade_instructions = ( + tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) + if bot._carbon_in_trade_route(ordered_trade_instructions_objects) + else ordered_trade_instructions_objects + ) + # Calculate the trade instructions + calculated_trade_instructions = tx_route_handler.calculate_trade_outputs( + agg_trade_instructions + ) + encoded_trade_instructions = tx_route_handler.custom_data_encoder( + calculated_trade_instructions + ) + deadline = bot._get_deadline() + + # Get the route struct + route_struct = [ + asdict(rs) + for rs in tx_route_handler.get_route_structs( + encoded_trade_instructions, deadline + ) + ] + for route in route_struct: + if route["platformId"] == 6: + encoded_trade = route["customData"].split("0x")[1] + encoded_trades = [encoded_trade[i:i+64] for i in range(0, len(encoded_trade), 64)] + for trade in encoded_trades: + assert trade != "0000000000000000000000000000000000000000000000000000000000000000", f"[TestEmptyCarbonOrders] Empty Carbon instructions not filtered out by calculate_trade_outputs" \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_047_Randomizer.py b/fastlane_bot/tests/nbtest/test_047_Randomizer.py new file mode 100644 index 000000000..b708b288c --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_047_Randomizer.py @@ -0,0 +1,225 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_047_Randomizer.py` +# ------------------------------------------------------------ +# source file = NBTest_047_Randomizer.py +# test id = 047 +# test comment = Randomizer +# ------------------------------------------------------------ + + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Bot, Config +from fastlane_bot.bot import CarbonBot +from fastlane_bot.tools.cpc import ConstantProductCurve +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC +from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 +from fastlane_bot.events.interface import QueryInterface +from fastlane_bot.helpers.poolandtokens import PoolAndTokens +from fastlane_bot.helpers import TradeInstruction, TxReceiptHandler, TxRouteHandler, TxSubmitHandler, TxHelpers, TxHelper +from fastlane_bot.events.managers.manager import Manager +from fastlane_bot.events.interface import QueryInterface +from joblib import Parallel, delayed +import pytest +import math +import json +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SushiswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) +from fastlane_bot.testing import * +from fastlane_bot.modes import triangle_single_bancor3 +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +C = cfg = Config.new(config=Config.CONFIG_MAINNET) +C.DEFAULT_MIN_PROFIT_BNT = 0.02 +C.DEFAULT_MIN_PROFIT = 0.02 +cfg.DEFAULT_MIN_PROFIT_BNT = 0.02 +cfg.DEFAULT_MIN_PROFIT = 0.02 +assert (C.NETWORK == C.NETWORK_MAINNET) +assert (C.PROVIDER == C.PROVIDER_ALCHEMY) +setup_bot = CarbonBot(ConfigObj=C) +pools = None +with open('fastlane_bot/data/tests/latest_pool_data_testing.json') as f: + pools = json.load(f) +pools = [pool for pool in pools] +pools[0] +static_pools = pools +state = pools +exchanges = list({ex['exchange_name'] for ex in state}) +db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) +setup_bot.db = db + +static_pool_data_filename = "static_pool_data" + +static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) + +uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) + +tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) + +exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" + +exchanges = exchanges.split(",") + + +alchemy_max_block_fetch = 20 +static_pool_data["cid"] = [ + cfg.w3.keccak(text=f"{row['descr']}").hex() + for index, row in static_pool_data.iterrows() + ] +static_pool_data = [ + row for index, row in static_pool_data.iterrows() + if row["exchange_name"] in exchanges +] + +static_pool_data = pd.DataFrame(static_pool_data) +static_pool_data['exchange_name'].unique() +mgr = Manager( + web3=cfg.w3, + cfg=cfg, + pool_data=static_pool_data.to_dict(orient="records"), + SUPPORTED_EXCHANGES=exchanges, + alchemy_max_block_fetch=alchemy_max_block_fetch, + uniswap_v2_event_mappings=uniswap_v2_event_mappings, + tokens=tokens.to_dict(orient="records"), +) + +start_time = time.time() +Parallel(n_jobs=-1, backend="threading")( + delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data +) +cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") + +mgr.deduplicate_pool_data() +cids = [pool["cid"] for pool in mgr.pool_data] +assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" +def init_bot(mgr: Manager) -> CarbonBot: + """ + Initializes the bot. + + Parameters + ---------- + mgr : Manager + The manager object. + + Returns + ------- + CarbonBot + The bot object. + """ + mgr.cfg.logger.info("Initializing the bot...") + bot = CarbonBot(ConfigObj=mgr.cfg) + bot.db = db + bot.db.mgr = mgr + assert isinstance( + bot.db, QueryInterface + ), "QueryInterface not initialized correctly" + return bot +bot = init_bot(mgr) +bot.db.handle_token_key_cleanup() +bot.db.remove_unmapped_uniswap_v2_pools() +bot.db.remove_zero_liquidity_pools() +bot.db.remove_unsupported_exchanges() +tokens = bot.db.get_tokens() +ADDRDEC = {t.key: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} +flashloan_tokens = bot.setup_flashloan_tokens(None) +CCm = bot.setup_CCm(None) +pools = db.get_pool_data_with_tokens() + +arb_mode = "multi" + + +# ------------------------------------------------------------ +# Test 047 +# File test_047_Randomizer.py +# Segment Test_randomizer +# ------------------------------------------------------------ +def test_test_randomizer(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("multi") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + + #arb_opp = r[0] + + + assert len(r) == 22, f"[NB047 Randomizer], expected 22 arbs, found {len(r)}" + + + arb_opp_0 = bot.randomize(arb_opps=r, randomizer=0) + arb_opp_1 = bot.randomize(arb_opps=r, randomizer=1) + arb_opp_2 = bot.randomize(arb_opps=r, randomizer=1) + arb_opp_3 = bot.randomize(arb_opps=r, randomizer=1) + arb_opp_25 = bot.randomize(arb_opps=r, randomizer=1) + arb_opp_None = bot.randomize(arb_opps=None, randomizer=5) + + assert len(arb_opp_0) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_0)}" + assert len(arb_opp_1) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_1)}" + assert len(arb_opp_2) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_2)}" + assert len(arb_opp_3) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_3)}" + assert len(arb_opp_25) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_25)}" + assert isinstance(np.float64(arb_opp_0[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_0[0])}" + assert isinstance(np.float64(arb_opp_1[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_1[0])}" + assert isinstance(np.float64(arb_opp_2[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_2[0])}" + # - + + assert isinstance(np.float64(arb_opp_3[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_3[0])}" + assert isinstance(np.float64(arb_opp_25[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_25[0])}" + + arb_opp_0[2] + + assert type(arb_opp_0[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_0[2])}" + assert type(arb_opp_1[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_1[2])}" + assert type(arb_opp_2[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_2[2])}" + assert type(arb_opp_3[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_3[2])}" + assert type(arb_opp_25[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_25[2])}" + + assert arb_opp_None == None, f"[NB047 Randomizer], expected randomizer to return None when it receives None, but it returned {type(arb_opp_None)}" + + +# ------------------------------------------------------------ +# Test 047 +# File test_047_Randomizer.py +# Segment Test_sorted_by_profit +# ------------------------------------------------------------ +def test_test_sorted_by_profit(): +# ------------------------------------------------------------ + + # + + arb_opps = [(2.6927646232907136, [{'cid': '0xe37abfaee752c24a764955cbb2d10c3c9f88472263cbd2c00ca57facb0f128fe', 'tknin': 'WETH-6Cc2', 'amtin': 0.003982724863828224, 'tknout': 'BNT-FF1C', 'amtout': -19.27862435251882, 'error': None}, {'cid': '3743106036130323098097120681749450326076-0', 'tknin': 'BNT-FF1C', 'amtin': 16.585859729228105, 'tknout': 'WETH-6Cc2', 'amtout': -0.003982724874543209, 'error': None}] + ), (2.5352758371554955, [{'cid': '0x748ab2bef0d97e5a044268626e6c9c104bab818605d44f650fdeaa03a3c742d2', 'tknin': 'WETH-6Cc2', 'amtin': 0.003982718826136988, 'tknout': 'BNT-FF1C', 'amtout': -19.1211355663836, 'error': None}, {'cid': '3743106036130323098097120681749450326076-0', 'tknin': 'BNT-FF1C', 'amtin': 16.585859729228105, 'tknout': 'WETH-6Cc2', 'amtout': -0.003982724874543209, 'error': None}] + ), (1.9702345513100596, [{'cid': '0xc4771395e1389e2e3a12ec22efbb7aff5b1c04e5ce9c7596a82e9dc8fdec725b', 'tknin': 'BNT-FF1C', 'amtin': 750.6057364856824, 'tknout': 'USDC-eB48', 'amtout': -293.5068652469199, 'error': None}, {'cid': '2381976568446569244243622252022377480332-1', 'tknin': 'USDC-eB48', 'amtin': 292.73623752593994, 'tknout': 'BNT-FF1C', 'amtout': -750.6057367324829, 'error': None}] + ), (2.67115241495777, [{'cid': '0xe37abfaee752c24a764955cbb2d10c3c9f88472263cbd2c00ca57facb0f128fe', 'tknin': 'WETH-6Cc2', 'amtin': 0.0034263543081607395, 'tknout': 'BNT-FF1C', 'amtout': -16.58585974665766, 'error': None}, {'cid': '3743106036130323098097120681749450326076-0', 'tknin': 'BNT-FF1C', 'amtin': 16.585859729228105, 'tknout': 'WETH-6Cc2', 'amtout': -0.003982724874543209, 'error': None}] + ), (2.535310217715329, [{'cid': '0x748ab2bef0d97e5a044268626e6c9c104bab818605d44f650fdeaa03a3c742d2', 'tknin': 'WETH-6Cc2', 'amtin': 0.003454648687693407, 'tknout': 'BNT-FF1C', 'amtout': -16.58585971966386, 'error': None}, {'cid': '3743106036130323098097120681749450326076-0', 'tknin': 'BNT-FF1C', 'amtin': 16.585859729228105, 'tknout': 'WETH-6Cc2', 'amtout': -0.003982724874543209, 'error': None}] + ), (5.438084583685771, [{'cid': '0x8f9771f2886aa12c1659c275b8e305f58c7c41ba82df03bb21c0bcac98ffde4b', 'tknin': 'WETH-6Cc2', 'amtin': 0.002847350733645726, 'tknout': 'HEX-eb39', 'amtout': -556.3312638401985, 'error': None}, {'cid': '14291859410679415465461733512134264881242-0', 'tknin': 'HEX-eb39', 'amtin': 556.3312644516602, 'tknout': 'WETH-6Cc2', 'amtout': -0.003980041696137606, 'error': None}] + ), (5.400385044154462, [{'cid': '0x3a98798837e610ac07762e2d58f29f0cf96297a2528f86e0fe9b903b1e45a204', 'tknin': 'WETH-6Cc2', 'amtin': 0.0028413006787388895, 'tknout': 'HEX-eb39', 'amtout': -553.6187023743987, 'error': None}, {'cid': '14291859410679415465461733512134264881242-0', 'tknin': 'HEX-eb39', 'amtin': 553.6187027173414, 'tknout': 'WETH-6Cc2', 'amtout': -0.003966139257351835, 'error': None}] + ), (1.9713220433332026, [{'cid': '0xc4771395e1389e2e3a12ec22efbb7aff5b1c04e5ce9c7596a82e9dc8fdec725b', 'tknin': 'BNT-FF1C', 'amtin': 748.6344146891497, 'tknout': 'USDC-eB48', 'amtout': -292.73623879346997, 'error': None}, {'cid': '2381976568446569244243622252022377480332-1', 'tknin': 'USDC-eB48', 'amtin': 292.73623752593994, 'tknout': 'BNT-FF1C', 'amtout': -750.6057367324829, 'error': None}] + ), (8.465616944048316, [{'cid': '0x5b5f170977fe879c965a9fec9aeba4dfe29659f503cd5fe6e67349bdc3089295', 'tknin': '0x0-1AD5', 'amtin': 359.7323400862515, 'tknout': 'WETH-6Cc2', 'amtout': -0.0070300615800533706, 'error': None}, {'cid': '9868188640707215440437863615521278132277-1', 'tknin': 'WETH-6Cc2', 'amtin': 0.00526677017535393, 'tknout': '0x0-1AD5', 'amtout': -359.73234041399974, 'error': None}] + ), (6.717558869249757, [{'cid': '0x1eda42a2cced5e9cfffe1b15d7c39253514267401c5bd2e9ca28287f8a996fde', 'tknin': 'rETH-6393', 'amtin': 0.2496827895520255, 'tknout': 'WETH-6Cc2', 'amtout': -0.26914170442614704, 'error': None}, {'cid': '3062541302288446171170371466885913903202-0', 'tknin': 'WETH-6Cc2', 'amtin': 0.267742513570596, 'tknout': 'rETH-6393', 'amtout': -0.2496827897163172, 'error': None}] + )] + + ops = bot.randomize(arb_opps=arb_opps, randomizer=3) + + assert iseq(ops[0], 8.465616944048316) or iseq(ops[0], 6.717558869249757) or iseq(ops[0], 5.438084583685771), f"[NB047 Randomizer], expected randomizer to return top 3 most profitable arbs, but it did not!" + # - + + \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_901_TestMultiTriangleModeSlow.py b/fastlane_bot/tests/nbtest/test_901_TestMultiTriangleModeSlow.py new file mode 100644 index 000000000..6604f208e --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_901_TestMultiTriangleModeSlow.py @@ -0,0 +1,247 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_901_TestMultiTriangleModeSlow.py` +# ------------------------------------------------------------ +# source file = NBTest_901_TestMultiTriangleModeSlow.py +# test id = 901 +# test comment = TestMultiTriangleModeSlow +# ------------------------------------------------------------ + + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Bot, Config +from fastlane_bot.bot import CarbonBot +from fastlane_bot.tools.cpc import ConstantProductCurve +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC +from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 +from fastlane_bot.events.interface import QueryInterface +from fastlane_bot.helpers.poolandtokens import PoolAndTokens +from fastlane_bot.helpers import TradeInstruction, TxReceiptHandler, TxRouteHandler, TxSubmitHandler, TxHelpers, TxHelper +from fastlane_bot.events.managers.manager import Manager +from fastlane_bot.events.interface import QueryInterface +from joblib import Parallel, delayed +import pytest +import math +import json +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SushiswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) +from fastlane_bot.testing import * +from fastlane_bot.modes import triangle_single_bancor3 +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +C = cfg = Config.new(config=Config.CONFIG_MAINNET) +C.DEFAULT_MIN_PROFIT_BNT = 0.02 +C.DEFAULT_MIN_PROFIT = 0.02 +cfg.DEFAULT_MIN_PROFIT_BNT = 0.02 +cfg.DEFAULT_MIN_PROFIT = 0.02 +assert (C.NETWORK == C.NETWORK_MAINNET) +assert (C.PROVIDER == C.PROVIDER_ALCHEMY) +setup_bot = CarbonBot(ConfigObj=C) +pools = None +with open('fastlane_bot/data/tests/latest_pool_data_testing.json') as f: + pools = json.load(f) +pools = [pool for pool in pools] +pools[0] +static_pools = pools +state = pools +exchanges = list({ex['exchange_name'] for ex in state}) +db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) +setup_bot.db = db + +static_pool_data_filename = "static_pool_data" + +static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) + +uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) + +tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) + +exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" + +exchanges = exchanges.split(",") + + +alchemy_max_block_fetch = 20 +static_pool_data["cid"] = [ + cfg.w3.keccak(text=f"{row['descr']}").hex() + for index, row in static_pool_data.iterrows() + ] +static_pool_data = [ + row for index, row in static_pool_data.iterrows() + if row["exchange_name"] in exchanges +] + +static_pool_data = pd.DataFrame(static_pool_data) +static_pool_data['exchange_name'].unique() +mgr = Manager( + web3=cfg.w3, + cfg=cfg, + pool_data=static_pool_data.to_dict(orient="records"), + SUPPORTED_EXCHANGES=exchanges, + alchemy_max_block_fetch=alchemy_max_block_fetch, + uniswap_v2_event_mappings=uniswap_v2_event_mappings, + tokens=tokens.to_dict(orient="records"), +) + +start_time = time.time() +Parallel(n_jobs=-1, backend="threading")( + delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data +) +cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") + +mgr.deduplicate_pool_data() +cids = [pool["cid"] for pool in mgr.pool_data] +assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" +def init_bot(mgr: Manager) -> CarbonBot: + """ + Initializes the bot. + + Parameters + ---------- + mgr : Manager + The manager object. + + Returns + ------- + CarbonBot + The bot object. + """ + mgr.cfg.logger.info("Initializing the bot...") + bot = CarbonBot(ConfigObj=mgr.cfg) + bot.db = db + bot.db.mgr = mgr + assert isinstance( + bot.db, QueryInterface + ), "QueryInterface not initialized correctly" + return bot +bot = init_bot(mgr) +bot.db.handle_token_key_cleanup() +bot.db.remove_unmapped_uniswap_v2_pools() +bot.db.remove_zero_liquidity_pools() +bot.db.remove_unsupported_exchanges() +tokens = bot.db.get_tokens() +ADDRDEC = {t.key: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} +flashloan_tokens = bot.setup_flashloan_tokens(None) +CCm = bot.setup_CCm(None) +pools = db.get_pool_data_with_tokens() + +arb_mode = "multi_triangle" + + +# ------------------------------------------------------------ +# Test 901 +# File test_901_TestMultiTriangleModeSlow.py +# Segment Test_min_profit +# ------------------------------------------------------------ +def test_test_min_profit(): +# ------------------------------------------------------------ + + assert(cfg.DEFAULT_MIN_PROFIT_BNT <= 0.02), f"[TestMultiMode], DEFAULT_MIN_PROFIT_BNT must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_BNT}" + assert(C.DEFAULT_MIN_PROFIT_BNT <= 0.02), f"[TestMultiMode], DEFAULT_MIN_PROFIT_BNT must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_BNT}" + assert bot.ConfigObj.DEFAULT_MIN_PROFIT_BNT == 0.02 + + # ### Test_arb_mode_class + + arb_finder = bot._get_arb_finder("multi_triangle") + assert arb_finder.__name__ == "ArbitrageFinderTriangleMulti", f"[TestMultiMode] Expected arb_finder class name name = FindArbitrageMultiPairwise, found {arb_finder.__name__}" + + +# ------------------------------------------------------------ +# Test 901 +# File test_901_TestMultiTriangleModeSlow.py +# Segment Test_combos +# ------------------------------------------------------------ +def test_test_combos(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("multi_triangle") + finder2 = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_TOKENS, + ConfigObj=bot.ConfigObj, + ) + combos = finder2.get_combos(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode="multi_triangle") + assert len(combos) == 1370, f"[TestMultiMode] Using wrong dataset, expected 1370 combos, found {len(combos)}" + + # ### Test_find_arbitrage + + run_full = bot._run(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode=arb_mode, data_validator=False, result=bot.XS_ARBOPPS) + arb_finder = bot._get_arb_finder("multi_triangle") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + assert len(r) == 40, f"[TestMultiMode] Expected 40 arbs, found {len(r)}" + assert len(r) == len(run_full), f"[TestMultiMode] Expected arbs from .find_arbitrage: {len(r)} - to match _run: {len(run_full)}" + + # ### Test_multi_carbon_pools + + arb_finder = bot._get_arb_finder("multi_triangle") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + multi_carbon_count = 0 + for arb in r: + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = arb + if len(best_trade_instructions_dic) > 3: + multi_carbon_count += 1 + assert multi_carbon_count > 0, f"[TestMultiMode] Not finding arbs with multiple Carbon curves." + + # ### Test_mono_direction_carbon_curves + + arb_finder = bot._get_arb_finder("multi_triangle") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + for arb in r: + ( + best_profit, + best_trade_instructions_df, + best_trade_instructions_dic, + best_src_token, + best_trade_instructions, + ) = arb + if len(best_trade_instructions_dic) > 3: + + has_zero_curves = False + has_one_curves = False + for curve in best_trade_instructions_dic: + if "-0" in curve['cid']: + has_zero_curves = True + if "-1" in curve['cid']: + has_one_curves = True + assert not has_zero_curves or not has_one_curves, f"[TestMultiMode] Finding Carbon curves in opposite directions - not supported in this mode." \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_902_ValidatorSlow.py b/fastlane_bot/tests/nbtest/test_902_ValidatorSlow.py new file mode 100644 index 000000000..0b5ac3fd1 --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_902_ValidatorSlow.py @@ -0,0 +1,283 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_902_ValidatorSlow.py` +# ------------------------------------------------------------ +# source file = NBTest_902_ValidatorSlow.py +# test id = 902 +# test comment = ValidatorSlow +# ------------------------------------------------------------ + + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Bot, Config +from fastlane_bot.bot import CarbonBot +from fastlane_bot.tools.cpc import ConstantProductCurve +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC +from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, SushiswapV2, CarbonV1, BancorV3 +from fastlane_bot.events.interface import QueryInterface +from fastlane_bot.helpers.poolandtokens import PoolAndTokens +from fastlane_bot.helpers import TradeInstruction, TxReceiptHandler, TxRouteHandler, TxSubmitHandler, TxHelpers, TxHelper +from fastlane_bot.events.managers.manager import Manager +from fastlane_bot.events.interface import QueryInterface +from joblib import Parallel, delayed +import pytest +import math +import json +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SushiswapV2)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) +from fastlane_bot.testing import * +from fastlane_bot.modes import triangle_single_bancor3 +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +C = cfg = Config.new(config=Config.CONFIG_MAINNET) +C.DEFAULT_MIN_PROFIT_BNT = 0.02 +C.DEFAULT_MIN_PROFIT = 0.02 +cfg.DEFAULT_MIN_PROFIT_BNT = 0.02 +cfg.DEFAULT_MIN_PROFIT = 0.02 +assert (C.NETWORK == C.NETWORK_MAINNET) +assert (C.PROVIDER == C.PROVIDER_ALCHEMY) +setup_bot = CarbonBot(ConfigObj=C) +pools = None +with open('fastlane_bot/data/tests/latest_pool_data_testing.json') as f: + pools = json.load(f) +pools = [pool for pool in pools] +pools[0] +static_pools = pools +state = pools +exchanges = list({ex['exchange_name'] for ex in state}) +db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) +setup_bot.db = db + +static_pool_data_filename = "static_pool_data" + +static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) + +uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) + +tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) + +exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" + +exchanges = exchanges.split(",") + + +alchemy_max_block_fetch = 20 +static_pool_data["cid"] = [ + cfg.w3.keccak(text=f"{row['descr']}").hex() + for index, row in static_pool_data.iterrows() + ] +static_pool_data = [ + row for index, row in static_pool_data.iterrows() + if row["exchange_name"] in exchanges +] + +static_pool_data = pd.DataFrame(static_pool_data) +static_pool_data['exchange_name'].unique() +mgr = Manager( + web3=cfg.w3, + cfg=cfg, + pool_data=static_pool_data.to_dict(orient="records"), + SUPPORTED_EXCHANGES=exchanges, + alchemy_max_block_fetch=alchemy_max_block_fetch, + uniswap_v2_event_mappings=uniswap_v2_event_mappings, + tokens=tokens.to_dict(orient="records"), +) + +start_time = time.time() +Parallel(n_jobs=-1, backend="threading")( + delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data +) +cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") + +mgr.deduplicate_pool_data() +cids = [pool["cid"] for pool in mgr.pool_data] +assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" +def init_bot(mgr: Manager) -> CarbonBot: + """ + Initializes the bot. + + Parameters + ---------- + mgr : Manager + The manager object. + + Returns + ------- + CarbonBot + The bot object. + """ + mgr.cfg.logger.info("Initializing the bot...") + bot = CarbonBot(ConfigObj=mgr.cfg) + bot.db = db + bot.db.mgr = mgr + assert isinstance( + bot.db, QueryInterface + ), "QueryInterface not initialized correctly" + return bot +bot = init_bot(mgr) +bot.db.handle_token_key_cleanup() +bot.db.remove_unmapped_uniswap_v2_pools() +bot.db.remove_zero_liquidity_pools() +bot.db.remove_unsupported_exchanges() +tokens = bot.db.get_tokens() +ADDRDEC = {t.key: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} +flashloan_tokens = bot.setup_flashloan_tokens(None) +CCm = bot.setup_CCm(None) +pools = db.get_pool_data_with_tokens() + +arb_mode = "multi" + + +# ------------------------------------------------------------ +# Test 902 +# File test_902_ValidatorSlow.py +# Segment Test_MIN_PROFIT +# ------------------------------------------------------------ +def test_test_min_profit(): +# ------------------------------------------------------------ + + assert(cfg.DEFAULT_MIN_PROFIT_BNT <= 0.02), f"[TestMultiMode], DEFAULT_MIN_PROFIT_BNT must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_BNT}" + assert(C.DEFAULT_MIN_PROFIT_BNT <= 0.02), f"[TestMultiMode], DEFAULT_MIN_PROFIT_BNT must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_BNT}" + + +# ------------------------------------------------------------ +# Test 902 +# File test_902_ValidatorSlow.py +# Segment Test_validator_in_out +# ------------------------------------------------------------ +def test_test_validator_in_out(): +# ------------------------------------------------------------ + + arb_finder = bot._get_arb_finder("multi") + assert arb_finder.__name__ == "FindArbitrageMultiPairwise", f"[TestMultiMode] Expected arb_finder class name name = FindArbitrageMultiPairwise, found {arb_finder.__name__}" + + +# ------------------------------------------------------------ +# Test 902 +# File test_902_ValidatorSlow.py +# Segment Test_validator_multi +# ------------------------------------------------------------ +def test_test_validator_multi(): +# ------------------------------------------------------------ + + # + + arb_finder = bot._get_arb_finder("multi") + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + + arb_opp = r[0] + + validated = bot.validate_optimizer_trades(arb_opp=arb_opp, arb_mode="multi", arb_finder=finder) + + + + assert arb_opp == validated + + # - + + +# ------------------------------------------------------------ +# Test 902 +# File test_902_ValidatorSlow.py +# Segment Test_validator_single +# ------------------------------------------------------------ +def test_test_validator_single(): +# ------------------------------------------------------------ + + # + + arb_mode="single" + arb_finder = bot._get_arb_finder(arb_mode) + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + + arb_opp = r[0] + + validated = bot.validate_optimizer_trades(arb_opp=arb_opp, arb_mode=arb_mode, arb_finder=finder) + + + assert arb_opp == validated + # - + + +# ------------------------------------------------------------ +# Test 902 +# File test_902_ValidatorSlow.py +# Segment Test_validator_bancor_v3 +# ------------------------------------------------------------ +def test_test_validator_bancor_v3(): +# ------------------------------------------------------------ + + # + + arb_mode="bancor_v3" + + arb_finder = bot._get_arb_finder(arb_mode) + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + + arb_opp = r[0] + + validated = bot.validate_optimizer_trades(arb_opp=arb_opp, arb_mode=arb_mode, arb_finder=finder) + + + + assert arb_opp != validated + # - + + +# ------------------------------------------------------------ +# Test 902 +# File test_902_ValidatorSlow.py +# Segment Test_validator_multi_triangle +# ------------------------------------------------------------ +def test_test_validator_multi_triangle(): +# ------------------------------------------------------------ + + # + + arb_mode="multi_triangle" + arb_finder = bot._get_arb_finder(arb_mode) + finder = arb_finder( + flashloan_tokens=flashloan_tokens, + CCm=CCm, + mode="bothin", + result=bot.AO_CANDIDATES, + ConfigObj=bot.ConfigObj, + ) + r = finder.find_arbitrage() + + arb_opp = r[0] + + validated = bot.validate_optimizer_trades(arb_opp=arb_opp, arb_mode=arb_mode, arb_finder=finder) + + + + assert arb_opp == validated \ No newline at end of file diff --git a/fastlane_bot/tests/nbtest/test_903_OptimizerDetailedSlow.py b/fastlane_bot/tests/nbtest/test_903_OptimizerDetailedSlow.py new file mode 100644 index 000000000..48703bc21 --- /dev/null +++ b/fastlane_bot/tests/nbtest/test_903_OptimizerDetailedSlow.py @@ -0,0 +1,685 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_903_OptimizerDetailedSlow.py` +# ------------------------------------------------------------ +# source file = NBTest_903_OptimizerDetailedSlow.py +# test id = 903 +# test comment = OptimizerDetailedSlow +# ------------------------------------------------------------ + + + +#from fastlane_bot import Bot, Config, ConfigDB, ConfigNetwork, ConfigProvider +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, CPCContainer, T, Pair +from fastlane_bot.tools.analyzer import CPCAnalyzer +from fastlane_bot.tools.optimizer import SimpleOptimizer, MargPOptimizer, ConvexOptimizer +from fastlane_bot.tools.arbgraphs import ArbGraph +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPCAnalyzer)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(SimpleOptimizer)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(MargPOptimizer)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(ConvexOptimizer)) +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(ArbGraph)) +#print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) +from fastlane_bot.testing import * +import itertools as it +import collections as cl +#plt.style.use('seaborn-dark') +plt.rcParams['figure.figsize'] = [12,6] +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + + + +try: + CCm = CPCContainer.from_df(pd.read_csv("_data/NBTest_006.csv.gz")) +except: + CCm = CPCContainer.from_df(pd.read_csv("fastlane_bot/tests/nbtest/_data/NBTest_006.csv.gz")) + +CCu3 = CCm.byparams(exchange="uniswap_v3") +CCu2 = CCm.byparams(exchange="uniswap_v2") +CCs2 = CCm.byparams(exchange="sushiswap_v2") +CCc1 = CCm.byparams(exchange="carbon_v1") +tc_u3 = CCu3.token_count(asdict=True) +tc_u2 = CCu2.token_count(asdict=True) +tc_s2 = CCs2.token_count(asdict=True) +tc_c1 = CCc1.token_count(asdict=True) +CAm = CPCAnalyzer(CCm) +#CCm.asdf().to_csv("A011-test.csv.gz", compression = "gzip") + +CA = CAm +pairs0 = CA.CC.pairs(standardize=False) +pairs = CA.pairs() +pairsc = CA.pairsc() +tokens = CA.tokens() + + +# ------------------------------------------------------------ +# Test 903 +# File test_903_OptimizerDetailedSlow.py +# Segment Market structure analysis [NOTEST] +# ------------------------------------------------------------ +def notest_market_structure_analysis(): +# ------------------------------------------------------------ + + print(f"Total pairs: {len(pairs0):4}") + print(f"Primary pairs: {len(pairs):4}") + print(f"...carbon: {len(pairsc):4}") + print(f"Tokens: {len(CA.tokens()):4}") + print(f"Curves: {len(CCm):4}") + + CA.count_by_pairs() + + CA.count_by_pairs(minn=2) + + # ### All crosses + + CCx = CCm.bypairs( + CCm.filter_pairs(notin=f"{T.ETH},{T.USDC},{T.USDT},{T.BNT},{T.DAI},{T.WBTC}") + ) + len(CCx), CCx.token_count()[:10] + + AGx=ArbGraph.from_cc(CCx) + AGx.plot(labels=False, node_size=50, node_color="#fcc")._ + + # ### Biggest crosses (HEX, UNI, ICHI, FRAX) + + CCx2 = CCx.bypairs( + CCx.filter_pairs(onein=f"{T.HEX}, {T.UNI}, {T.ICHI}, {T.FRAX}") + ) + ArbGraph.from_cc(CCx2).plot() + len(CCx2) + + # ### Carbon + + ArbGraph.from_cc(CCc1).plot()._ + + len(CCc1), len(CCc1.tokens()) + + CCc1.token_count() + + + len(CCc1.pairs()), CCc1.pairs() + + # ### Token subsets + + O = MargPOptimizer(CCm.bypairs( + CCm.filter_pairs(bothin=f"{T.ETH},{T.USDC},{T.USDT},{T.BNT},{T.DAI},{T.WBTC}") + )) + r = O.margp_optimizer(f"{T.USDC}", params=dict(verbose=False, debug=False)) + r.trade_instructions(ti_format=O.TIF_DFAGGR).fillna("") + + # + + #r.trade_instructions(ti_format=O.TIF_DFAGGR).fillna("").to_excel("ti.xlsx") + # - + + ArbGraph.from_r(r).plot()._ + + # + + #O.CC.plot() + # - + + +# ------------------------------------------------------------ +# Test 903 +# File test_903_OptimizerDetailedSlow.py +# Segment Tests +# ------------------------------------------------------------ +def test_tests(): +# ------------------------------------------------------------ + + CA = CAm + + # ### General tests + + # #### General data integrity (should ALWAYS hold) + + assert len(pairs0) > 2500 + assert len(pairs) > 2500 + assert len(pairs0) > len(pairs) + assert len(pairsc) > 10 + assert len(CCm.tokens()) > 2000 + assert len(CCm)>4000 + assert len(CCm.filter_pairs(onein=f"{T.ETH}")) > 1900 # ETH pairs + assert len(CCm.filter_pairs(onein=f"{T.USDC}")) > 300 # USDC pairs + assert len(CCm.filter_pairs(onein=f"{T.USDT}")) > 190 # USDT pairs + assert len(CCm.filter_pairs(onein=f"{T.DAI}")) > 50 # DAI pairs + assert len(CCm.filter_pairs(onein=f"{T.WBTC}")) > 30 # WBTC pairs + + xis0 = {c.cid: (c.x, c.y) for c in CCm if c.x==0} + yis0 = {c.cid: (c.x, c.y) for c in CCm if c.y==0} + assert len(xis0) == 0 # set loglevel debug to see removal of curves + assert len(yis0) == 0 + + # #### Data integrity + + assert len(CCm) == 4155 + assert len(CCu3) == 1411 + assert len(CCu2) == 2177 + assert len(CCs2) == 236 + assert len(CCm.tokens()) == 2233 + assert len(CCm.pairs()) == 2834 + assert len(CCm.pairs(standardize=False)) == 2864 + + + assert CA.pairs() == CCm.pairs(standardize=True) + assert CA.pairsc() == {c.pairo.primary for c in CCm if c.P("exchange")=="carbon_v1"} + assert CA.tokens() == CCm.tokens() + + # #### prices + + r1 = CCc1.prices(result=CCc1.PR_TUPLE) + r2 = CCc1.prices(result=CCc1.PR_TUPLE, primary=False) + r3 = CCc1.prices(result=CCc1.PR_TUPLE, primary=False, inclpair=False) + assert isinstance(r1, tuple) + assert isinstance(r2, tuple) + assert isinstance(r3, tuple) + assert len(r1) == len(r2) + assert len(r1) == len(r3) + assert len(r1[0]) == 3 + assert isinstance(r1[0][0], str) + assert isinstance(r1[0][1], float) + assert len(r1[0][2].split("/"))==2 + + r2[:2] + + r3[:2] + + r1a = CCc1.prices(result=CCc1.PR_DICT) + r2a = CCc1.prices(result=CCc1.PR_DICT, primary=False) + r3a = CCc1.prices(result=CCc1.PR_DICT, primary=False, inclpair=False) + assert isinstance(r1a, dict) + assert isinstance(r2a, dict) + assert isinstance(r3a, dict) + assert len(r1a) == len(r1) + assert len(r1a) == len(r2a) + assert len(r1a) == len(r3a) + assert list(r1a.keys()) == list(x[0] for x in r1) + assert r1a.keys() == r2a.keys() + assert r1a.keys() == r3a.keys() + assert set(len(x) for x in r1a.values()) == {2}, "all records must be of of length 2" + assert set(type(x[0]) for x in r1a.values()) == {float}, "all records must have first type float" + assert set(type(x[1]) for x in r1a.values()) == {str}, "all records must have second type str" + assert tuple(r3a.values()) == r3 + + df = CCc1.prices(result=CCc1.PR_DF, primary=False) + assert len(df) == len(r1) + assert tuple(df.index) == tuple(x[0] for x in r1) + assert tuple(df["price"]) == r3 + df + + # #### more prices + + CCt = CCm.bypairs(f"{T.USDC}/{T.ETH}") + + r = CCt.prices(result=CCt.PR_TUPLE) + assert isinstance(r, tuple) + assert len(r) == len(CCt) + assert r[0] == ('6c988ffdc9e74acd97ccfb16dd65c110', 1833.9007005259564, 'WETH-6Cc2/USDC-eB48') + assert CCt.prices() == CCt.prices(result=CCt.PR_DICT) + r = CCt.prices(result=CCt.PR_DICT) + assert len(r) == len(CCt) + assert isinstance(r, dict) + assert r['6c988ffdc9e74acd97ccfb16dd65c110'] == (1833.9007005259564, 'WETH-6Cc2/USDC-eB48') + df = CCt.prices(result=CCt.PR_DF) + assert len(df) == len(CCt) + assert tuple(df.loc["1701411834604692317316873037158841057339-0"]) == (1799.9999997028303, 'WETH-6Cc2/USDC-eB48') + + # #### price_ranges + + CCt = CCm.bypairs(f"{T.USDC}/{T.ETH}") + CAt = CPCAnalyzer(CCt) + + r = CAt.price_ranges(result=CAt.PR_TUPLE) + assert len(r) == len(CCt) + assert r[0] == ( + 'WETH/USDC', + '16dd65c110', + 'sushiswap_v2', + 'b', + 's', + None, + None, + 1833.9007005259564 + ) + assert r[1] == ( + 'WETH/USDC', + '41057334-0', + 'carbon_v1', + 'b', + '', + 1699.999829864358, + 1700.000169864341, + 1700.000169864341 + ) + r = CAt.price_ranges(result=CAt.PR_TUPLE, short=False) + assert r[0] == ( + 'WETH-6Cc2/USDC-eB48', + '6c988ffdc9e74acd97ccfb16dd65c110', + 'sushiswap_v2', + 'b', + 's', + None, + None, + 1833.9007005259564 + ) + r = CAt.price_ranges(result=CAt.PR_DICT) + assert len(r) == len(CCt) + assert r['6c988ffdc9e74acd97ccfb16dd65c110'] == ( + 'WETH/USDC', + '16dd65c110', + 'sushiswap_v2', + 'b', + 's', + None, + None, + 1833.9007005259564 + ) + df = CAt.price_ranges(result=CAt.PR_DF) + assert len(df) == len(CCt) + assert tuple(df.index.names) == ('pair', 'exch', 'cid') + assert tuple(df.columns) == ('b', 's', 'p_min', 'p_max', 'p_marg') + assert set(df["p_marg"]) == set(x[-1] for x in CAt.price_ranges(result=CCt.PR_TUPLE)) + for p1, p2 in zip(df["p_marg"], df["p_marg"][1:]): + assert p2 >= p1 + df + + # #### count_by_pairs + + assert len(CA.count_by_pairs()) == len(CA.pairs()) + assert sum(CA.count_by_pairs()["count"])==len(CA.CC) + assert np.all(CA.count_by_pairs() == CA.count_by_pairs(asdf=True)) + assert len(CA.count_by_pairs()) == len(CA.count_by_pairs(asdf=False)) + assert type(CA.count_by_pairs()).__name__ == "DataFrame" + assert type(CA.count_by_pairs(asdf=False)).__name__ == "list" + assert type(CA.count_by_pairs(asdf=False)[0]).__name__ == "tuple" + for i in range(10): + assert len(CA.count_by_pairs(minn=i)) >= len(CA.count_by_pairs(minn=i)), f"failed {i}" + + # #### count_by_tokens + + r = CA.count_by_tokens() + assert len(r) == len(CA.tokens()) + assert sum(r["total"]) == 2*len(CA.CC) + assert tuple(r["total"]) == tuple(x[1] for x in CA.CC.token_count()) + for ix, row in r[:10].iterrows(): + assert row[0] >= sum(row[1:]), f"failed at {ix} {tuple(row)}" + CA.count_by_tokens() + + # #### pool_arbitrage_statistics + + pas = CAm.pool_arbitrage_statistics() + assert np.all(pas == CAm.pool_arbitrage_statistics(CAm.POS_DF)) + assert len(pas)==165 + assert list(pas.columns) == ['price', 'vl', 'itm', 'b', 's', 'bsv'] + assert list(pas.index.names) == ['pair', 'exchange', 'cid0'] + assert {x[0] for x in pas.index} == {Pair.n(x) for x in CAm.pairsc()} + assert {x[1] for x in pas.index} == {'bancor_v2', 'bancor_v3','carbon_v1','sushiswap_v2','uniswap_v2','uniswap_v3'} + pas + + pasd = CAm.pool_arbitrage_statistics(CAm.POS_DICT) + assert isinstance(pasd, dict) + assert len(pasd) == 26 + assert len(pasd['WETH-6Cc2/DAI-1d0F']) == 7 + pd0 = pasd['WETH-6Cc2/DAI-1d0F'][0] + assert pd0[:2] == ('WETH/DAI', 'WETH-6Cc2/DAI-1d0F') + assert iseq(pd0[2], 1840.1216491367131) + assert pd0[3:6] == ('594', '594', 'uniswap_v3') + assert iseq(pd0[6], 8.466598820198278) + assert pd0[7:] == ('', 'b', 's', 'buy-sell-WETH @ 1840.12 DAI per WETH') + pd0 + + pasl = CAm.pool_arbitrage_statistics(result = CAm.POS_LIST) + assert isinstance(pasl, tuple) + assert len(pasl) == len(pas) + pd0 = [(ix, x) for ix, x in enumerate(pasl) if x[2]==1840.1216491367131] + pd0 = pasl[pd0[0][0]] + assert pd0[:2] == ('WETH/DAI', 'WETH-6Cc2/DAI-1d0F') + assert iseq(pd0[2], 1840.1216491367131) + assert pd0[3:6] == ('594', '594', 'uniswap_v3') + assert iseq(pd0[6], 8.466598820198278) + assert pd0[7:] == ('', 'b', 's', 'buy-sell-WETH @ 1840.12 DAI per WETH') + pd0 + + # ### MargP Optimizer + + # #### margp optimizer + + tokenlist = f"{T.ETH},{T.USDC},{T.USDT},{T.BNT},{T.DAI},{T.WBTC}" + targettkn = f"{T.USDC}" + O = MargPOptimizer(CCm.bypairs(CCm.filter_pairs(bothin=tokenlist))) + r = O.margp_optimizer(targettkn, params=dict(verbose=False, debug=False)) + r.trade_instructions(ti_format=O.TIF_DFAGGR).fillna("") + + # #### MargpOptimizerResult + + assert type(r) == MargPOptimizer.MargpOptimizerResult + assert iseq(r.result, -4606.010157294979) + assert r.time > 0.001 + assert r.time < 0.1 + assert r.method == O.METHOD_MARGP + assert r.targettkn == targettkn + assert set(r.tokens_t)==set(['USDT-1ec7', 'WETH-6Cc2', 'WBTC-C599', 'DAI-1d0F', 'BNT-FF1C']) + p_opt_d0 = {t:x for x, t in zip(r.p_optimal_t, r.tokens_t)} + p_opt_d = {t:round(x,6) for x, t in zip(r.p_optimal_t, r.tokens_t)} + print("optimal p", p_opt_d) + assert p_opt_d == {'WETH-6Cc2': 1842.67228, 'WBTC-C599': 27604.143472, + 'BNT-FF1C': 0.429078, 'USDT-1ec7': 1.00058, 'DAI-1d0F': 1.000179} + assert r.p_optimal[r.targettkn] == 1 + po = [(k,v) for k,v in r.p_optimal.items()][:-1] + assert len(po)==len(r.p_optimal_t) + for k,v in po: + assert p_opt_d0[k] == v, f"error at {k}, {v}, {p_opt_d0[k]}" + + # #### TradeInstructions + + assert r.trade_instructions() == r.trade_instructions(ti_format=O.TIF_OBJECTS) + ti = r.trade_instructions(ti_format=O.TIF_OBJECTS) + cids = tuple(ti_.cid for ti_ in ti) + assert isinstance(ti, tuple) + assert len(ti) == 86 + ti0=[x for x in ti if x.cid=="357"] + assert len(ti0)==1 + ti0=ti0[0] + assert ti0.cid == ti0.curve.cid + assert type(ti0).__name__ == "TradeInstruction" + assert type(ti[0]) == MargPOptimizer.TradeInstruction + assert ti0.tknin == f"{T.USDT}" + assert ti0.tknout == f"{T.USDC}" + assert round(ti0.amtin, 8) == 1214.45596849 + assert round(ti0.amtout, 8) == -1216.41933959 + assert ti0.error is None + ti[:2] + + tid = r.trade_instructions(ti_format=O.TIF_DICTS) + assert isinstance(tid, tuple) + assert len(tid) == len(ti) + tid0=[x for x in tid if x["cid"]=="357"] + assert len(tid0)==1 + tid0=tid0[0] + assert type(tid0)==dict + assert tid0["tknin"] == f"{T.USDT}" + assert tid0["tknout"] == f"{T.USDC}" + assert round(tid0["amtin"], 8) == 1214.45596849 + assert round(tid0["amtout"], 8) == -1216.41933959 + assert tid0["error"] is None + tid[:2] + + df = r.trade_instructions(ti_format=O.TIF_DF).fillna("") + assert tuple(df.index) == cids + assert np.all(r.trade_instructions(ti_format=O.TIF_DFRAW).fillna("")==df) + assert len(df) == len(ti) + assert list(df.columns)[:4] == ['pair', 'pairp', 'tknin', 'tknout'] + assert len(df.columns) == 4 + len(r.tokens_t) + 1 + tif0 = dict(df.loc["357"]) + assert tif0["pair"] == "USDC-eB48/USDT-1ec7" + assert tif0["pairp"] == "USDC/USDT" + assert tif0["tknin"] == tid0["tknin"] + assert tif0[tif0["tknin"]] == tid0["amtin"] + assert tif0[tif0["tknout"]] == tid0["amtout"] + df[:2] + + dfa = r.trade_instructions(ti_format=O.TIF_DFAGGR).fillna("") + assert tuple(dfa.index)[:-4] == cids + assert len(dfa) == len(df)+4 + assert len(dfa.columns) == len(r.tokens_t) + 1 + assert set(dfa.columns) == set(r.tokens_t).union(set([r.targettkn])) + assert list(dfa.index)[-4:] == ['PRICE', 'AMMIn', 'AMMOut', 'TOTAL NET'] + dfa[:10] + + dfpg = r.trade_instructions(ti_format=O.TIF_DFPG) + assert set(x[1] for x in dfpg.index) == set(cids) + assert np.all(dfpg["gain_tknq"]>=0) + assert np.all(dfpg["gain_r"]>=0) + assert round(np.max(dfpg["gain_r"]),8) == 0.04739068 + assert round(np.min(dfpg["gain_r"]),8) == 1.772e-05 + assert len(dfpg) == len(ti) + for p, t in zip(tuple(dfpg["pair"]), tuple(dfpg["tknq"])): + assert p.split("/")[1] == t, f"error in {p} [{t}]" + print(f"total gains: {sum(dfpg['gain_ttkn']):,.2f} {r.targettkn} [result={-r.result:,.2f}]") + assert abs(sum(dfpg["gain_ttkn"])/r.result+1)<0.01 + dfpg[:10] + + # ### Convex Optimizer + + tokens = f"{T.DAI},{T.USDT},{T.HEX},{T.WETH},{T.LINK}" + CCo = CCu2.bypairs(CCu2.filter_pairs(bothin=tokens)) + CCo += CCs2.bypairs(CCu2.filter_pairs(bothin=tokens)) + CA = CPCAnalyzer(CCo) + O = ConvexOptimizer(CCo) + #ArbGraph.from_cc(CCo).plot()._ + + CA.count_by_tokens() + + # + + #CCo.plot() + # - + + # #### convex optimizer + + targettkn = T.USDT + # r = O.margp_optimizer(targettkn, params=dict(verbose=True, debug=False)) + # r + + SFC = O.SFC(**{targettkn:O.AMMPays}) + r = O.convex_optimizer(SFC, verbose=False, solver=O.SOLVER_SCS) + r + + # #### NofeesOptimizerResult + + round(r.result,-5) + + assert type(r) == ConvexOptimizer.NofeesOptimizerResult + # assert round(r.result,-5) <= -1500000.0 + # assert round(r.result,-5) >= -2500000.0 + assert r.time < 5 + assert r.method == "convex" + assert set(r.token_table.keys()) == set(['USDT-1ec7', 'WETH-6Cc2', 'LINK-86CA', 'DAI-1d0F', 'HEX-eb39']) + assert len(r.token_table[T.USDT].x)==0 + assert len(r.token_table[T.USDT].y)==10 + lx = list(it.chain(*[rr.x for rr in r.token_table.values()])) + lx.sort() + ly = list(it.chain(*[rr.y for rr in r.token_table.values()])) + ly.sort() + assert lx == [_ for _ in range(21)] + assert ly == lx + + # #### trade instructions + + ti = r.trade_instructions() + assert type(ti[0]) == ConvexOptimizer.TradeInstruction + + assert r.trade_instructions() == r.trade_instructions(ti_format=O.TIF_OBJECTS) + ti = r.trade_instructions(ti_format=O.TIF_OBJECTS) + cids = tuple(ti_.cid for ti_ in ti) + assert isinstance(ti, tuple) + assert len(ti) == 21 + ti0=[x for x in ti if x.cid=="175"] + assert len(ti0)==1 + ti0=ti0[0] + assert ti0.cid == ti0.curve.cid + assert type(ti0).__name__ == "TradeInstruction" + assert type(ti[0]) == ConvexOptimizer.TradeInstruction + assert ti0.tknin == f"{T.LINK}" + assert ti0.tknout == f"{T.DAI}" + # assert round(ti0.amtin, 8) == 8.50052943 + # assert round(ti0.amtout, 8) == -50.40963779 + assert ti0.error is None + ti[:2] + + tid = r.trade_instructions(ti_format=O.TIF_DICTS) + assert isinstance(tid, tuple) + assert type(tid[0])==dict + assert len(tid) == len(ti) + tid0=[x for x in tid if x["cid"]=="175"] + assert len(tid0)==1 + tid0=tid0[0] + assert tid0["tknin"] == f"{T.LINK}" + assert tid0["tknout"] == f"{T.DAI}" + # assert round(tid0["amtin"], 8) == 8.50052943 + # assert round(tid0["amtout"], 8) == -50.40963779 + assert tid0["error"] is None + tid[:2] + + df = r.trade_instructions(ti_format=O.TIF_DF).fillna("") + assert tuple(df.index) == cids + assert np.all(r.trade_instructions(ti_format=O.TIF_DFRAW).fillna("")==df) + assert len(df) == len(ti) + assert list(df.columns)[:4] == ['pair', 'pairp', 'tknin', 'tknout'] + assert len(df.columns) == 4 + 4 + 1 + tif0 = dict(df.loc["175"]) + assert tif0["pair"] == 'LINK-86CA/DAI-1d0F' + assert tif0["pairp"] == "LINK/DAI" + assert tif0["tknin"] == tid0["tknin"] + assert tif0[tif0["tknin"]] == tid0["amtin"] + assert tif0[tif0["tknout"]] == tid0["amtout"] + df[:2] + + assert raises(r.trade_instructions, ti_format=O.TIF_DFAGGR).startswith("TIF_DFAGGR not implemented for") + assert raises(r.trade_instructions, ti_format=O.TIF_DFPG).startswith("TIF_DFPG not implemented for") + + # ### Simple Optimizer + + pair = f"{T.ETH}/{T.USDC}" + CCs = CCm.bypairs(pair) + CA = CPCAnalyzer(CCs) + O = SimpleOptimizer(CCs) + #ArbGraph.from_cc(CCs).plot()._ + + CA.count_by_tokens() + + # + + #CCs.plot() + # - + + # #### simple optimizer + + r = O.simple_optimizer(T.USDC) + r + + # #### result + + assert type(r) == SimpleOptimizer.SimpleOptimizerResult + assert round(r.result,5) <= -1217.28494 + assert r.time < 0.1 + assert r.method == "simple-targettkn" + assert r.errormsg is None + + round(r.result,5) + + # #### trade instructions + + ti = r.trade_instructions() + assert type(ti[0]) == SimpleOptimizer.TradeInstruction + + assert r.trade_instructions() == r.trade_instructions(ti_format=O.TIF_OBJECTS) + ti = r.trade_instructions(ti_format=O.TIF_OBJECTS) + cids = tuple(ti_.cid for ti_ in ti) + assert isinstance(ti, tuple) + assert len(ti) == 12 + ti0=[x for x in ti if x.cid=="6c988ffdc9e74acd97ccfb16dd65c110"] + assert len(ti0)==1 + ti0=ti0[0] + assert ti0.cid == ti0.curve.cid + assert type(ti0).__name__ == "TradeInstruction" + assert type(ti[0]) == SimpleOptimizer.TradeInstruction + assert ti0.tknin == f"{T.USDC}" + assert ti0.tknout == f"{T.WETH}" + assert round(ti0.amtin, 8) == 48153.80713493 + assert round(ti0.amtout, 8) == -26.18299611 + assert ti0.error is None + ti[:2] + + tid = r.trade_instructions(ti_format=O.TIF_DICTS) + assert isinstance(tid, tuple) + assert type(tid[0])==dict + assert len(tid) == len(ti) + tid0=[x for x in tid if x["cid"]=="6c988ffdc9e74acd97ccfb16dd65c110"] + assert len(tid0)==1 + tid0=tid0[0] + assert tid0["tknin"] == f"{T.USDC}" + assert tid0["tknout"] == f"{T.WETH}" + assert round(tid0["amtin"], 8) == 48153.80713493 + assert round(tid0["amtout"], 8) == -26.18299611 + assert tid0["error"] is None + tid[:2] + + df = r.trade_instructions(ti_format=O.TIF_DF).fillna("") + assert tuple(df.index) == cids + assert np.all(r.trade_instructions(ti_format=O.TIF_DFRAW).fillna("")==df) + assert len(df) == len(ti) + assert list(df.columns)[:4] == ['pair', 'pairp', 'tknin', 'tknout'] + assert len(df.columns) == 4 + 1 + 1 + tif0 = dict(df.loc["6c988ffdc9e74acd97ccfb16dd65c110"]) + assert tif0["pair"] == 'WETH-6Cc2/USDC-eB48' + assert tif0["pairp"] == "WETH/USDC" + assert tif0["tknin"] == tid0["tknin"] + assert tif0[tif0["tknin"]] == tid0["amtin"] + assert tif0[tif0["tknout"]] == tid0["amtout"] + df[:2] + + assert raises(r.trade_instructions, ti_format=O.TIF_DFAGGR).startswith("TIF_DFAGGR not implemented for") + assert raises(r.trade_instructions, ti_format=O.TIF_DFPG).startswith("TIF_DFPG not implemented for") + + +# ------------------------------------------------------------ +# Test 903 +# File test_903_OptimizerDetailedSlow.py +# Segment Analysis by pair +# ------------------------------------------------------------ +def test_analysis_by_pair(): +# ------------------------------------------------------------ + + # + + # CCm1 = CAm.CC.copy() + # CCm1 += CPC.from_carbon( + # pair=f"{T.WETH}/{T.USDC}", + # yint = 1, + # y = 1, + # pa = 1500, + # pb = 1501, + # tkny = f"{T.WETH}", + # cid = "test-1", + # isdydx=False, + # params=dict(exchange="carbon_v1"), + # ) + # CAm1 = CPCAnalyzer(CCm1) + # CCm1.asdf().to_csv("NBTest_006-augmented.csv.gz", compression = "gzip") + # - + + pricedf = CAm.pool_arbitrage_statistics() + assert len(pricedf)==165 + pricedf + + # ### WETH/USDC + + pair = "WETH-6Cc2/USDC-eB48" + print(f"Pair = {pair}") + + df = pricedf.loc[Pair.n(pair)] + assert len(df)==24 + df + + pi = CAm.pair_data(pair) + O = MargPOptimizer(pi.CC) + + # #### Target token = base token + + targettkn = pair.split("/")[0] + print(f"Target token = {targettkn}") + r = O.margp_optimizer(targettkn, params=dict(verbose=False, debug=False)) + r.trade_instructions(ti_format=O.TIF_DFAGGR) + + dfti1 = r.trade_instructions(ti_format=O.TIF_DFPG8) + print(f"Total gain: {sum(dfti1['gain_ttkn']):.4f} {targettkn}") + dfti1 + + # #### Target token = quote token + + targettkn = pair.split("/")[1] + print(f"Target token = {targettkn}") + r = O.margp_optimizer(targettkn, params=dict(verbose=False, debug=False)) + r.trade_instructions(ti_format=O.TIF_DFAGGR) + + dfti2 = r.trade_instructions(ti_format=O.TIF_DFPG8) + print(f"Total gain: {sum(dfti2['gain_ttkn']):.4f}", targettkn) + dfti2 \ No newline at end of file