-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Triangle mode refactor with pstart #676
base: triangle-mode-refactor
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Entire code section replaced with calling |
||
usd_gastkn_conversion_rate = Decimal(str(sorted_price_curves_usd[0][-1])) | ||
except Exception: | ||
usd_gastkn_conversion_rate = Decimal("NaN") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may yield a functional (behavioral) change outside the scope of this PR. |
||
exchanges=mgr.exchanges, | ||
) | ||
bot = CarbonBot(ConfigObj=mgr.cfg) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Entire function section revised. |
||
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)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why (and since when) do we "require that the pstart be on the Carbon curve to get successful optimizer runs"? The code below actually opts for a What exactly does choosing a random carbon strategy to get the price from achieve here "as opposed to iterating over all possible combinations"? First of all, It's not like we were iterating these curves prior to this change. Second, even if for some reason we need a random Carbon curve here, the first curve is just as random as any other curve on that list, since you have no knowledge of how this list was generated to begin with. Third, adding randomness in a system requires a really (REALLY) good reason, as it turns the behavior of that system inconsistent, unexpected, non-deterministic, hard to test and hard to reproduce any reported issues. For example, reducing the probability of collisions between different bot operators is a good reason for choosing a random arbitrage-opportunity (at the top layer). Such reason does not appear to be viable in the case at hand. |
||
|
||
# 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'))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code for sorting the list of curves by order of exchange-significance (B2, B3, U2, U3, Carbon) is already implemented in the base class (please pull from the base branch first because it has been slightly modified since you posted this PR). But even more importantly - you are using it here only for the triangle modes. And as explained in a previous comment - the randomization of Carbon curve prices seems completely out of place. A new list of curves is generated on every iteration of the bot, and the first Carbon curve is as random as any other Carbon curve. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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)}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to go wild, especially in the case of debug loggings. |
||
|
||
# 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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Entire code section replaced with calling
ArbitrageFinderBase.calculate_profit
.Need to pull from the base branch.