From 158989fe4748f6f2cb165301697a8e48d01d4993 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Thu, 6 Jul 2023 17:28:29 -0700 Subject: [PATCH 1/3] Fixing format_float_as_string to take FixedPoint. Adjusting apeworkx to check for strings --- elfpy/utils/apeworx_integrations.py | 10 +++++----- elfpy/utils/format.py | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/elfpy/utils/apeworx_integrations.py b/elfpy/utils/apeworx_integrations.py index 778d6d7b16..d5890360c4 100644 --- a/elfpy/utils/apeworx_integrations.py +++ b/elfpy/utils/apeworx_integrations.py @@ -1019,11 +1019,11 @@ def attempt_txn( # kwargs["gas_price"] = kwargs["max_fee_per_gas"] formatted_items = [] for key, value in kwargs.items(): - value = ( - format_utils.format_float_as_string(value / 1e9) - if "fee" in key - else format_utils.format_float_as_string(value) - ) + if "fee" in key: + value = format_utils.format_float_as_string(value / 1e9) + elif isinstance(value, float) or isinstance(value, FixedPoint): + value = format_utils.format_float_as_string(value) + # Otherwise, do nothing formatted_items.append(f"{key}={value}") logging.debug("txn attempt %s of %s with %s", attempt, mult, ", ".join(formatted_items)) serial_txn: TransactionAPI = contract_txn.serialize_transaction(*args, **kwargs) diff --git a/elfpy/utils/format.py b/elfpy/utils/format.py index 9fe6ef95cb..42b79d528d 100644 --- a/elfpy/utils/format.py +++ b/elfpy/utils/format.py @@ -2,9 +2,10 @@ import logging import numpy as np +from fixedpointmath import FixedPoint -def format_float_as_string(value: float, precision=3, min_digits=0, debug=False): +def format_float_as_string(value: float | FixedPoint, precision=3, min_digits=0, debug=False): """ Format a float to a string with a given precision. This follows the significant figure behavior, irrespective of the number's size. @@ -14,6 +15,9 @@ def format_float_as_string(value: float, precision=3, min_digits=0, debug=False) log_vars = (value, type(value), precision, min_digits) logging.error(log_str, *log_vars) + if isinstance(value, FixedPoint): + value = float(value) + if np.isinf(value): return "inf" if np.isnan(value): From eaf695a7e2d801b46cf14b592b81c21efc20398c Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Thu, 6 Jul 2023 17:35:29 -0700 Subject: [PATCH 2/3] pylint --- elfpy/utils/apeworx_integrations.py | 2 +- elfpy/utils/format.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/elfpy/utils/apeworx_integrations.py b/elfpy/utils/apeworx_integrations.py index d5890360c4..3fdb8422c7 100644 --- a/elfpy/utils/apeworx_integrations.py +++ b/elfpy/utils/apeworx_integrations.py @@ -1021,7 +1021,7 @@ def attempt_txn( for key, value in kwargs.items(): if "fee" in key: value = format_utils.format_float_as_string(value / 1e9) - elif isinstance(value, float) or isinstance(value, FixedPoint): + elif isinstance(value, (FixedPoint, float)): value = format_utils.format_float_as_string(value) # Otherwise, do nothing formatted_items.append(f"{key}={value}") diff --git a/elfpy/utils/format.py b/elfpy/utils/format.py index 42b79d528d..6528f622ab 100644 --- a/elfpy/utils/format.py +++ b/elfpy/utils/format.py @@ -1,4 +1,6 @@ """Formatting utilities to aid in human-readable values.""" +from __future__ import annotations # types will be strings by default in 3.11 + import logging import numpy as np From e71eaa0310d56d93836be5b97052eafd20dfc525 Mon Sep 17 00:00:00 2001 From: Sheng Lundquist Date: Thu, 6 Jul 2023 20:37:22 -0700 Subject: [PATCH 3/3] Renaming to format_numeric_string --- elfpy/utils/apeworx_integrations.py | 12 ++++----- elfpy/utils/format.py | 4 +-- elfpy/utils/logs.py | 2 +- examples/evm_bots.py | 20 +++++++------- tests/utils/test_format_utils.py | 42 ++++++++++++++--------------- 5 files changed, 39 insertions(+), 41 deletions(-) diff --git a/elfpy/utils/apeworx_integrations.py b/elfpy/utils/apeworx_integrations.py index 3fdb8422c7..f1e7a41449 100644 --- a/elfpy/utils/apeworx_integrations.py +++ b/elfpy/utils/apeworx_integrations.py @@ -397,7 +397,7 @@ def get_wallet_from_trade_history( raise ValueError( f"events {balance=} and {on_chain_balance=} disagree by more than {tolerance} wei for {address}" ) - logging.debug(" => calculated balance = on_chain = %s", format_utils.format_float_as_string(balance)) + logging.debug(" => calculated balance = on_chain = %s", format_utils.format_numeric_string(balance)) # check if there's an outstanding balance if balance != 0 or on_chain_balance != 0: if asset_type == "SHORT": @@ -930,7 +930,7 @@ def ape_trade( return None, None return get_pool_state(txn_receipt=txn_receipt, hyperdrive_contract=hyperdrive_contract), txn_receipt except TransactionError as err: - formatted_amount = format_utils.format_float_as_string(amount) + formatted_amount = format_utils.format_numeric_string(amount) pool_info = hyperdrive_contract.getPoolInfo() pool_config = hyperdrive_contract.getPoolConfig() logging.error( @@ -1005,8 +1005,8 @@ def attempt_txn( base_fee = getattr(latest, "base_fee") logging.debug( "latest block %s has base_fee %s", - format_utils.format_float_as_string(getattr(latest, "number")), - format_utils.format_float_as_string(base_fee / 1e9, min_digits=3), + format_utils.format_numeric_string(getattr(latest, "number")), + format_utils.format_numeric_string(base_fee / 1e9, min_digits=3), ) kwargs["max_priority_fee_per_gas"] = int( agent.provider.priority_fee * (1 + priority_fee_multiple * (attempt - 1)) @@ -1020,9 +1020,9 @@ def attempt_txn( formatted_items = [] for key, value in kwargs.items(): if "fee" in key: - value = format_utils.format_float_as_string(value / 1e9) + value = format_utils.format_numeric_string(value / 1e9) elif isinstance(value, (FixedPoint, float)): - value = format_utils.format_float_as_string(value) + value = format_utils.format_numeric_string(value) # Otherwise, do nothing formatted_items.append(f"{key}={value}") logging.debug("txn attempt %s of %s with %s", attempt, mult, ", ".join(formatted_items)) diff --git a/elfpy/utils/format.py b/elfpy/utils/format.py index 6528f622ab..f11b830def 100644 --- a/elfpy/utils/format.py +++ b/elfpy/utils/format.py @@ -7,7 +7,7 @@ from fixedpointmath import FixedPoint -def format_float_as_string(value: float | FixedPoint, precision=3, min_digits=0, debug=False): +def format_numeric_string(value: float | FixedPoint, precision=3, min_digits=0, debug=False): """ Format a float to a string with a given precision. This follows the significant figure behavior, irrespective of the number's size. @@ -32,7 +32,7 @@ def format_float_as_string(value: float | FixedPoint, precision=3, min_digits=0, digits = int(np.floor(np.log10(abs(value)))) + 1 except Exception as err: # pylint: disable=broad-except if debug: - log_str = "Error in format_float_as_string: value=%s(%s), precision=%s, min_digits=%s, \n error=%s" + log_str = "Error in format_numeric_string: value=%s(%s), precision=%s, min_digits=%s, \n error=%s" log_vars = (value, type(value), precision, min_digits, err) logging.error(log_str, *log_vars) return str(value) diff --git a/elfpy/utils/logs.py b/elfpy/utils/logs.py index c8f71ba93b..5acbaa5b10 100644 --- a/elfpy/utils/logs.py +++ b/elfpy/utils/logs.py @@ -179,7 +179,7 @@ def log_hyperdrive_crash_report( """Failed to execute %s: %s\n Amount: %s\n Agent: %s\n PoolInfo: %s\n PoolConfig: %s\n""", trade_type, error, - format_utils.format_float_as_string(amount), + format_utils.format_numeric_string(amount), agent_address, formatted_pool_info, formatted_pool_config, diff --git a/examples/evm_bots.py b/examples/evm_bots.py index 571a6e0ace..946899e08f 100644 --- a/examples/evm_bots.py +++ b/examples/evm_bots.py @@ -40,7 +40,7 @@ from elfpy.bots import BotConfig from elfpy.bots.bot_info import BotInfo from elfpy.markets.hyperdrive import HyperdriveMarket, HyperdrivePricingModel -from elfpy.utils.format import format_float_as_string +from elfpy.utils.format import format_numeric_string ape_logger.set_level(logging.ERROR) @@ -174,9 +174,7 @@ def create_agent( # mint base tokens for the agents if (need_to_mint := (params["budget"].scaled_value - base_instance.balanceOf(agent.contract.address)) / 1e18) > 0: - logging.info( - " agent_%s needs to mint %s Base", agent.contract.address[:8], format_float_as_string(need_to_mint) - ) + logging.info(" agent_%s needs to mint %s Base", agent.contract.address[:8], format_numeric_string(need_to_mint)) if bot_config.devnet: txn_args = agent.contract.address, int(50_000 * 1e18) ape_utils.attempt_txn(agent.contract, base_instance.mint, *txn_args) @@ -188,9 +186,9 @@ def create_agent( " agent_%s is a %s with budget=%s Eth=%s Base=%s", bot.index, bot.name, - format_float_as_string(params["budget"]), - format_float_as_string(agent.contract.balance / 1e18), - format_float_as_string(base_instance.balanceOf(agent.contract.address) / 1e18), + format_numeric_string(params["budget"]), + format_numeric_string(agent.contract.balance / 1e18), + format_numeric_string(base_instance.balanceOf(agent.contract.address) / 1e18), ) agent.wallet = ape_utils.get_wallet_from_trade_history( address=agent.contract.address, @@ -257,7 +255,7 @@ def set_up_agents( start_time_ = now() if trade_history is None: trade_history = ape_utils.get_trade_history(hyperdrive_contract=hyperdrive_instance) - logging.debug("Getting on-chain trade info took %s seconds", format_float_as_string(now() - start_time_)) + logging.debug("Getting on-chain trade info took %s seconds", format_numeric_string(now() - start_time_)) for bot_name in [name for name in bot_config.scratch["bot_names"] if bot_config.scratch[f"num_{name}"] > 0]: bot_info = bot_config.scratch[bot_name] bot_info.name = bot_name @@ -333,8 +331,8 @@ def do_trade( logging.info( "agent_%s has Eth=%s Base=%s", agent_contract.address[:8], - format_float_as_string(agent_contract.balance / 1e18), - format_float_as_string(base_instance.balanceOf(agent_contract.address) / 1e18), + format_numeric_string(agent_contract.balance / 1e18), + format_numeric_string(base_instance.balanceOf(agent_contract.address) / 1e18), ) logging.info("\trade %s", trade.action_type.name) # execute the trade using key-word arguments @@ -370,7 +368,7 @@ def log_and_show_block_info(provider: ape.api.ProjectAPI, trade_streak: int, blo base_fee = getattr(block, "base_fee") / 1e9 logging.info( "Block number: %s, Block time: %s, Trades without crashing: %s, base_fee: %s", - format_float_as_string(block_number), + format_numeric_string(block_number), datetime.fromtimestamp(block_timestamp), trade_streak, base_fee, diff --git a/tests/utils/test_format_utils.py b/tests/utils/test_format_utils.py index 6b6f389658..cc0fb26c98 100644 --- a/tests/utils/test_format_utils.py +++ b/tests/utils/test_format_utils.py @@ -3,53 +3,53 @@ import numpy as np -from elfpy.utils.format import format_float_as_string +from elfpy.utils.format import format_numeric_string class TestFormatFloatAsString: - """Unit tests for format_float_as_string.""" + """Unit tests for format_numeric_string.""" def test_positive_values(self): """Test positive values""" - assert format_float_as_string(123.456, precision=7) == "123.4560" - assert format_float_as_string(123.456, precision=6) == "123.456" - assert format_float_as_string(123.456, precision=5) == "123.46" - assert format_float_as_string(123.456, precision=3) == "123" - assert format_float_as_string(0.12345, precision=3) == "0.123" - assert format_float_as_string(1000000, precision=7) == "1,000,000" + assert format_numeric_string(123.456, precision=7) == "123.4560" + assert format_numeric_string(123.456, precision=6) == "123.456" + assert format_numeric_string(123.456, precision=5) == "123.46" + assert format_numeric_string(123.456, precision=3) == "123" + assert format_numeric_string(0.12345, precision=3) == "0.123" + assert format_numeric_string(1000000, precision=7) == "1,000,000" def test_negative_values(self): """Test negative values""" - assert format_float_as_string(-123.456, precision=6) == "-123.456" - assert format_float_as_string(-0.12345, precision=3) == "-0.123" - assert format_float_as_string(-1000000, precision=7) == "-1,000,000" + assert format_numeric_string(-123.456, precision=6) == "-123.456" + assert format_numeric_string(-0.12345, precision=3) == "-0.123" + assert format_numeric_string(-1000000, precision=7) == "-1,000,000" def test_values_less_than_one(self): """Test values less than 1""" - assert format_float_as_string(0.0000123, precision=7) == "0.0000123" - assert format_float_as_string(0.000001, precision=7) == "0.0000010" - assert format_float_as_string(0.0000001, precision=7) == "0.0000001" + assert format_numeric_string(0.0000123, precision=7) == "0.0000123" + assert format_numeric_string(0.000001, precision=7) == "0.0000010" + assert format_numeric_string(0.0000001, precision=7) == "0.0000001" def test_large_values(self): """Test large values""" - assert format_float_as_string(1e12, precision=13) == "1,000,000,000,000" - assert format_float_as_string(1e9, precision=10) == "1,000,000,000" - assert format_float_as_string(1e6, precision=7) == "1,000,000" + assert format_numeric_string(1e12, precision=13) == "1,000,000,000,000" + assert format_numeric_string(1e9, precision=10) == "1,000,000,000" + assert format_numeric_string(1e6, precision=7) == "1,000,000" def test_zero_value(self): """Test zero value""" - assert format_float_as_string(0) == "0" + assert format_numeric_string(0) == "0" def test_inf_value(self): """Test infinity""" - assert format_float_as_string(np.inf) == "inf" + assert format_numeric_string(np.inf) == "inf" def test_nan_value(self): """Test NaN""" - assert format_float_as_string(np.nan) == "nan" + assert format_numeric_string(np.nan) == "nan" def test_debug_mode(self, caplog): """Test debug mode""" with caplog.at_level(logging.ERROR): - format_float_as_string(123.456, debug=True) + format_numeric_string(123.456, debug=True) assert "value: 123.456, type: , precision: 3, min_digits: 0" in caplog.text