Skip to content

Commit

Permalink
test trade prediction (#1285)
Browse files Browse the repository at this point in the history
add tests for predicting the outcome of a trade
  • Loading branch information
wakamex authored Jan 26, 2024
1 parent b7aa96c commit 85bad80
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 3 deletions.
4 changes: 2 additions & 2 deletions lib/agent0/agent0/hyperdrive/policies/lpandarb.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,8 @@ def action(
# Open a new short, if there's still a need, and we have money
if we_have_money and bonds_needed > self.minimum_trade_amount:
max_short_bonds = interface.calc_max_short(wallet.balance.amount)
amount = min(bonds_needed, max_short_bonds)
action_list.append(interface.open_short_trade(amount, self.slippage_tolerance))
amount_bonds = min(bonds_needed, max_short_bonds)
action_list.append(interface.open_short_trade(amount_bonds, self.slippage_tolerance))

if self.policy_config.done_on_empty and len(action_list) == 0:
return [], True
Expand Down
182 changes: 181 additions & 1 deletion lib/agent0/agent0/hyperdrive/policies/lpandarb_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
from __future__ import annotations

import logging
from copy import deepcopy

import pytest
from fixedpointmath import FixedPoint

from agent0.hyperdrive.interactive import LocalChain, InteractiveHyperdrive
from agent0.hyperdrive.policies.zoo import Zoo
from agent0.hyperdrive.interactive.chain import Chain
from agent0.hyperdrive.interactive.event_types import CloseLong, CloseShort
from agent0.hyperdrive.interactive.interactive_hyperdrive_agent import InteractiveHyperdriveAgent

Expand All @@ -21,6 +23,9 @@
PRECISION = FixedPoint(1e-5)
YEAR_IN_SECONDS = 31_536_000

# pylint: disable=missing-function-docstring,too-many-statements,logging-fstring-interpolation,missing-return-type-doc
# pylint: disable=missing-return-doc,too-many-function-args


@pytest.fixture(scope="function")
def interactive_hyperdrive(chain: LocalChain) -> InteractiveHyperdrive:
Expand Down Expand Up @@ -54,7 +59,21 @@ def arbitrage_andy(interactive_hyperdrive) -> InteractiveHyperdriveAgent:
-------
InteractiveHyperdriveAgent
Arbitrage Andy interactive hyperdrive agent."""
# create arbitrage andy
return create_arbitrage_andy(interactive_hyperdrive)


def create_arbitrage_andy(interactive_hyperdrive) -> InteractiveHyperdriveAgent:
"""Create Arbitrage Andy interactive hyperdrive agent used to arbitrage the fixed rate to the variable rate.
Arguments
---------
interactive_hyperdrive: InteractiveHyperdrive
Interactive hyperdrive.
Returns
-------
InteractiveHyperdriveAgent
Arbitrage Andy interactive hyperdrive agent."""
andy_base = FixedPoint(1e9)
andy_config = Zoo.lp_and_arb.Config(
lp_portion=FixedPoint(0),
Expand Down Expand Up @@ -301,3 +320,164 @@ def test_reduce_short(interactive_hyperdrive: InteractiveHyperdrive, arbitrage_a
event = event[0] if isinstance(event, list) else event
logging.info("event is %s", event)
assert isinstance(event, CloseShort)


@pytest.mark.anvil
def test_predict_open_long(chain: Chain):
"""Predict oucome of an open long."""
interactive_config = InteractiveHyperdrive.Config(
position_duration=YEAR_IN_SECONDS, # 1 year term
governance_lp_fee=FixedPoint(0.1),
curve_fee=FixedPoint(0.01),
flat_fee=FixedPoint(0),
)
interactive_hyperdrive = InteractiveHyperdrive(chain, interactive_config)
arbitrage_andy = create_arbitrage_andy(interactive_hyperdrive=interactive_hyperdrive)
pool_state = deepcopy(interactive_hyperdrive.hyperdrive_interface.current_pool_state)
starting_bond_reserves = pool_state.pool_info.bond_reserves
starting_share_reserves = pool_state.pool_info.share_reserves
stating_price = interactive_hyperdrive.hyperdrive_interface.calc_spot_price(pool_state)
logging.info("starting bond_reserves is %s (%s)", starting_bond_reserves, type(starting_bond_reserves).__name__)
logging.info("starting share_reserves is %s (%s)", starting_share_reserves, type(starting_share_reserves).__name__)
logging.info("starting spot price is %s (%s)", stating_price, type(stating_price).__name__)

# start with bonds_needed, convert to base_needed
bonds_needed = FixedPoint(100_000)
spot_price = interactive_hyperdrive.hyperdrive_interface.calc_spot_price(pool_state)
price_discount = FixedPoint(1) - spot_price
curve_fee = pool_state.pool_config.fees.curve
governance_fee = pool_state.pool_config.fees.governance_lp
shares_needed = interactive_hyperdrive.hyperdrive_interface.calc_shares_in_given_bonds_out_down(bonds_needed)
share_price = interactive_hyperdrive.hyperdrive_interface.current_pool_state.pool_info.share_price
base_needed = shares_needed * share_price
# use rust to predict trade outcome
bonds_after_fees = interactive_hyperdrive.hyperdrive_interface.calc_open_long(base_needed)
logging.info("bonds_after_fees is %s", bonds_after_fees)
bond_fees = bonds_after_fees * price_discount * curve_fee
bond_fees_to_pool = bond_fees * (FixedPoint(1) - governance_fee)
bond_fees_to_gov = bond_fees * governance_fee
bonds_before_fees = bonds_after_fees + bond_fees_to_pool + bond_fees_to_gov
logging.info("bonds_before_fees is %s", bonds_before_fees)
logging.info("bond_fees_to_pool is %s", bond_fees_to_pool)
logging.info("bond_fees_to_gov is %s", bond_fees_to_gov)

predicted_delta_bonds = -bonds_after_fees - bond_fees_to_gov
predicted_delta_shares = base_needed / share_price * (FixedPoint(1) - price_discount * curve_fee * governance_fee)
logging.info("predicted delta bonds is %s", predicted_delta_bonds)
logging.info("predicted delta shares is %s", predicted_delta_shares)

# measure pool before trade
pool_state_before = deepcopy(interactive_hyperdrive.hyperdrive_interface.current_pool_state)
pool_bonds_before = pool_state_before.pool_info.bond_reserves
pool_shares_before = pool_state_before.pool_info.share_reserves
# do the trade
event = arbitrage_andy.open_long(base=base_needed)
event = event[0] if isinstance(event, list) else event
actual_event_bonds = event.bond_amount
actual_event_base = event.base_amount
logging.info(
"opened long with input base=%s, output Δbonds= %s%s, Δbase= %s%s",
base_needed,
"+" if actual_event_bonds > 0 else "",
actual_event_bonds,
"+" if actual_event_base > 0 else "",
actual_event_base,
)
# measure pool after trade
pool_state_after = deepcopy(interactive_hyperdrive.hyperdrive_interface.current_pool_state)
pool_bonds_after = pool_state_after.pool_info.bond_reserves
pool_shares_after = pool_state_after.pool_info.share_reserves
logging.info("pool bonds after is %s", pool_bonds_after)
logging.info("pool shares after is %s", pool_shares_after)
actual_delta_bonds = pool_bonds_after - pool_bonds_before
actual_delta_shares = pool_shares_after - pool_shares_before
logging.info("actual delta bonds is %s", actual_delta_bonds)
logging.info("actual delta shares is %s", actual_delta_shares)

bonds_discrepancy = float((actual_delta_bonds - predicted_delta_bonds) / predicted_delta_bonds)
shares_discrepancy = float((actual_delta_shares - predicted_delta_shares) / predicted_delta_shares)
logging.info(f"discrepancy (%) for bonds is {bonds_discrepancy:e}")
logging.info(f"discrepancy (%) for shares is {shares_discrepancy:e}")

assert abs(bonds_discrepancy) < 1e-7
assert abs(shares_discrepancy) < 1e-7


@pytest.mark.anvil
def test_predict_open_short(chain: Chain):
"""Predict oucome of an open short."""
interactive_config = InteractiveHyperdrive.Config(
position_duration=YEAR_IN_SECONDS, # 1 year term
governance_lp_fee=FixedPoint(0.1),
curve_fee=FixedPoint(0.01),
flat_fee=FixedPoint(0),
)
interactive_hyperdrive = InteractiveHyperdrive(chain, interactive_config)
arbitrage_andy = create_arbitrage_andy(interactive_hyperdrive=interactive_hyperdrive)
pool_state = deepcopy(interactive_hyperdrive.hyperdrive_interface.current_pool_state)
starting_bond_reserves = pool_state.pool_info.bond_reserves
starting_share_reserves = pool_state.pool_info.share_reserves
stating_price = interactive_hyperdrive.hyperdrive_interface.calc_spot_price(pool_state)
logging.info("starting bond_reserves is %s (%s)", starting_bond_reserves, type(starting_bond_reserves).__name__)
logging.info("starting share_reserves is %s (%s)", starting_share_reserves, type(starting_share_reserves).__name__)
logging.info("starting spot price is %s (%s)", stating_price, type(stating_price).__name__)

# start with bonds_needed, convert to base_needed
bonds_needed = FixedPoint(100_000)
spot_price = interactive_hyperdrive.hyperdrive_interface.calc_spot_price(pool_state)
price_discount = FixedPoint(1) - spot_price
curve_fee = pool_state.pool_config.fees.curve
governance_fee = pool_state.pool_config.fees.governance_lp
# use rust to predict trade outcome
shares_before_fees = interactive_hyperdrive.hyperdrive_interface.calc_shares_out_given_bonds_in_down(bonds_needed)
logging.info("shares_before_fees is %s", shares_before_fees)
fees = bonds_needed * price_discount * curve_fee
fees_to_pool = fees * (FixedPoint(1) - governance_fee)
fees_to_gov = fees * governance_fee
shares_after_fees = shares_before_fees + fees_to_pool + fees_to_gov
logging.info("shares_after_fees is %s", shares_after_fees)
logging.info("fees_to_pool is %s", fees_to_pool)
logging.info("fees_to_gov is %s", fees_to_gov)

predicted_delta_bonds = bonds_needed
predicted_delta_shares = -shares_before_fees + fees_to_pool
logging.info("predicted delta bonds is %s", predicted_delta_bonds)
logging.info("predicted delta shares is %s", predicted_delta_shares)

# # measure pool before trade
pool_state_before = deepcopy(interactive_hyperdrive.hyperdrive_interface.current_pool_state)
pool_bonds_before = pool_state_before.pool_info.bond_reserves
pool_shares_before = pool_state_before.pool_info.share_reserves
# # do the trade
event = arbitrage_andy.open_short(bonds=bonds_needed)
event = event[0] if isinstance(event, list) else event
actual_event_bonds = event.bond_amount
actual_event_base = event.base_amount
logging.info(
"opened short with input base=%s, output Δbonds= %s%s, Δbase= %s%s",
bonds_needed,
"+" if actual_event_bonds > 0 else "",
actual_event_bonds,
"+" if actual_event_base > 0 else "",
actual_event_base,
)
# # measure pool after trade
pool_state_after = deepcopy(interactive_hyperdrive.hyperdrive_interface.current_pool_state)
pool_bonds_after = pool_state_after.pool_info.bond_reserves
pool_shares_after = pool_state_after.pool_info.share_reserves
logging.info("pool bonds after is %s", pool_bonds_after)
logging.info("pool shares after is %s", pool_shares_after)
actual_delta_bonds = pool_bonds_after - pool_bonds_before
actual_delta_shares = pool_shares_after - pool_shares_before
logging.info("actual delta bonds is %s", actual_delta_bonds)
logging.info("actual delta shares is %s", actual_delta_shares)

bonds_discrepancy = float((actual_delta_bonds - predicted_delta_bonds) / predicted_delta_bonds)
shares_discrepancy = float((actual_delta_shares - predicted_delta_shares) / predicted_delta_shares)
logging.info(f"discrepancy (%) for bonds is {bonds_discrepancy:e}") # pylint: disable=logging-fstring-interpolation
logging.info(
f"discrepancy (%) for shares is {shares_discrepancy:e}"
) # pylint: disable=logging-fstring-interpolation

assert abs(bonds_discrepancy) < 1e-7
assert abs(shares_discrepancy) < 1e-7

0 comments on commit 85bad80

Please sign in to comment.