From 73ae53d5a3cd3e9ae547c97b187c91de3c164b5c Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 20 Mar 2024 10:01:21 -0400 Subject: [PATCH 01/12] wip1 --- .../core/hyperdrive/interactive/econ_tests.py | 72 ++++++++++++++++++- src/agent0/core/utilities/__init__.py | 1 + 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 src/agent0/core/utilities/__init__.py diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index 46451d7052..abe43f1b79 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -1,19 +1,27 @@ """Tests of economic intuition.""" +import logging +from copy import deepcopy + +import pandas as pd import pytest from fixedpointmath import FixedPoint from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive +from agent0.core.utilities import predict_long, predict_short YEAR_IN_SECONDS = 31_536_000 +# I want to be able to use fancy f-string formatting +# pylint: disable=logging-fstring-interpolation @pytest.mark.anvil def test_symmetry(chain: LocalChain): - """Does in equal out? + """Check wether in equals out. One may be under the impression swaps between x and y have the same result, irrespective of direction. - We set the number of bonds in and out to 100k and see if the resulting shares_in and shares_out differ.""" + We set the number of bonds in and out to 100k and see if the resulting shares_in and shares_out differ. + """ interactive_config = LocalHyperdrive.Config( position_duration=YEAR_IN_SECONDS, # 1 year term governance_lp_fee=FixedPoint(0.1), @@ -27,3 +35,63 @@ def test_symmetry(chain: LocalChain): print(shares_out) print(shares_in) assert shares_out != shares_in + +@pytest.mark.anvil +def test_discoverability(chain: LocalChain): + """Test discoverability of rates across by time stretch.""" + liquidity = FixedPoint(10_000_000) + time_stretch_apr_list = [0.03] + with open("discoverability.csv", "w", encoding="UTF-8") as file: + file.write("rate,time_stretch_apr\n") + for time_stretch_apr in time_stretch_apr_list: + interactive_config = LocalHyperdrive.Config( + position_duration=YEAR_IN_SECONDS, # 1 year term + governance_lp_fee=FixedPoint(0.1), + curve_fee=FixedPoint(0.01), + flat_fee=FixedPoint(0), + initial_liquidity=liquidity, + initial_time_stretch_apr=FixedPoint(str(time_stretch_apr)), + ) + interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) + interface = interactive_hyperdrive.interface + + max_long = interface.calc_max_long(liquidity) + logging.info(f"Max long : base={float(max_long):>10,.0f}") + max_short = interface.calc_max_short(liquidity) + logging.info(f"Max short: base={float(max_short):>10,.0f}") + biggest = int(max(max_long, max_short)) + increment = biggest // 10 + records = [] + for trade_size in range(increment, 11 * increment, increment): + long_price = short_price = long_rate = short_rate = None + if trade_size <= max_long: + long_trade = predict_long(interface, base=FixedPoint(trade_size)) + pool_state = deepcopy(interface.current_pool_state) + pool_state.pool_info.bond_reserves += long_trade.pool.bonds + pool_state.pool_info.share_reserves += long_trade.pool.shares + long_price = interface.calc_spot_price(pool_state) + long_rate = interface.calc_fixed_rate(pool_state) + if trade_size <= max_short: + short_trade = predict_short(interface, bonds=FixedPoint(trade_size)) + pool_state = deepcopy(interface.current_pool_state) + pool_state.pool_info.bond_reserves += short_trade.pool.bonds + pool_state.pool_info.share_reserves += short_trade.pool.shares + short_price = interface.calc_spot_price(pool_state) + short_rate = interface.calc_fixed_rate(pool_state) + records.append((trade_size, long_price, short_price, long_rate, short_rate)) + results_df = pd.DataFrame.from_records(records, columns=["trade_size", "long_price", "short_price", "long_rate", "short_rate"]) + logging.info(f"Prices:\n{results_df[['trade_size', 'long_price', 'short_price']]}") + logging.info(f"Rates:\n{results_df[['trade_size', 'long_rate', 'short_rate']]}") + + # put all rates together in a discoverability vector + rate_list = results_df["long_rate"].to_list() + results_df["short_rate"].to_list() + discoverability = [float(num) if num is not None else None for num in rate_list] + # remove None + discoverability = [rate for rate in discoverability if rate is not None] + # sort + discoverability = sorted(discoverability) + disc_df = pd.DataFrame(data=discoverability, columns=["discoverability"]) + disc_df["time_stretch_apr"] = time_stretch_apr + disc_df.to_csv(file, mode="a", header=False, index=False) + discoverability_text = '\n'.join([str(num) for num in discoverability]) + logging.info(f"Discoverability:{discoverability_text}") \ No newline at end of file diff --git a/src/agent0/core/utilities/__init__.py b/src/agent0/core/utilities/__init__.py new file mode 100644 index 0000000000..98bccfdfcf --- /dev/null +++ b/src/agent0/core/utilities/__init__.py @@ -0,0 +1 @@ +from .predict import predict_long, predict_short \ No newline at end of file From 997c9d0ab5a0dbbe034df30c70ad92d3c6e61f6d Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 20 Mar 2024 10:40:35 -0400 Subject: [PATCH 02/12] wip2 --- .../core/hyperdrive/interactive/econ_tests.py | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index abe43f1b79..cb37e788f5 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -15,6 +15,7 @@ # I want to be able to use fancy f-string formatting # pylint: disable=logging-fstring-interpolation + @pytest.mark.anvil def test_symmetry(chain: LocalChain): """Check wether in equals out. @@ -36,14 +37,16 @@ def test_symmetry(chain: LocalChain): print(shares_in) assert shares_out != shares_in + @pytest.mark.anvil def test_discoverability(chain: LocalChain): """Test discoverability of rates across by time stretch.""" liquidity = FixedPoint(10_000_000) - time_stretch_apr_list = [0.03] + time_stretch_apr_list = [0.03, 0.05, 0.1] with open("discoverability.csv", "w", encoding="UTF-8") as file: - file.write("rate,time_stretch_apr\n") + file.write("trade_size,rate,time_stretch_apr\n") for time_stretch_apr in time_stretch_apr_list: + logging.info(f"Time stretch APR: {time_stretch_apr}") interactive_config = LocalHyperdrive.Config( position_duration=YEAR_IN_SECONDS, # 1 year term governance_lp_fee=FixedPoint(0.1), @@ -79,19 +82,12 @@ def test_discoverability(chain: LocalChain): short_price = interface.calc_spot_price(pool_state) short_rate = interface.calc_fixed_rate(pool_state) records.append((trade_size, long_price, short_price, long_rate, short_rate)) - results_df = pd.DataFrame.from_records(records, columns=["trade_size", "long_price", "short_price", "long_rate", "short_rate"]) + results_df = pd.DataFrame.from_records( + records, columns=["trade_size", "long_price", "short_price", "long_rate", "short_rate"] + ) logging.info(f"Prices:\n{results_df[['trade_size', 'long_price', 'short_price']]}") logging.info(f"Rates:\n{results_df[['trade_size', 'long_rate', 'short_rate']]}") - # put all rates together in a discoverability vector - rate_list = results_df["long_rate"].to_list() + results_df["short_rate"].to_list() - discoverability = [float(num) if num is not None else None for num in rate_list] - # remove None - discoverability = [rate for rate in discoverability if rate is not None] - # sort - discoverability = sorted(discoverability) - disc_df = pd.DataFrame(data=discoverability, columns=["discoverability"]) - disc_df["time_stretch_apr"] = time_stretch_apr - disc_df.to_csv(file, mode="a", header=False, index=False) - discoverability_text = '\n'.join([str(num) for num in discoverability]) - logging.info(f"Discoverability:{discoverability_text}") \ No newline at end of file + for row in results_df.itertuples(): + file.write(f"{row.trade_size},{row.long_rate},{time_stretch_apr}\n") + file.write(f"{-row.trade_size},{row.short_rate},{time_stretch_apr}\n") From 5bb89294621e39311ba402a060dd2bdb0413e3d5 Mon Sep 17 00:00:00 2001 From: Mihai Date: Tue, 2 Apr 2024 11:31:16 -0400 Subject: [PATCH 03/12] wip3 --- .../core/hyperdrive/interactive/econ_tests.py | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index cb37e788f5..6364fe8203 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -2,9 +2,14 @@ import logging from copy import deepcopy +from decimal import Decimal import pandas as pd import pytest +from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive +from agent0.core.hyperdrive.interactive.i_local_hyperdrive_agent import LocalHyperdriveAgent +from agent0.core.hyperdrive.policies import PolicyZoo +from agent0.core.utilities import predict_long, predict_short from fixedpointmath import FixedPoint from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive @@ -91,3 +96,205 @@ def test_discoverability(chain: LocalChain): for row in results_df.itertuples(): file.write(f"{row.trade_size},{row.long_rate},{time_stretch_apr}\n") file.write(f"{-row.trade_size},{row.short_rate},{time_stretch_apr}\n") + + +@pytest.mark.anvil +def test_lp_pnl(chain: LocalChain): + """Test whether LP PNL matches our rule of thumb.""" + liquidity = FixedPoint(10_000_000) + time_stretch_apr_list = [0.05] + with open("discoverability.csv", "w", encoding="UTF-8") as file: + file.write("trade_size,rate,time_stretch_apr\n") + for time_stretch_apr in time_stretch_apr_list: + logging.info(f"Time stretch APR: {time_stretch_apr}") + interactive_config = LocalHyperdrive.Config( + position_duration=YEAR_IN_SECONDS, # 1 year term + governance_lp_fee=FixedPoint(0.1), # 10% governance fee + curve_fee=FixedPoint(0.01), # 1% curve fee + flat_fee=FixedPoint(0), # 0bps flat fee + initial_liquidity=liquidity, + initial_time_stretch_apr=FixedPoint(str(time_stretch_apr)), + ) + interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) + interface = interactive_hyperdrive.interface + + manual_agent = interactive_hyperdrive.init_agent(base=FixedPoint(1e9)) + manual_agent.open_short(bonds=FixedPoint(9_050_000)) + logging.info(f"New rate: {interface.calc_fixed_rate()}") + + +def test_lp_pnl_calculator(chain: LocalChain): + """Calculate LP PNL given a set of parameters.""" + initial_liquidity = FixedPoint(10_000_000) + time_stretch_apr = 0.05 + initial_fixed_apr = 0.05 + + interactive_config = LocalHyperdrive.Config( + position_duration=YEAR_IN_SECONDS, # 1 year term + governance_lp_fee=FixedPoint(0.1), + curve_fee=FixedPoint(0), + flat_fee=FixedPoint(0), + initial_liquidity=initial_liquidity, + initial_time_stretch_apr=FixedPoint(str(time_stretch_apr)), + calc_pnl=False, + initial_fixed_apr=FixedPoint(str(initial_fixed_apr)), + initial_variable_rate=FixedPoint(str(initial_fixed_apr)), + ) + max_short = LocalHyperdrive(chain, interactive_config).interface.calc_max_short(budget=FixedPoint(1e12)) + increment = int(max_short) // 10 + records = [] + for trade_size in range(increment, 11 * increment, increment): + interactive_config = LocalHyperdrive.Config( + position_duration=YEAR_IN_SECONDS, # 1 year term + governance_lp_fee=FixedPoint(0.1), + curve_fee=FixedPoint(0), + flat_fee=FixedPoint(0), + initial_liquidity=initial_liquidity, + initial_time_stretch_apr=FixedPoint(str(time_stretch_apr)), + calc_pnl=False, + initial_fixed_apr=FixedPoint(str(initial_fixed_apr)), + initial_variable_rate=FixedPoint(str(initial_fixed_apr)), + ) + interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) + lp_larry = interactive_hyperdrive.init_agent( + base=FixedPoint(0), name="larry", private_key=chain.get_deployer_account_private_key() + ) + manual_agent = interactive_hyperdrive.init_agent(base=FixedPoint(1e12)) + start_timestamp = interactive_hyperdrive.interface.current_pool_state.block_time + + print("\n=== START ===") + starting_base = {} + for agent in interactive_hyperdrive._pool_agents: # pylint: disable=protected-access + if agent.name == "larry": + # larry is the deployer, their base balance is the initial liquidity + starting_base[agent.name] = initial_liquidity + else: + starting_base[agent.name] = agent.wallet.balance.amount + for k, v in starting_base.items(): + if k is not None: + print(f"{k:6}: {float(v):>17,.0f}") + pool_state = deepcopy(interactive_hyperdrive.interface.current_pool_state) + print("fixed rate is", interactive_hyperdrive.interface.calc_fixed_rate(pool_state)) + print(f"lp_share_price={pool_state.pool_info.lp_share_price}") + + print(f"=== TRADE ({trade_size:,.0f}) ===") + short_bonds = interactive_hyperdrive.interface.calc_shares_out_given_bonds_in_down(FixedPoint(trade_size)) + event_list = manual_agent.open_short(bonds=short_bonds) + event = event_list[0] if isinstance(event_list, list) else event_list + effective_spot_price = event.base_proceeds / event.bond_amount + effective_interest_rate = (FixedPoint(1) - effective_spot_price) / effective_spot_price + position_size = manual_agent.agent.wallet.shorts[list(manual_agent.agent.wallet.shorts)[0]].balance + print(f" position size is {float(position_size):,.0f} bonds") + spent_base = {} + for agent in interactive_hyperdrive._pool_agents: # pylint: disable=protected-access + spent_base[agent.name] = starting_base[agent.name] - agent.wallet.balance.amount + ending_pool_state = deepcopy(interactive_hyperdrive.interface.current_pool_state) + new_fixed_rate = interactive_hyperdrive.interface.calc_fixed_rate(ending_pool_state) + print("fixed rate is", new_fixed_rate) + print(f"lp_share_price={ending_pool_state.pool_info.lp_share_price}") + # set variable rate equal to fixed rate + interactive_hyperdrive.set_variable_rate(new_fixed_rate) + + # advance one year to let all positions mature + current_timestamp = interactive_hyperdrive.interface.current_pool_state.block_time + time_already_passed = current_timestamp - start_timestamp + advance_time_to = YEAR_IN_SECONDS + advance_time_seconds = int(advance_time_to) - time_already_passed + print(f" advancing {advance_time_seconds} seconds... ", end="") + chain.advance_time(advance_time_seconds, create_checkpoints=False) + print("done.") + current_timestamp = interactive_hyperdrive.interface.current_pool_state.block_time + print(f"new timestamp is {current_timestamp}") + # close all positions + print("before agent action") + for short in manual_agent.agent.wallet.shorts: + print( + f" {short}: time to maturity {short-current_timestamp} seconds ({(short-current_timestamp)/YEAR_IN_SECONDS:0.5f} years)" + ) + manual_agent.liquidate() + print("after agent action") + for short in manual_agent.agent.wallet.shorts: + print( + f" {short}: time to maturity {short-current_timestamp} seconds ({(short-current_timestamp)/YEAR_IN_SECONDS:0.5f} years)" + ) + lp_larry.remove_liquidity(lp_larry.wallet.lp_tokens - interactive_config.minimum_share_reserves * 2) + + print("=== END ===") + print("ending WETH balances:") + ending_base = {} + for agent in interactive_hyperdrive._pool_agents: # pylint: disable=protected-access + ending_base[agent.name] = agent.wallet.balance.amount + for k, v in ending_base.items(): + if k is not None: + print(f" {k:6}: {float(v):>17,.0f}") + lp_larry_starting_base = starting_base["larry"] + lp_larry_ending_base = ending_base["larry"] + lp_larry_return_abs = lp_larry_ending_base - lp_larry_starting_base + lp_larry_return_pct = lp_larry_return_abs / lp_larry_starting_base + ending_pool_state = deepcopy(interactive_hyperdrive.interface.current_pool_state) + print("fixed rate is", interactive_hyperdrive.interface.calc_fixed_rate(ending_pool_state)) + print(f"lp_share_price={ending_pool_state.pool_info.lp_share_price}") + print("returns:") + # calculate rule of thumb return + linear_interest_rate = (new_fixed_rate - FixedPoint(initial_fixed_apr)) / FixedPoint(2) + FixedPoint( + initial_fixed_apr + ) + # estimated_arb_return = position_size * (new_fixed_rate - FixedPoint(initial_fixed_apr)) / FixedPoint(2) + estimated_arb_return = position_size * (new_fixed_rate - FixedPoint(effective_interest_rate)) + print(f"estimate arb return is {float(estimated_arb_return):>17,.0f}") + estimated_lp_return = lp_larry_starting_base * new_fixed_rate - estimated_arb_return + # estimated_lp_return_pct = estimated_lp_return / lp_larry_starting_base + estimated_lp_return_pct_linear = ( + new_fixed_rate - (new_fixed_rate - FixedPoint(linear_interest_rate)) * event.bond_amount / initial_liquidity + ) + estimated_lp_return_pct_effective = ( + new_fixed_rate + - (new_fixed_rate - FixedPoint(effective_interest_rate)) * event.base_proceeds / initial_liquidity + ) + print(f"estimated LP return is {float(estimated_lp_return):>17,.0f}") + print(f"actual LP return is {float(lp_larry_return_abs):>17,.0f}") + print(f"estimated LP return is {float(estimated_lp_return_pct_effective)*100:>17,.5f}% (effective)") + print(f"estimated LP return is {float(estimated_lp_return_pct_linear)*100:>17,.5f}% (linear)") + print(f"actual LP return is {float(lp_larry_return_pct)*100:>17,.5f}%") + return_diff = estimated_lp_return_pct_effective - lp_larry_return_pct + return_diff_pct = return_diff / lp_larry_return_pct + print(f" error is {float(return_diff)*100:>17,.5f}% points") + print(f" error is {float(return_diff_pct*100):>17,.5f}%") + pool_info = interactive_hyperdrive.get_pool_state() + time_passed_days = (pool_info.timestamp.iloc[-1] - pool_info.timestamp.iloc[0]).total_seconds() / 60 / 60 / 24 + print(f" Holding Period Return: {float(lp_larry_return_abs):,.0f} ({float(lp_larry_return_pct):,.2%})") + print(f" Holding Period: {time_passed_days:,.2f} days") + years_passed = time_passed_days / 365 + print(f" Annualization Factor = {years_passed:,.5f} years passed ({time_passed_days:.2f}/365)") + print(f" APR = (1+HPR) ** (1/{years_passed:,.5f}) - 1") + apr = (1 + Decimal(str(lp_larry_return_pct))) ** (1 / Decimal(years_passed)) - 1 + print(f" Annualized Percent Return: {apr:,.2%}") + + records.append( + ( + new_fixed_rate, + estimated_lp_return_pct_effective, + estimated_lp_return_pct_linear, + lp_larry_return_pct, + return_diff, + return_diff_pct, + effective_interest_rate, + linear_interest_rate, + ) + ) + + df = pd.DataFrame( + records, + columns=[ + "new_fixed_rate", + "estimated_lp_return_pct_effective", + "estimated_lp_return_pct_linear", + "actual_lp_return_pct", + "return_diff", + "return_diff_pct", + "effective_interest_rate", + "linear_interest_rate", + ], + ) + print(df) + df.to_csv("lp_returns.csv", index=False) From 996304d9e9887cf2c1133508b5fd35ea9ffe069e Mon Sep 17 00:00:00 2001 From: Mihai Date: Mon, 29 Apr 2024 17:41:26 -0400 Subject: [PATCH 04/12] wip4 --- pyproject.toml | 4 + .../core/hyperdrive/interactive/econ_tests.py | 116 ++++++++++-------- 2 files changed, 67 insertions(+), 53 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f135ece683..af3b3a1e9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,6 +77,10 @@ dev = [ "urllib3", ] +datascience = [ + "seaborn", +] + ai = [ "gymnasium", "scipy", diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index 6364fe8203..b41cc949c4 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -4,19 +4,20 @@ from copy import deepcopy from decimal import Decimal +import numpy as np import pandas as pd import pytest from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive -from agent0.core.hyperdrive.interactive.i_local_hyperdrive_agent import LocalHyperdriveAgent -from agent0.core.hyperdrive.policies import PolicyZoo from agent0.core.utilities import predict_long, predict_short from fixedpointmath import FixedPoint -from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive -from agent0.core.utilities import predict_long, predict_short - YEAR_IN_SECONDS = 31_536_000 +# too many local variables +# pylint: disable=too-many-locals +# too many statements +# pylint: disable=too-many-statements +# ruff: noqa: PLR0915 # I want to be able to use fancy f-string formatting # pylint: disable=logging-fstring-interpolation @@ -45,57 +46,66 @@ def test_symmetry(chain: LocalChain): @pytest.mark.anvil def test_discoverability(chain: LocalChain): - """Test discoverability of rates across by time stretch.""" + """Test discoverability of rates by time stretch.""" liquidity = FixedPoint(10_000_000) - time_stretch_apr_list = [0.03, 0.05, 0.1] - with open("discoverability.csv", "w", encoding="UTF-8") as file: - file.write("trade_size,rate,time_stretch_apr\n") - for time_stretch_apr in time_stretch_apr_list: - logging.info(f"Time stretch APR: {time_stretch_apr}") - interactive_config = LocalHyperdrive.Config( - position_duration=YEAR_IN_SECONDS, # 1 year term - governance_lp_fee=FixedPoint(0.1), - curve_fee=FixedPoint(0.01), - flat_fee=FixedPoint(0), - initial_liquidity=liquidity, - initial_time_stretch_apr=FixedPoint(str(time_stretch_apr)), - ) - interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) - interface = interactive_hyperdrive.interface + time_stretch_apr_list = [0.01, 0.03, 0.05, 0.1] + # time_stretch_apr_list = [0.05] + trade_portion_list = [*np.arange(0.1, 1.0, 0.1), 0.99] + records = [] + for time_stretch_apr in time_stretch_apr_list: + logging.info(f"Time stretch APR: {time_stretch_apr}") + interactive_config = LocalHyperdrive.Config( + position_duration=YEAR_IN_SECONDS, # 1 year term + governance_lp_fee=FixedPoint(0.1), + curve_fee=FixedPoint(0.01), + flat_fee=FixedPoint(0), + initial_liquidity=liquidity, + initial_time_stretch_apr=FixedPoint(str(time_stretch_apr)), + # factory_min_fixed_apr = FixedPoint(scaled_value=1) + factory_min_fixed_apr = FixedPoint(0.001), + factory_max_fixed_apr = FixedPoint(100), + # factory_min_time_stretch_apr = FixedPoint(scaled_value=1), + factory_min_time_stretch_apr = FixedPoint(0.001), + factory_max_time_stretch_apr = FixedPoint(100), + ) + interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) + interface = interactive_hyperdrive.interface + time_stretch = interface.current_pool_state.pool_config.time_stretch + logging.info("Time stretch: %s", time_stretch) + logging.info("Time stretch: %s", time_stretch) - max_long = interface.calc_max_long(liquidity) - logging.info(f"Max long : base={float(max_long):>10,.0f}") - max_short = interface.calc_max_short(liquidity) - logging.info(f"Max short: base={float(max_short):>10,.0f}") - biggest = int(max(max_long, max_short)) - increment = biggest // 10 - records = [] - for trade_size in range(increment, 11 * increment, increment): - long_price = short_price = long_rate = short_rate = None - if trade_size <= max_long: - long_trade = predict_long(interface, base=FixedPoint(trade_size)) - pool_state = deepcopy(interface.current_pool_state) - pool_state.pool_info.bond_reserves += long_trade.pool.bonds - pool_state.pool_info.share_reserves += long_trade.pool.shares - long_price = interface.calc_spot_price(pool_state) - long_rate = interface.calc_fixed_rate(pool_state) - if trade_size <= max_short: - short_trade = predict_short(interface, bonds=FixedPoint(trade_size)) - pool_state = deepcopy(interface.current_pool_state) - pool_state.pool_info.bond_reserves += short_trade.pool.bonds - pool_state.pool_info.share_reserves += short_trade.pool.shares - short_price = interface.calc_spot_price(pool_state) - short_rate = interface.calc_fixed_rate(pool_state) - records.append((trade_size, long_price, short_price, long_rate, short_rate)) - results_df = pd.DataFrame.from_records( - records, columns=["trade_size", "long_price", "short_price", "long_rate", "short_rate"] - ) - logging.info(f"Prices:\n{results_df[['trade_size', 'long_price', 'short_price']]}") - logging.info(f"Rates:\n{results_df[['trade_size', 'long_rate', 'short_rate']]}") + max_long = interface.calc_max_long(liquidity) + max_short = interface.calc_max_short(liquidity) + logging.info(f"Max long : base={float(max_long):>10,.0f}") + logging.info(f"Max short: base={float(max_short):>10,.0f}") + for trade_portion in trade_portion_list: + long_price = long_rate = None + trade_size = int(float(max_long) * trade_portion) + logging.info(f"Attempting long trade of {trade_size}") + long_trade = predict_long(interface, base=FixedPoint(trade_size)) + pool_state = deepcopy(interface.current_pool_state) + pool_state.pool_info.bond_reserves += long_trade.pool.bonds + pool_state.pool_info.share_reserves += long_trade.pool.shares + long_price = interface.calc_spot_price(pool_state) + long_rate = interface.calc_fixed_rate(pool_state) + records.append((trade_size, trade_portion, long_price, long_rate, time_stretch_apr)) + for trade_portion in trade_portion_list: + short_price = short_rate = None + trade_size = int(float(max_short) * trade_portion) + logging.info(f"Attempting short trade of {trade_size}") + short_trade = predict_short(interface, bonds=FixedPoint(trade_size)) + pool_state = deepcopy(interface.current_pool_state) + pool_state.pool_info.bond_reserves += short_trade.pool.bonds + pool_state.pool_info.share_reserves += short_trade.pool.shares + short_price = interface.calc_spot_price(pool_state) + short_rate = interface.calc_fixed_rate(pool_state) + records.append((-trade_size, -trade_portion, short_price, short_rate, time_stretch_apr)) + results_df = pd.DataFrame.from_records(records, columns=["trade_size", "portion", "price", "rate", "time_stretch_apr"]) + logging.info(f"\n{results_df[['trade_size', 'portion', 'price', 'rate']]}") - for row in results_df.itertuples(): - file.write(f"{row.trade_size},{row.long_rate},{time_stretch_apr}\n") - file.write(f"{-row.trade_size},{row.short_rate},{time_stretch_apr}\n") + # for row in results_df.itertuples(): + # file.write(f"{row.trade_size},{row.portion},{row.price},{row.rate},{time_stretch_apr}\n") + results_df.to_csv("discoverability.csv", index=False) @pytest.mark.anvil From 4d87d1fd12b80aa3ca35e3f7ccb9f6d2c5fe60bc Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 1 May 2024 16:03:40 -0400 Subject: [PATCH 05/12] parametrize --- .../core/hyperdrive/interactive/econ_tests.py | 109 +++++++++--------- 1 file changed, 53 insertions(+), 56 deletions(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index b41cc949c4..28ad86fbe9 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -44,68 +44,65 @@ def test_symmetry(chain: LocalChain): assert shares_out != shares_in +# parametrize with time_stretch_apr +@pytest.mark.parametrize("time_stretch_apr", [1]) @pytest.mark.anvil -def test_discoverability(chain: LocalChain): +def test_discoverability(chain: LocalChain, time_stretch_apr: float): """Test discoverability of rates by time stretch.""" liquidity = FixedPoint(10_000_000) - time_stretch_apr_list = [0.01, 0.03, 0.05, 0.1] - # time_stretch_apr_list = [0.05] trade_portion_list = [*np.arange(0.1, 1.0, 0.1), 0.99] records = [] - for time_stretch_apr in time_stretch_apr_list: - logging.info(f"Time stretch APR: {time_stretch_apr}") - interactive_config = LocalHyperdrive.Config( - position_duration=YEAR_IN_SECONDS, # 1 year term - governance_lp_fee=FixedPoint(0.1), - curve_fee=FixedPoint(0.01), - flat_fee=FixedPoint(0), - initial_liquidity=liquidity, - initial_time_stretch_apr=FixedPoint(str(time_stretch_apr)), - # factory_min_fixed_apr = FixedPoint(scaled_value=1) - factory_min_fixed_apr = FixedPoint(0.001), - factory_max_fixed_apr = FixedPoint(100), - # factory_min_time_stretch_apr = FixedPoint(scaled_value=1), - factory_min_time_stretch_apr = FixedPoint(0.001), - factory_max_time_stretch_apr = FixedPoint(100), - ) - interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) - interface = interactive_hyperdrive.interface - time_stretch = interface.current_pool_state.pool_config.time_stretch - logging.info("Time stretch: %s", time_stretch) - logging.info("Time stretch: %s", time_stretch) - - max_long = interface.calc_max_long(liquidity) - max_short = interface.calc_max_short(liquidity) - logging.info(f"Max long : base={float(max_long):>10,.0f}") - logging.info(f"Max short: base={float(max_short):>10,.0f}") - for trade_portion in trade_portion_list: - long_price = long_rate = None - trade_size = int(float(max_long) * trade_portion) - logging.info(f"Attempting long trade of {trade_size}") - long_trade = predict_long(interface, base=FixedPoint(trade_size)) - pool_state = deepcopy(interface.current_pool_state) - pool_state.pool_info.bond_reserves += long_trade.pool.bonds - pool_state.pool_info.share_reserves += long_trade.pool.shares - long_price = interface.calc_spot_price(pool_state) - long_rate = interface.calc_fixed_rate(pool_state) - records.append((trade_size, trade_portion, long_price, long_rate, time_stretch_apr)) - for trade_portion in trade_portion_list: - short_price = short_rate = None - trade_size = int(float(max_short) * trade_portion) - logging.info(f"Attempting short trade of {trade_size}") - short_trade = predict_short(interface, bonds=FixedPoint(trade_size)) - pool_state = deepcopy(interface.current_pool_state) - pool_state.pool_info.bond_reserves += short_trade.pool.bonds - pool_state.pool_info.share_reserves += short_trade.pool.shares - short_price = interface.calc_spot_price(pool_state) - short_rate = interface.calc_fixed_rate(pool_state) - records.append((-trade_size, -trade_portion, short_price, short_rate, time_stretch_apr)) - results_df = pd.DataFrame.from_records(records, columns=["trade_size", "portion", "price", "rate", "time_stretch_apr"]) - logging.info(f"\n{results_df[['trade_size', 'portion', 'price', 'rate']]}") + logging.info(f"Time stretch APR: {time_stretch_apr}") + interactive_config = LocalHyperdrive.Config( + position_duration=YEAR_IN_SECONDS, # 1 year term + governance_lp_fee=FixedPoint(0.1), + curve_fee=FixedPoint(0.01), + flat_fee=FixedPoint(0), + initial_liquidity=liquidity, + initial_fixed_apr=FixedPoint(time_stretch_apr), + initial_time_stretch_apr=FixedPoint(time_stretch_apr), + factory_min_fixed_apr=FixedPoint(0.001), + factory_max_fixed_apr=FixedPoint(1000), + factory_min_time_stretch_apr=FixedPoint(0.001), + factory_max_time_stretch_apr=FixedPoint(1000), + ) + interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) + interface = interactive_hyperdrive.interface + time_stretch = interface.current_pool_state.pool_config.time_stretch + logging.info("Time stretch: %s", time_stretch) + logging.info("Time stretch: %s", time_stretch) - # for row in results_df.itertuples(): - # file.write(f"{row.trade_size},{row.portion},{row.price},{row.rate},{time_stretch_apr}\n") - results_df.to_csv("discoverability.csv", index=False) + max_long = interface.calc_max_long(liquidity) + max_short = interface.calc_max_short(liquidity) + logging.info(f"Max long : base={float(max_long):>10,.0f}") + logging.info(f"Max short: base={float(max_short):>10,.0f}") + for trade_portion in trade_portion_list: + long_price = long_rate = None + trade_size = int(float(max_long) * trade_portion) + logging.info(f"Attempting long trade of {trade_size}") + long_trade = predict_long(interface, base=FixedPoint(trade_size)) + pool_state = deepcopy(interface.current_pool_state) + pool_state.pool_info.bond_reserves += long_trade.pool.bonds + pool_state.pool_info.share_reserves += long_trade.pool.shares + long_price = interface.calc_spot_price(pool_state) + long_rate = interface.calc_fixed_rate(pool_state) + records.append((trade_size, trade_portion, long_price, long_rate, time_stretch_apr)) + for trade_portion in trade_portion_list: + short_price = short_rate = None + trade_size = int(float(max_short) * trade_portion) + logging.info(f"Attempting short trade of {trade_size}") + short_trade = predict_short(interface, bonds=FixedPoint(trade_size)) + pool_state = deepcopy(interface.current_pool_state) + pool_state.pool_info.bond_reserves += short_trade.pool.bonds + pool_state.pool_info.share_reserves += short_trade.pool.shares + short_price = interface.calc_spot_price(pool_state) + short_rate = interface.calc_fixed_rate(pool_state) + records.append((-trade_size, -trade_portion, short_price, short_rate, time_stretch_apr)) + new_result = pd.DataFrame.from_records(records, columns=["trade_size", "portion", "price", "rate", "time_stretch_apr"]) + logging.info(f"\n{new_result[['trade_size', 'portion', 'price', 'rate']]}") + previous_results = pd.read_csv("discoverability.csv", index_col=0) + all_results = pd.concat([previous_results, new_result]) + all_results.to_csv("discoverability.csv", index=False) @pytest.mark.anvil From 894ad918c92b66dd3f06f6859cf60f6a9677b447 Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 1 May 2024 16:16:40 -0400 Subject: [PATCH 06/12] format imports --- src/agent0/core/hyperdrive/interactive/econ_tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index 28ad86fbe9..e6afe362db 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -7,9 +7,10 @@ import numpy as np import pandas as pd import pytest +from fixedpointmath import FixedPoint + from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive from agent0.core.utilities import predict_long, predict_short -from fixedpointmath import FixedPoint YEAR_IN_SECONDS = 31_536_000 From 774b474a579dc7d492e44ab9d322c4324829f942 Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 1 May 2024 16:19:26 -0400 Subject: [PATCH 07/12] import fromright location --- src/agent0/core/hyperdrive/interactive/econ_tests.py | 2 +- src/agent0/core/hyperdrive/utilities/__init__.py | 1 + src/agent0/core/utilities/__init__.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index e6afe362db..5f6e0582aa 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -10,7 +10,7 @@ from fixedpointmath import FixedPoint from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive -from agent0.core.utilities import predict_long, predict_short +from agent0.core.hyperdrive.utilities import predict_long, predict_short YEAR_IN_SECONDS = 31_536_000 diff --git a/src/agent0/core/hyperdrive/utilities/__init__.py b/src/agent0/core/hyperdrive/utilities/__init__.py index e69de29bb2..d7589b34a3 100644 --- a/src/agent0/core/hyperdrive/utilities/__init__.py +++ b/src/agent0/core/hyperdrive/utilities/__init__.py @@ -0,0 +1 @@ +from .predict import predict_long, predict_short diff --git a/src/agent0/core/utilities/__init__.py b/src/agent0/core/utilities/__init__.py index 98bccfdfcf..e69de29bb2 100644 --- a/src/agent0/core/utilities/__init__.py +++ b/src/agent0/core/utilities/__init__.py @@ -1 +0,0 @@ -from .predict import predict_long, predict_short \ No newline at end of file From 9e4d500aa0917dc3bfcdb87b35241d578fdc0938 Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 1 May 2024 16:24:48 -0400 Subject: [PATCH 08/12] switch to calc_spot_rate --- .../core/hyperdrive/interactive/econ_tests.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index 5f6e0582aa..a302b968e1 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -46,7 +46,7 @@ def test_symmetry(chain: LocalChain): # parametrize with time_stretch_apr -@pytest.mark.parametrize("time_stretch_apr", [1]) +@pytest.mark.parametrize("time_stretch_apr", [0.01, 0.05, 0.1, 0.5, 1]) @pytest.mark.anvil def test_discoverability(chain: LocalChain, time_stretch_apr: float): """Test discoverability of rates by time stretch.""" @@ -86,7 +86,7 @@ def test_discoverability(chain: LocalChain, time_stretch_apr: float): pool_state.pool_info.bond_reserves += long_trade.pool.bonds pool_state.pool_info.share_reserves += long_trade.pool.shares long_price = interface.calc_spot_price(pool_state) - long_rate = interface.calc_fixed_rate(pool_state) + long_rate = interface.calc_spot_rate(pool_state) records.append((trade_size, trade_portion, long_price, long_rate, time_stretch_apr)) for trade_portion in trade_portion_list: short_price = short_rate = None @@ -97,7 +97,7 @@ def test_discoverability(chain: LocalChain, time_stretch_apr: float): pool_state.pool_info.bond_reserves += short_trade.pool.bonds pool_state.pool_info.share_reserves += short_trade.pool.shares short_price = interface.calc_spot_price(pool_state) - short_rate = interface.calc_fixed_rate(pool_state) + short_rate = interface.calc_spot_rate(pool_state) records.append((-trade_size, -trade_portion, short_price, short_rate, time_stretch_apr)) new_result = pd.DataFrame.from_records(records, columns=["trade_size", "portion", "price", "rate", "time_stretch_apr"]) logging.info(f"\n{new_result[['trade_size', 'portion', 'price', 'rate']]}") @@ -128,7 +128,7 @@ def test_lp_pnl(chain: LocalChain): manual_agent = interactive_hyperdrive.init_agent(base=FixedPoint(1e9)) manual_agent.open_short(bonds=FixedPoint(9_050_000)) - logging.info(f"New rate: {interface.calc_fixed_rate()}") + logging.info(f"New rate: {interface.calc_spot_rate()}") def test_lp_pnl_calculator(chain: LocalChain): @@ -182,7 +182,7 @@ def test_lp_pnl_calculator(chain: LocalChain): if k is not None: print(f"{k:6}: {float(v):>17,.0f}") pool_state = deepcopy(interactive_hyperdrive.interface.current_pool_state) - print("fixed rate is", interactive_hyperdrive.interface.calc_fixed_rate(pool_state)) + print("fixed rate is", interactive_hyperdrive.interface.calc_spot_rate(pool_state)) print(f"lp_share_price={pool_state.pool_info.lp_share_price}") print(f"=== TRADE ({trade_size:,.0f}) ===") @@ -197,7 +197,7 @@ def test_lp_pnl_calculator(chain: LocalChain): for agent in interactive_hyperdrive._pool_agents: # pylint: disable=protected-access spent_base[agent.name] = starting_base[agent.name] - agent.wallet.balance.amount ending_pool_state = deepcopy(interactive_hyperdrive.interface.current_pool_state) - new_fixed_rate = interactive_hyperdrive.interface.calc_fixed_rate(ending_pool_state) + new_fixed_rate = interactive_hyperdrive.interface.calc_spot_rate(ending_pool_state) print("fixed rate is", new_fixed_rate) print(f"lp_share_price={ending_pool_state.pool_info.lp_share_price}") # set variable rate equal to fixed rate @@ -240,7 +240,7 @@ def test_lp_pnl_calculator(chain: LocalChain): lp_larry_return_abs = lp_larry_ending_base - lp_larry_starting_base lp_larry_return_pct = lp_larry_return_abs / lp_larry_starting_base ending_pool_state = deepcopy(interactive_hyperdrive.interface.current_pool_state) - print("fixed rate is", interactive_hyperdrive.interface.calc_fixed_rate(ending_pool_state)) + print("fixed rate is", interactive_hyperdrive.interface.calc_spot_rate(ending_pool_state)) print(f"lp_share_price={ending_pool_state.pool_info.lp_share_price}") print("returns:") # calculate rule of thumb return From 7434b9621eef5f10ba1b4ed29f23611c3cd4b6e7 Mon Sep 17 00:00:00 2001 From: Mihai Date: Fri, 3 May 2024 11:39:19 -0400 Subject: [PATCH 09/12] sweep after adding liquidity --- .../core/hyperdrive/interactive/econ_tests.py | 116 ++++++++++++------ 1 file changed, 80 insertions(+), 36 deletions(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index a302b968e1..b1c4aeaef3 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -1,6 +1,7 @@ """Tests of economic intuition.""" import logging +import os from copy import deepcopy from decimal import Decimal @@ -10,7 +11,8 @@ from fixedpointmath import FixedPoint from agent0.core.hyperdrive.interactive import LocalChain, LocalHyperdrive -from agent0.core.hyperdrive.utilities import predict_long, predict_short +from agent0.core.hyperdrive.interactive.local_hyperdrive_agent import LocalHyperdriveAgent +from agent0.ethpy.hyperdrive.interface.read_write_interface import HyperdriveReadWriteInterface YEAR_IN_SECONDS = 31_536_000 @@ -44,13 +46,35 @@ def test_symmetry(chain: LocalChain): print(shares_in) assert shares_out != shares_in +def calc_price_and_rate(interface:HyperdriveReadWriteInterface): + price = interface.calc_spot_price() + rate = interface.calc_spot_rate() + return price, rate + +def trade_long(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): + agent.open_long(base=FixedPoint(trade_size)) + return calc_price_and_rate(interface) + +def trade_short(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): + agent.open_short(bonds=FixedPoint(trade_size)) + return calc_price_and_rate(interface) + +def trade_liq(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): + agent.add_liquidity(base=trade_size) + return calc_price_and_rate(interface) # parametrize with time_stretch_apr -@pytest.mark.parametrize("time_stretch_apr", [0.01, 0.05, 0.1, 0.5, 1]) +# TODO: add back 1% time_stretch_apr, currently failing in rust (https://github.com/delvtech/hyperdrive-rs/issues/62) +# @pytest.mark.parametrize("time_stretch_apr", [0.01, 0.05, 0.1, 0.2, 0.3]) +@pytest.mark.parametrize( + "trial,time_stretch_apr, trade_portion_one", + [(1, 0.2, 0.90), (2, 0.2, 0.99)], + # [(1, 0.2, 0.99)], +) @pytest.mark.anvil -def test_discoverability(chain: LocalChain, time_stretch_apr: float): +def test_discoverability(chain: LocalChain, trial: int, time_stretch_apr: float, trade_portion_one: float): """Test discoverability of rates by time stretch.""" - liquidity = FixedPoint(10_000_000) + liquidity = FixedPoint(100) trade_portion_list = [*np.arange(0.1, 1.0, 0.1), 0.99] records = [] logging.info(f"Time stretch APR: {time_stretch_apr}") @@ -67,42 +91,62 @@ def test_discoverability(chain: LocalChain, time_stretch_apr: float): factory_min_time_stretch_apr=FixedPoint(0.001), factory_max_time_stretch_apr=FixedPoint(1000), ) - interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) - interface = interactive_hyperdrive.interface + hyperdrive:LocalHyperdrive = LocalHyperdrive(chain, interactive_config) + agent:LocalHyperdriveAgent = hyperdrive.init_agent(base=FixedPoint(1e18)) + interface = hyperdrive.interface time_stretch = interface.current_pool_state.pool_config.time_stretch logging.info("Time stretch: %s", time_stretch) logging.info("Time stretch: %s", time_stretch) - max_long = interface.calc_max_long(liquidity) - max_short = interface.calc_max_short(liquidity) - logging.info(f"Max long : base={float(max_long):>10,.0f}") - logging.info(f"Max short: base={float(max_short):>10,.0f}") - for trade_portion in trade_portion_list: - long_price = long_rate = None - trade_size = int(float(max_long) * trade_portion) - logging.info(f"Attempting long trade of {trade_size}") - long_trade = predict_long(interface, base=FixedPoint(trade_size)) - pool_state = deepcopy(interface.current_pool_state) - pool_state.pool_info.bond_reserves += long_trade.pool.bonds - pool_state.pool_info.share_reserves += long_trade.pool.shares - long_price = interface.calc_spot_price(pool_state) - long_rate = interface.calc_spot_rate(pool_state) - records.append((trade_size, trade_portion, long_price, long_rate, time_stretch_apr)) - for trade_portion in trade_portion_list: - short_price = short_rate = None - trade_size = int(float(max_short) * trade_portion) - logging.info(f"Attempting short trade of {trade_size}") - short_trade = predict_short(interface, bonds=FixedPoint(trade_size)) - pool_state = deepcopy(interface.current_pool_state) - pool_state.pool_info.bond_reserves += short_trade.pool.bonds - pool_state.pool_info.share_reserves += short_trade.pool.shares - short_price = interface.calc_spot_price(pool_state) - short_rate = interface.calc_spot_rate(pool_state) - records.append((-trade_size, -trade_portion, short_price, short_rate, time_stretch_apr)) - new_result = pd.DataFrame.from_records(records, columns=["trade_size", "portion", "price", "rate", "time_stretch_apr"]) - logging.info(f"\n{new_result[['trade_size', 'portion', 'price', 'rate']]}") - previous_results = pd.read_csv("discoverability.csv", index_col=0) - all_results = pd.concat([previous_results, new_result]) + max_long = interface.calc_max_long(budget=agent.wallet.balance.amount) + max_short = interface.calc_max_short(budget=agent.wallet.balance.amount) + logging.info(f"Max long : base={float(max_long):>10,.0f}") + logging.info(f"Max short: bonds={float(max_short):>10,.0f}") + + # we short + trade_size = int(float(max_short) * trade_portion_one) + price, rate = trade_short(interface, agent, trade_size) + records.append((trial, "first", interface.calc_effective_share_reserves(), -trade_size, -trade_portion_one, price, rate, time_stretch_apr)) + price, rate = trade_liq(interface, agent, liquidity) + records.append((trial, "addliq", interface.calc_effective_share_reserves(), -trade_size, -trade_portion_one, price, rate, time_stretch_apr)) + del price, rate, trade_size + + # save the snapshot + chain.save_snapshot() + + # then we short + max_short_two = interface.calc_max_short(budget=agent.wallet.balance.amount) + logging.info(f"Max short: bonds={float(max_short_two):>10,.0f}") + for trade_portion_two in trade_portion_list: + chain.load_snapshot() + trade_size = int(float(max_short_two) * trade_portion_two) + try: + price, rate = trade_short(interface, agent, trade_size) + records.append((trial, "second", interface.calc_effective_share_reserves(), -trade_size, -trade_portion_two, price, rate, time_stretch_apr)) + logging.info("trade_portion=%s, rate=%s", -trade_portion_two, rate) + except: + logging.info("FAILED trade_portion=%s, rate=NA", -trade_portion_two) + pass + # then we long + max_long_two = interface.calc_max_long(budget=agent.wallet.balance.amount) + logging.info(f"Max long : base={float(max_long_two):>10,.0f}") + for trade_portion_two in trade_portion_list: + chain.load_snapshot() + trade_size = int(float(max_long_two) * trade_portion_two) + try: + price, rate = trade_long(interface, agent, trade_size) + records.append((trial, "second", interface.calc_effective_share_reserves(), trade_size, trade_portion_two, price, rate, time_stretch_apr)) + logging.info("trade_portion=%s, rate=%s", trade_portion_two, rate) + except: + logging.info("FAILED trade_portion=%s, rate=NA", trade_portion_two) + pass + columns = ["trial", "type", "liquidity", "trade_size", "portion", "price", "rate", "time_stretch_apr"] + new_result = pd.DataFrame.from_records(records, columns=columns) + logging.info(f"\n{new_result[columns[:-1]]}") + previous_results = pd.read_csv("discoverability.csv") if os.path.exists("discoverability.csv") else pd.DataFrame() + logging.info(f"previous_results.shape: {previous_results.shape}") + all_results = pd.concat([previous_results, new_result], ignore_index=True, axis=0) + logging.info(f"all_results.shape: {all_results.shape}") all_results.to_csv("discoverability.csv", index=False) From d37d62f9e69b9e52465bfe931d42c8b8b1efa1ae Mon Sep 17 00:00:00 2001 From: Mihai Date: Fri, 3 May 2024 18:48:59 -0400 Subject: [PATCH 10/12] make general --- .../core/hyperdrive/interactive/econ_tests.py | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index b1c4aeaef3..dd651cd249 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -51,13 +51,27 @@ def calc_price_and_rate(interface:HyperdriveReadWriteInterface): rate = interface.calc_spot_rate() return price, rate +def trade(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_portion, max_long, max_short): + relevant_max = max_long if trade_portion > 0 else max_short + trade_size = int(float(relevant_max) * trade_portion) + trade_result = trade_long(interface, agent, trade_size) if trade_size > 0 else trade_short(interface, agent, abs(trade_size)) + return *trade_result, trade_size + def trade_long(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): - agent.open_long(base=FixedPoint(trade_size)) - return calc_price_and_rate(interface) + try: + agent.open_long(base=FixedPoint(trade_size)) + return calc_price_and_rate(interface) + except: + pass + return None, None def trade_short(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): - agent.open_short(bonds=FixedPoint(trade_size)) - return calc_price_and_rate(interface) + try: + agent.open_short(bonds=FixedPoint(trade_size)) + return calc_price_and_rate(interface) + except: + pass + return None, None def trade_liq(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): agent.add_liquidity(base=trade_size) @@ -76,6 +90,7 @@ def test_discoverability(chain: LocalChain, trial: int, time_stretch_apr: float, """Test discoverability of rates by time stretch.""" liquidity = FixedPoint(100) trade_portion_list = [*np.arange(0.1, 1.0, 0.1), 0.99] + trade_portion_list += [-x for x in trade_portion_list] # add negative portions records = [] logging.info(f"Time stretch APR: {time_stretch_apr}") interactive_config = LocalHyperdrive.Config( @@ -102,13 +117,10 @@ def test_discoverability(chain: LocalChain, trial: int, time_stretch_apr: float, max_short = interface.calc_max_short(budget=agent.wallet.balance.amount) logging.info(f"Max long : base={float(max_long):>10,.0f}") logging.info(f"Max short: bonds={float(max_short):>10,.0f}") - - # we short - trade_size = int(float(max_short) * trade_portion_one) - price, rate = trade_short(interface, agent, trade_size) - records.append((trial, "first", interface.calc_effective_share_reserves(), -trade_size, -trade_portion_one, price, rate, time_stretch_apr)) + price, rate, trade_size = trade(interface, agent, trade_portion_one, max_long, max_short) + records.append((trial, "first", interface.calc_effective_share_reserves(), trade_size, trade_portion_one, price, rate, time_stretch_apr)) price, rate = trade_liq(interface, agent, liquidity) - records.append((trial, "addliq", interface.calc_effective_share_reserves(), -trade_size, -trade_portion_one, price, rate, time_stretch_apr)) + records.append((trial, "addliq", interface.calc_effective_share_reserves(), trade_size, trade_portion_one, price, rate, time_stretch_apr)) del price, rate, trade_size # save the snapshot @@ -116,30 +128,14 @@ def test_discoverability(chain: LocalChain, trial: int, time_stretch_apr: float, # then we short max_short_two = interface.calc_max_short(budget=agent.wallet.balance.amount) - logging.info(f"Max short: bonds={float(max_short_two):>10,.0f}") - for trade_portion_two in trade_portion_list: - chain.load_snapshot() - trade_size = int(float(max_short_two) * trade_portion_two) - try: - price, rate = trade_short(interface, agent, trade_size) - records.append((trial, "second", interface.calc_effective_share_reserves(), -trade_size, -trade_portion_two, price, rate, time_stretch_apr)) - logging.info("trade_portion=%s, rate=%s", -trade_portion_two, rate) - except: - logging.info("FAILED trade_portion=%s, rate=NA", -trade_portion_two) - pass - # then we long max_long_two = interface.calc_max_long(budget=agent.wallet.balance.amount) + logging.info(f"Max short: bonds={float(max_short_two):>10,.0f}") logging.info(f"Max long : base={float(max_long_two):>10,.0f}") for trade_portion_two in trade_portion_list: chain.load_snapshot() - trade_size = int(float(max_long_two) * trade_portion_two) - try: - price, rate = trade_long(interface, agent, trade_size) - records.append((trial, "second", interface.calc_effective_share_reserves(), trade_size, trade_portion_two, price, rate, time_stretch_apr)) - logging.info("trade_portion=%s, rate=%s", trade_portion_two, rate) - except: - logging.info("FAILED trade_portion=%s, rate=NA", trade_portion_two) - pass + price, rate, trade_size = trade(interface, agent, trade_portion_two, max_long_two, max_short_two) + records.append((trial, "second", interface.calc_effective_share_reserves(), trade_size, trade_portion_two, price, rate, time_stretch_apr)) + logging.info("trade_portion=%s, rate=%s", trade_portion_two, rate) columns = ["trial", "type", "liquidity", "trade_size", "portion", "price", "rate", "time_stretch_apr"] new_result = pd.DataFrame.from_records(records, columns=columns) logging.info(f"\n{new_result[columns[:-1]]}") From 8d59bbec9f20afcfeb81c1a82c1285296b80ea1f Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 8 May 2024 20:14:29 -0400 Subject: [PATCH 11/12] set shorts as default --- src/agent0/core/hyperdrive/interactive/econ_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index dd651cd249..1b64e9cb55 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -82,7 +82,7 @@ def trade_liq(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent # @pytest.mark.parametrize("time_stretch_apr", [0.01, 0.05, 0.1, 0.2, 0.3]) @pytest.mark.parametrize( "trial,time_stretch_apr, trade_portion_one", - [(1, 0.2, 0.90), (2, 0.2, 0.99)], + [(1, 0.2, -0.90), (2, 0.2, -0.99)], # [(1, 0.2, 0.99)], ) @pytest.mark.anvil From f57be8e7c6c2f690c89e6addb767a2bb69741143 Mon Sep 17 00:00:00 2001 From: Mihai Date: Tue, 28 May 2024 13:00:32 -0400 Subject: [PATCH 12/12] update and use float trade size --- .../core/hyperdrive/interactive/econ_tests.py | 94 +++++++++++-------- .../interactive/local_hyperdrive.py | 2 +- 2 files changed, 55 insertions(+), 41 deletions(-) diff --git a/src/agent0/core/hyperdrive/interactive/econ_tests.py b/src/agent0/core/hyperdrive/interactive/econ_tests.py index ed95143b8b..2722b694c9 100644 --- a/src/agent0/core/hyperdrive/interactive/econ_tests.py +++ b/src/agent0/core/hyperdrive/interactive/econ_tests.py @@ -53,25 +53,29 @@ def calc_price_and_rate(interface:HyperdriveReadWriteInterface): def trade(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_portion, max_long, max_short): relevant_max = max_long if trade_portion > 0 else max_short - trade_size = int(float(relevant_max) * trade_portion) + trade_size = float(relevant_max) * trade_portion trade_result = trade_long(interface, agent, trade_size) if trade_size > 0 else trade_short(interface, agent, abs(trade_size)) return *trade_result, trade_size def trade_long(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): try: - agent.open_long(base=FixedPoint(trade_size)) - return calc_price_and_rate(interface) + trade_result = agent.open_long(base=FixedPoint(trade_size)) + base_traded = trade_result.amount + bonds_traded = trade_result.bond_amount + return *calc_price_and_rate(interface), base_traded, bonds_traded except: pass - return None, None + return None, None, None, None def trade_short(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): try: - agent.open_short(bonds=FixedPoint(trade_size)) - return calc_price_and_rate(interface) + trade_result = agent.open_short(bonds=FixedPoint(trade_size)) + base_traded = -trade_result.amount + bonds_traded = -trade_result.bond_amount + return *calc_price_and_rate(interface), base_traded, bonds_traded except: pass - return None, None + return None, None, None, None def trade_liq(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent, trade_size): agent.add_liquidity(base=trade_size) @@ -82,13 +86,14 @@ def trade_liq(interface:HyperdriveReadWriteInterface, agent:LocalHyperdriveAgent # @pytest.mark.parametrize("time_stretch_apr", [0.01, 0.05, 0.1, 0.2, 0.3]) @pytest.mark.parametrize( "trial,time_stretch_apr, trade_portion_one", - [(1, 0.2, -0.90), (2, 0.2, -0.99)], - # [(1, 0.2, 0.99)], + # [(1, 0.2, -0.90), (2, 0.2, -0.99), (3, 0.15, -0.90), (4, 0.15, -0.99), (5, 0.1, -0.90), (6, 0.1, -0.99), (7, 0.05, -0.90), (8, 0.05, -0.99)], + [(1, 0.1, -0.995)], ) @pytest.mark.anvil -def test_discoverability(chain: LocalChain, trial: int, time_stretch_apr: float, trade_portion_one: float): +def test_discoverability(fast_chain_fixture: LocalChain, trial: int, time_stretch_apr: float, trade_portion_one: float): """Test discoverability of rates by time stretch.""" - liquidity = FixedPoint(100) + liquidity = FixedPoint(100) # stEth ($3000*100 = $30k) + liquidity = FixedPoint(10_000) # Dai ($10k) trade_portion_list = [*np.arange(0.1, 1.0, 0.1), 0.99] trade_portion_list += [-x for x in trade_portion_list] # add negative portions records = [] @@ -106,37 +111,39 @@ def test_discoverability(chain: LocalChain, trial: int, time_stretch_apr: float, factory_min_time_stretch_apr=FixedPoint(0.001), factory_max_time_stretch_apr=FixedPoint(1000), ) - hyperdrive:LocalHyperdrive = LocalHyperdrive(chain, interactive_config) - agent:LocalHyperdriveAgent = hyperdrive.init_agent(base=FixedPoint(1e18)) + hyperdrive:LocalHyperdrive = LocalHyperdrive(fast_chain_fixture, interactive_config) + agent:LocalHyperdriveAgent = hyperdrive.init_agent(base=FixedPoint(1e18), eth=FixedPoint(1e18)) interface = hyperdrive.interface time_stretch = interface.current_pool_state.pool_config.time_stretch logging.info("Time stretch: %s", time_stretch) logging.info("Time stretch: %s", time_stretch) - max_long = interface.calc_max_long(budget=agent.wallet.balance.amount) - max_short = interface.calc_max_short(budget=agent.wallet.balance.amount) + max_long = interface.calc_max_long(budget=agent.get_positions().balance.amount) + max_short = interface.calc_max_short(budget=agent.get_positions().balance.amount) logging.info(f"Max long : base={float(max_long):>10,.0f}") logging.info(f"Max short: bonds={float(max_short):>10,.0f}") - price, rate, trade_size = trade(interface, agent, trade_portion_one, max_long, max_short) - records.append((trial, "first", interface.calc_effective_share_reserves(), trade_size, trade_portion_one, price, rate, time_stretch_apr)) - price, rate = trade_liq(interface, agent, liquidity) - records.append((trial, "addliq", interface.calc_effective_share_reserves(), trade_size, trade_portion_one, price, rate, time_stretch_apr)) + price, rate, base_traded, bonds_traded, trade_size = trade(interface, agent, trade_portion_one, max_long, max_short) + records.append((trial, "first", interface.calc_effective_share_reserves(), trade_size, base_traded, bonds_traded, trade_portion_one, price, rate, time_stretch_apr)) + price, rate = trade_liq(interface, agent, liquidity*100) + records.append((trial, "addliq", interface.calc_effective_share_reserves(), trade_size, None, None, trade_portion_one, price, rate, time_stretch_apr)) del price, rate, trade_size # save the snapshot - chain.save_snapshot() + fast_chain_fixture.save_snapshot() # then we short - max_short_two = interface.calc_max_short(budget=agent.wallet.balance.amount) - max_long_two = interface.calc_max_long(budget=agent.wallet.balance.amount) + max_short_two = interface.calc_max_short(budget=agent.get_positions().balance.amount) + max_long_two = interface.calc_max_long(budget=agent.get_positions().balance.amount) logging.info(f"Max short: bonds={float(max_short_two):>10,.0f}") logging.info(f"Max long : base={float(max_long_two):>10,.0f}") for trade_portion_two in trade_portion_list: - chain.load_snapshot() - price, rate, trade_size = trade(interface, agent, trade_portion_two, max_long_two, max_short_two) - records.append((trial, "second", interface.calc_effective_share_reserves(), trade_size, trade_portion_two, price, rate, time_stretch_apr)) + fast_chain_fixture.load_snapshot() + price = rate = base_traded = bonds_traded = trade_size = None + price, rate, base_traded, bonds_traded, trade_size = trade(interface, agent, trade_portion_two, max_long_two, max_short_two) + records.append((trial, "second", interface.calc_effective_share_reserves(), trade_size, base_traded, bonds_traded, trade_portion_two, price, rate, time_stretch_apr)) logging.info("trade_portion=%s, rate=%s", trade_portion_two, rate) - columns = ["trial", "type", "liquidity", "trade_size", "portion", "price", "rate", "time_stretch_apr"] + del price, rate, trade_size, bonds_traded, base_traded + columns = ["trial", "type", "liquidity", "trade_size", "base_traded", "bonds_traded", "portion", "price", "rate", "time_stretch_apr"] new_result = pd.DataFrame.from_records(records, columns=columns) logging.info(f"\n{new_result[columns[:-1]]}") previous_results = pd.read_csv("discoverability.csv") if os.path.exists("discoverability.csv") else pd.DataFrame() @@ -145,9 +152,16 @@ def test_discoverability(chain: LocalChain, trial: int, time_stretch_apr: float, logging.info(f"all_results.shape: {all_results.shape}") all_results.to_csv("discoverability.csv", index=False) + # 1. short + # 2. add liquidity + # 3. open long + # up to maximum circuit breaker limit (15%) + # how does circuit breaker work? + # + @pytest.mark.anvil -def test_lp_pnl(chain: LocalChain): +def test_lp_pnl(fast_chain_fixture: LocalChain): """Test whether LP PNL matches our rule of thumb.""" liquidity = FixedPoint(10_000_000) time_stretch_apr_list = [0.05] @@ -163,7 +177,7 @@ def test_lp_pnl(chain: LocalChain): initial_liquidity=liquidity, initial_time_stretch_apr=FixedPoint(str(time_stretch_apr)), ) - interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) + interactive_hyperdrive = LocalHyperdrive(fast_chain_fixture, interactive_config) interface = interactive_hyperdrive.interface manual_agent = interactive_hyperdrive.init_agent(base=FixedPoint(1e9)) @@ -171,7 +185,7 @@ def test_lp_pnl(chain: LocalChain): logging.info(f"New rate: {interface.calc_spot_rate()}") -def test_lp_pnl_calculator(chain: LocalChain): +def test_lp_pnl_calculator(fast_chain_fixture: LocalChain): """Calculate LP PNL given a set of parameters.""" initial_liquidity = FixedPoint(10_000_000) time_stretch_apr = 0.05 @@ -188,7 +202,7 @@ def test_lp_pnl_calculator(chain: LocalChain): initial_fixed_apr=FixedPoint(str(initial_fixed_apr)), initial_variable_rate=FixedPoint(str(initial_fixed_apr)), ) - max_short = LocalHyperdrive(chain, interactive_config).interface.calc_max_short(budget=FixedPoint(1e12)) + max_short = LocalHyperdrive(fast_chain_fixture, interactive_config).interface.calc_max_short(budget=FixedPoint(1e12)) increment = int(max_short) // 10 records = [] for trade_size in range(increment, 11 * increment, increment): @@ -203,9 +217,9 @@ def test_lp_pnl_calculator(chain: LocalChain): initial_fixed_apr=FixedPoint(str(initial_fixed_apr)), initial_variable_rate=FixedPoint(str(initial_fixed_apr)), ) - interactive_hyperdrive = LocalHyperdrive(chain, interactive_config) + interactive_hyperdrive = LocalHyperdrive(fast_chain_fixture, interactive_config) lp_larry = interactive_hyperdrive.init_agent( - base=FixedPoint(0), name="larry", private_key=chain.get_deployer_account_private_key() + base=FixedPoint(0), name="larry", private_key=fast_chain_fixture.get_deployer_account_private_key() ) manual_agent = interactive_hyperdrive.init_agent(base=FixedPoint(1e12)) start_timestamp = interactive_hyperdrive.interface.current_pool_state.block_time @@ -217,7 +231,7 @@ def test_lp_pnl_calculator(chain: LocalChain): # larry is the deployer, their base balance is the initial liquidity starting_base[agent.name] = initial_liquidity else: - starting_base[agent.name] = agent.wallet.balance.amount + starting_base[agent.name] = agent.get_positions().balance.amount for k, v in starting_base.items(): if k is not None: print(f"{k:6}: {float(v):>17,.0f}") @@ -231,11 +245,11 @@ def test_lp_pnl_calculator(chain: LocalChain): event = event_list[0] if isinstance(event_list, list) else event_list effective_spot_price = event.base_proceeds / event.bond_amount effective_interest_rate = (FixedPoint(1) - effective_spot_price) / effective_spot_price - position_size = manual_agent.agent.wallet.shorts[list(manual_agent.agent.wallet.shorts)[0]].balance + position_size = manual_agent.agent.get_positions().shorts[list(manual_agent.agent.get_positions().shorts)[0]].balance print(f" position size is {float(position_size):,.0f} bonds") spent_base = {} for agent in interactive_hyperdrive._pool_agents: # pylint: disable=protected-access - spent_base[agent.name] = starting_base[agent.name] - agent.wallet.balance.amount + spent_base[agent.name] = starting_base[agent.name] - agent.get_positions().balance.amount ending_pool_state = deepcopy(interactive_hyperdrive.interface.current_pool_state) new_fixed_rate = interactive_hyperdrive.interface.calc_spot_rate(ending_pool_state) print("fixed rate is", new_fixed_rate) @@ -249,29 +263,29 @@ def test_lp_pnl_calculator(chain: LocalChain): advance_time_to = YEAR_IN_SECONDS advance_time_seconds = int(advance_time_to) - time_already_passed print(f" advancing {advance_time_seconds} seconds... ", end="") - chain.advance_time(advance_time_seconds, create_checkpoints=False) + fast_chain_fixture.advance_time(advance_time_seconds, create_checkpoints=False) print("done.") current_timestamp = interactive_hyperdrive.interface.current_pool_state.block_time print(f"new timestamp is {current_timestamp}") # close all positions print("before agent action") - for short in manual_agent.agent.wallet.shorts: + for short in manual_agent.agent.get_positions().shorts: print( f" {short}: time to maturity {short-current_timestamp} seconds ({(short-current_timestamp)/YEAR_IN_SECONDS:0.5f} years)" ) manual_agent.liquidate() print("after agent action") - for short in manual_agent.agent.wallet.shorts: + for short in manual_agent.agent.get_positions().shorts: print( f" {short}: time to maturity {short-current_timestamp} seconds ({(short-current_timestamp)/YEAR_IN_SECONDS:0.5f} years)" ) - lp_larry.remove_liquidity(lp_larry.wallet.lp_tokens - interactive_config.minimum_share_reserves * 2) + lp_larry.remove_liquidity(lp_larry.get_positions().lp_tokens - interactive_config.minimum_share_reserves * 2) print("=== END ===") print("ending WETH balances:") ending_base = {} for agent in interactive_hyperdrive._pool_agents: # pylint: disable=protected-access - ending_base[agent.name] = agent.wallet.balance.amount + ending_base[agent.name] = agent.get_positions().balance.amount for k, v in ending_base.items(): if k is not None: print(f" {k:6}: {float(v):>17,.0f}") diff --git a/src/agent0/core/hyperdrive/interactive/local_hyperdrive.py b/src/agent0/core/hyperdrive/interactive/local_hyperdrive.py index 12e432741a..9d7307b389 100644 --- a/src/agent0/core/hyperdrive/interactive/local_hyperdrive.py +++ b/src/agent0/core/hyperdrive/interactive/local_hyperdrive.py @@ -163,7 +163,7 @@ class Config(Hyperdrive.Config): """The upper bound on the governance zombie fee that governance can set.""" # Pool Deploy Config variables - minimum_share_reserves: FixedPoint = FixedPoint(10) + minimum_share_reserves: FixedPoint = FixedPoint(1) """The minimum share reserves.""" minimum_transaction_amount: FixedPoint = FixedPoint("0.001") """The minimum amount of tokens that a position can be opened or closed with."""