From 59820d547dfd9d1c9dba45fb802ba48ff1255c2c Mon Sep 17 00:00:00 2001 From: Nicholas Welch Date: Mon, 27 May 2024 13:13:43 +1000 Subject: [PATCH 1/4] correct sorting and remove excess debug logs --- fastlane_bot/bot.py | 10 +++++----- fastlane_bot/modes/base.py | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 9d712e177..5778042f7 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -347,9 +347,9 @@ def calculate_profit( if fl_token not in [self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS]: price_curves = get_prices_simple(CCm, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, fl_token) sorted_price_curves = custom_sort(price_curves, sort_sequence, self.ConfigObj.CARBON_V1_FORKS) - self.ConfigObj.logger.debug(f"[bot.calculate_profit sort_sequence] {sort_sequence}") - self.ConfigObj.logger.debug(f"[bot.calculate_profit price_curves] {price_curves}") - self.ConfigObj.logger.debug(f"[bot.calculate_profit sorted_price_curves] {sorted_price_curves}") + # self.ConfigObj.logger.debug(f"[bot.calculate_profit sort_sequence] {sort_sequence}") + # self.ConfigObj.logger.debug(f"[bot.calculate_profit price_curves] {price_curves}") + # self.ConfigObj.logger.debug(f"[bot.calculate_profit sorted_price_curves] {sorted_price_curves}") if len(sorted_price_curves)>0: fltkn_gastkn_conversion_rate = sorted_price_curves[0][-1] flashloan_fee_amt_gastkn = Decimal(str(flashloan_fee_amt_fl_token)) / Decimal(str(fltkn_gastkn_conversion_rate)) @@ -366,8 +366,8 @@ def calculate_profit( try: price_curves_usd = get_prices_simple(CCm, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, self.ConfigObj.STABLECOIN_ADDRESS) sorted_price_curves_usd = custom_sort(price_curves_usd, sort_sequence, self.ConfigObj.CARBON_V1_FORKS) - self.ConfigObj.logger.debug(f"[bot.calculate_profit price_curves_usd] {price_curves_usd}") - self.ConfigObj.logger.debug(f"[bot.calculate_profit sorted_price_curves_usd] {sorted_price_curves_usd}") + # self.ConfigObj.logger.debug(f"[bot.calculate_profit price_curves_usd] {price_curves_usd}") + # self.ConfigObj.logger.debug(f"[bot.calculate_profit sorted_price_curves_usd] {sorted_price_curves_usd}") usd_gastkn_conversion_rate = Decimal(str(sorted_price_curves_usd[0][-1])) except Exception: usd_gastkn_conversion_rate = Decimal("NaN") diff --git a/fastlane_bot/modes/base.py b/fastlane_bot/modes/base.py index ea56c5663..d324fc406 100644 --- a/fastlane_bot/modes/base.py +++ b/fastlane_bot/modes/base.py @@ -50,12 +50,12 @@ def calculate_profit(self, src_token: str, src_profit: float, CCm: Any) -> Decim Calculate profit based on the source token. """ if src_token not in [self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS]: - sort_sequence = ['bancor_v2', 'bancor_v3', 'uniswap_v2', 'uniswap_v3'] + sort_sequence = ['bancor_v2','bancor_v3'] + self.ConfigObj.UNI_V2_FORKS + self.ConfigObj.UNI_V3_FORKS price_curves = get_prices_simple(CCm, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, src_token) sorted_price_curves = custom_sort(price_curves, sort_sequence, self.ConfigObj.CARBON_V1_FORKS) - self.ConfigObj.logger.debug(f"[modes.base.calculate_profit sort_sequence] {sort_sequence}") - self.ConfigObj.logger.debug(f"[modes.base.calculate_profit price_curves] {price_curves}") - self.ConfigObj.logger.debug(f"[modes.base.calculate_profit sorted_price_curves] {sorted_price_curves}") + # self.ConfigObj.logger.debug(f"[modes.base.calculate_profit sort_sequence] {sort_sequence}") + # self.ConfigObj.logger.debug(f"[modes.base.calculate_profit price_curves] {price_curves}") + # self.ConfigObj.logger.debug(f"[modes.base.calculate_profit sorted_price_curves] {sorted_price_curves}") assert len(sorted_price_curves) > 0, f"[modes.base.calculate_profit] Failed to get conversion rate for {src_token} and {self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS}" return Decimal(str(src_profit)) / Decimal(str(sorted_price_curves[0][-1])) return Decimal(str(src_profit)) From 2f23a4a1329ec8215bde1f4e30fee13d366aff7e Mon Sep 17 00:00:00 2001 From: Nicholas Welch Date: Mon, 27 May 2024 13:14:22 +1000 Subject: [PATCH 2/4] remove univ2 duplications --- fastlane_bot/config/network.py | 4 +--- fastlane_bot/events/utils.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/fastlane_bot/config/network.py b/fastlane_bot/config/network.py index 864b7095f..70bd428be 100644 --- a/fastlane_bot/config/network.py +++ b/fastlane_bot/config/network.py @@ -346,9 +346,7 @@ def __post_init__(self): df=self.network_df, fork_name=S.UNISWAP_V3 ) self.SOLIDLY_FEE_MAPPING = get_fee_map(df=self.network_df, fork_name=S.SOLIDLY_V2) - self.UNI_V2_FORKS = [key for key in self.UNI_V2_ROUTER_MAPPING.keys()] + [ - "uniswap_v2" - ] + self.UNI_V2_FORKS = [key for key in self.UNI_V2_ROUTER_MAPPING.keys()] self.UNI_V3_FORKS = [key for key in self.UNI_V3_ROUTER_MAPPING.keys()] self.SOLIDLY_V2_ROUTER_MAPPING = get_fork_map( diff --git a/fastlane_bot/events/utils.py b/fastlane_bot/events/utils.py index 9d6bffc46..175ad62d6 100644 --- a/fastlane_bot/events/utils.py +++ b/fastlane_bot/events/utils.py @@ -906,7 +906,6 @@ def init_bot(mgr: Any) -> CarbonBot: mgr=mgr, ConfigObj=mgr.cfg, state=mgr.pool_data, - uniswap_v2_event_mappings=mgr.uniswap_v2_event_mappings, exchanges=mgr.exchanges, ) bot = CarbonBot(ConfigObj=mgr.cfg) From bdc5855cabc767345eae0d33b0c5d50c5a73a529 Mon Sep 17 00:00:00 2001 From: Nicholas Welch Date: Mon, 27 May 2024 13:16:49 +1000 Subject: [PATCH 3/4] new pstart handling --- fastlane_bot/modes/base_triangle.py | 33 +++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/fastlane_bot/modes/base_triangle.py b/fastlane_bot/modes/base_triangle.py index 182e77d32..bc534061e 100644 --- a/fastlane_bot/modes/base_triangle.py +++ b/fastlane_bot/modes/base_triangle.py @@ -8,6 +8,7 @@ All rights reserved. Licensed under MIT. """ +import random from typing import Any, List, Dict from fastlane_bot.tools.cpc import CPCContainer from fastlane_bot.tools.optimizer import MargPOptimizer @@ -22,7 +23,7 @@ def find_arbitrage(self) -> Dict[List[Any], List[Any]]: try: container = CPCContainer(miniverse) optimizer = MargPOptimizer(container) - params = get_params(self.CCm, container.tokens(), src_token) + params = get_params(self, container, container.tokens(), src_token) optimization = optimizer.optimize(src_token, params=params) trade_instructions_dic = optimization.trade_instructions(optimizer.TIF_DICTS) trade_instructions_df = optimization.trade_instructions(optimizer.TIF_DFAGGR) @@ -45,16 +46,36 @@ def find_arbitrage(self) -> Dict[List[Any], List[Any]]: return {"combos": combos, "arb_opps": sorted(arb_opps, key=lambda arb_opp: arb_opp["profit"], reverse=True)} -def get_params(CCm, dst_tokens, src_token): +def get_params(self, CCm, dst_tokens, src_token): + # For a triangle, the pstart of each dst_token is derived from its rate vs the src_token. + # Since Carbon orders can contain diverse prices independent of external market prices, and + # we require that the pstart be on the Carbon curve to get successful optimizer runs, + # then for Carbon orders only we must randomize the pstart from the list of available Carbon curves. + # Random selection chosen as opposed to iterating over all possible combinations. + + # ASSUMPTIONS: There must be a complete triangle arb available i.e. src_token->dst_token1->dst_token2->src_token + sort_sequence = ['bancor_v2','bancor_v3'] + self.ConfigObj.UNI_V2_FORKS + self.ConfigObj.UNI_V3_FORKS pstart = {src_token: 1} for dst_token in [token for token in dst_tokens if token != src_token]: - CC = CCm.bytknx(dst_token).bytkny(src_token) + curves = list(CCm.bytknx(dst_token).bytkny(src_token)) + CC = CPCContainer(custom_sort(curves, sort_sequence, self.ConfigObj.CARBON_V1_FORKS)) if CC: - pstart[dst_token] = CC[0].p + if CC[0].params['exchange'] in self.ConfigObj.CARBON_V1_FORKS: #only carbon curve options left + pstart[dst_token] = random.choice(CC).p + else: + pstart[dst_token] = CC[0].p else: - CC = CCm.bytknx(src_token).bytkny(dst_token) + curves = list(CCm.bytknx(src_token).bytkny(dst_token)) + CC = CPCContainer(custom_sort(curves, sort_sequence, self.ConfigObj.CARBON_V1_FORKS)) if CC: - pstart[dst_token] = 1 / CC[0].p + if CC[0].params['exchange'] in self.ConfigObj.CARBON_V1_FORKS: #only carbon curve options left + pstart[dst_token] = 1/(random.choice(CC).p) + else: + pstart[dst_token] = 1 / CC[0].p else: return None return {"pstart": pstart} + +def custom_sort(data, sort_sequence, carbon_v1_forks): + sort_order = {key: index for index, key in enumerate(sort_sequence) if key not in carbon_v1_forks} + return sorted(data, key=lambda item: float('inf') if item.params['exchange'] in carbon_v1_forks else sort_order.get(item.params['exchange'], float('inf'))) From ff089ea880f974cc844aa3b2ef82eaaa090dbb6e Mon Sep 17 00:00:00 2001 From: Nicholas Welch Date: Mon, 27 May 2024 13:19:54 +1000 Subject: [PATCH 4/4] tri-compl replace native plus better logs --- fastlane_bot/modes/triangle_multi_complete.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fastlane_bot/modes/triangle_multi_complete.py b/fastlane_bot/modes/triangle_multi_complete.py index 83347ffd0..690581d98 100644 --- a/fastlane_bot/modes/triangle_multi_complete.py +++ b/fastlane_bot/modes/triangle_multi_complete.py @@ -16,13 +16,16 @@ class ArbitrageFinderTriangleMultiComplete(ArbitrageFinderTriangleBase): def get_combos(self) -> List[Any]: combos = [] + flashloan_tokens = [x.replace(self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS) for x in self.flashloan_tokens] + flashloan_tokens = list(set(flashloan_tokens)) - for flt in self.flashloan_tokens: + for flt in flashloan_tokens: # Get the Carbon pairs carbon_pairs = sort_pairs(set([curve.pair for curve in self.CCm.curves if curve.params.exchange in self.ConfigObj.CARBON_V1_FORKS])) # Create a set of unique tokens excluding the flashloan token x_tokens = {token for pair in carbon_pairs for token in pair.split("/") if token != flt} + x_tokens = list(set([x.replace(self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS) for x in x_tokens])) # Get relevant pairs containing the flashloan token flt_x_pairs = sort_pairs([f"{x_token}/{flt}" for x_token in x_tokens]) @@ -32,11 +35,11 @@ def get_combos(self) -> List[Any]: # Note the relevant pairs all_relevant_pairs = flt_x_pairs + x_y_pairs - self.ConfigObj.logger.debug(f"len(all_relevant_pairs) {len(all_relevant_pairs)}") + self.ConfigObj.logger.debug(f"[triangle_multi_complete.get_combos] all_relevant_pairs: {len(all_relevant_pairs)}") # Generate triangle groups triangle_groups = get_triangle_groups(flt, x_y_pairs) - self.ConfigObj.logger.debug(f"len(triangle_groups) {len(triangle_groups)}") + self.ConfigObj.logger.debug(f"[triangle_multi_complete.get_combos] triangle_groups: {len(triangle_groups)}") # Get pair info for the cohort all_relevant_pairs_info = get_all_relevant_pairs_info(self.CCm, all_relevant_pairs, self.ConfigObj.CARBON_V1_FORKS) @@ -46,6 +49,7 @@ def get_combos(self) -> List[Any]: # Get [(flt,curves)] analysis set for the flashloan token flt_triangle_analysis_set = get_analysis_set_per_flt(flt, valid_triangles, all_relevant_pairs_info) + self.ConfigObj.logger.debug(f"[triangle_multi_complete.get_combos] flt_triangle_analysis_set {flt, len(flt_triangle_analysis_set)}") # The entire analysis set for all flashloan tokens combos.extend(flt_triangle_analysis_set)