Skip to content

exploratory uniswap agent to explore target grid price #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ repos:
- "--max-line-length=88"
- "--max-complexity=18"
- "--select=B,C,E,F,W,T4,B9"
- "--ignore=W503,E203"
- "--ignore=W503,E203,W605"
- "--per-file-ignores=__init__.py:F401"
- repo: https://github.com/pycqa/isort
rev: 5.12.0
Expand Down
8,258 changes: 5,609 additions & 2,649 deletions simulations/aave/cache.json

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion simulations/aave/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@ def plot_results(
n_borrow_agents: int,
):
"""
Plot results from the Aave simulation
Plot results from the Aave example simulation
for a single seed.

Will generate the following plots

* Health factor for each borrower
* Collateral assets for each borrower locked in Aave
* Debt assets for each borrower locked in Aave
* Collateral assets for the liquidator
* Debt assets for the liquidator

Plots are saved in results/sim_aave_uniswap

Parameters
----------
Expand Down
71 changes: 36 additions & 35 deletions simulations/aave/sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,47 @@
In this example we consider the interaction between Aave and Uniswap
via the following agents:

1. A `Uniswap Agent` that trades between a Uniswap pool and
an external market, modelled by a Geometric Brownian Motion,
in order to make a profit.

2. Several `Borrow Agents` that borrow from an Aave v3 pool.

3. A `Liquidation Agent` that liquidated those positions from the
`Borrow agents` that are in distress (that is, that their Health
Factors are < 1) as long as the liquidation is profitable for
the liquidation agent.

We consider the following pools and tokens:

- Uniswap v3 pool for WETH and DAI with fee 3000.

- `Borrow agents` borrow DAI and deposit WETH as collateral.

- The price of the risky asset (WETH) in terms of the stablecoin (DAI) in the
external market is modelled by a GBM.

- The price of Uniswap follows the price in the external
* A `Uniswap Agent` that trades between a Uniswap pool and
an external market, modelled by a Geometric Brownian Motion,
in order to make a profit.
* Several `Borrow Agents` that borrow from an Aave v3 pool.
* A `Liquidation Agent` that liquidated those positions from the
`Borrow agents` that are in distress (that is, that their Health
Factors are < 1) as long as the liquidation is profitable for
the liquidation agent.

We consider the following:

* Uniswap v3 pool for WETH and DAI with fee 3000.
* `Borrow agents` borrow DAI and deposit WETH as collateral.
* The price of the risky asset (WETH) in terms of the stablecoin (DAI) in the
external market is modelled by a GBM.
* The price of Uniswap follows the price in the external
market. The Uniswap agent allows that by making the right trade in each step
so that the new Uniswap price is the same as the price in the external market.
* The liquidator agent checks whether a liquidation is profitable before making
the liquidation call.

- The liquidator agent checks whether a liquidation is profitable before making
the liquidation call:
Notes
-----
Profitability is checked by the following accountability:

- They check the amount of collateral that they would get by liquidation a
fraction of a loan.
- They check the price of the trade in Uniswap necessary to close the short
position in the debt asset.
- If they get a profit after closing their short position in the debt asset,
then they make the transaction.
* They check the amount of collateral that they would get by liquidating a
fraction of a loan.
* They check the price of the trade in Uniswap necessary to close the short
position in the debt asset.
* If they get a profit after closing their short position in the debt asset,
then they make the transaction.

References
----------
https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4540333
#. https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4540333
"""

import json
from functools import partial
from pathlib import Path
from typing import List

import verbs

Expand Down Expand Up @@ -85,7 +84,7 @@ def runner(
adversarial_liquidator: bool = False,
uniswap_agent_type=UniswapAgent,
show_progress: bool = True,
):
) -> List[List]:
"""
Aave simulation runner

Expand Down Expand Up @@ -143,9 +142,6 @@ def runner(
# -------------------------
# Initialize Uniswap agent
# -------------------------
uniswap_agent_type = (
partial(DummyUniswapAgent, sim_n_steps=n_steps) if init_cache else UniswapAgent
)
uniswap_agent = uniswap_agent_type(
env=env,
dt=0.01,
Expand Down Expand Up @@ -330,7 +326,7 @@ def init_cache(
n_borrow_agents: int,
mu: float = 0.1,
sigma: float = 0.6,
):
) -> verbs.types.Cache:
"""
Generate a simulation request cache

Expand All @@ -353,6 +349,11 @@ def init_cache(
GBM mu parameter, default 0.1
sigma: float, optional
GBM sigma parameter, default 0.6

Returns
-------
verbs.types.Cache
Cache generated using :py:meth:`verbs.envs.ForkEnv.export_cache`.
"""
# Fork environment from mainnet
env = verbs.envs.ForkEnv(
Expand Down
93 changes: 85 additions & 8 deletions simulations/agents/borrow_agent.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,64 @@
"""
Agent that supplies and borrows tokens from an Aave pool
"""

from typing import List, Tuple

import numpy as np
import verbs


class BorrowAgent:
"""
Agent that supplies and borrows tokens from an Aave pool
Borrower agent who supplies and borrows tokens from an Aave pool
"""

def __init__(
self,
env,
i: int,
pool_implementation_abi,
oracle_abi,
mintable_erc20_abi,
pool_implementation_abi: type,
oracle_abi: type,
mintable_erc20_abi: type,
pool_address: bytes,
oracle_address: bytes,
token_a_address: bytes,
token_b_address: bytes,
activation_rate: float,
):
"""
Initialise the Borrower agent and create the corresponding
account in the EVM.

The agent stores the ABIs of the Aave contracts and the token
contracts that they will be interacting with. ABIs are loaded
using the function :py:func:`verbs.abi.load_abi`.

Parameters
----------
env: verbs.types.Env
Simulation environment
i: int
Agent index in the simulation
pool_implementation_abi: type
abi of the Aave v3 pool contract
oracle_abi: type
abi of the Aave oracle contract for collateral and debt tokens
mintable_erc20_abi: type
abi of ERC20 contract
pool_address: bytes
Addres of Aave v3 pool contract
oracle_address: bytes
Address of Aave oracle contract for collateral and debt tokens
token_a_address: bytes
Address of collateral token (usually the risky token)
token_b_address: bytes
Address of debt token (usually the less risky token)
activation_rate: float
Probability of taking an action (either provide collateral
or borrow) at each step
"""

self.address = verbs.utils.int_to_address(i)
env.create_account(self.address, int(1e30))

Expand Down Expand Up @@ -51,9 +87,29 @@ def __init__(

self.step = 0

def update(self, rng: np.random.Generator, env):
def update(self, rng: np.random.Generator, env) -> List[verbs.types.Transaction]:
"""
Update the state of the agent
Update the state of the agent and returns
list of transactions according to their policy.

Borrower agent can either supply collateral to the Aave pool
or borrow debt assets.

Parameters
----------
rng: numpy.random.Generator
Numpy random generator, used for any random sampling
to ensure determinism of the simulation.
env: verbs.types.Env
Network/EVM that the simulation interacts with.

Returns
-------
list
List of transactions to be processed in the next block
of the simulation. This can be an empty list if the
agent is not submitting any transactions.

"""
self.step += 1

Expand All @@ -78,7 +134,7 @@ def update(self, rng: np.random.Generator, env):
env, self.address, self.oracle_address, [self.token_b_address]
)[0][0]
coef = 10 ** (self.decimals_token_b - 4)
u = rng.integers(low=7000, high=9500)
u = rng.integers(low=7000, high=9300)
available_borrow = int(
coef * available_borrow_base * u / borrow_asset_price
)
Expand All @@ -97,10 +153,31 @@ def update(self, rng: np.random.Generator, env):
else:
return []

def record(self, env):
def record(self, env) -> Tuple[int, float, float, float]:
"""
Record the state of the agent

This method is called at the end of each step for all agents.
It should return any data to be recorded over the course
of the simulation.

Parameters
----------
env: verbs.types.Env
Network/EVM that the simulation interacts with.

Returns
-------
tuple[int, float, float, float]
Tuple containing:

- Step of the simulation.
- Health factor of the borrower's position at the current step.
- Collateral value of the borrower's position in the base currency
In Aave the base currency is USD and it has 8 decimal places
- Debt asset value of the borrower's position in the base currency
"""

user_data = self.pool_implementation_abi.getUserAccountData.call(
env, self.address, self.pool_address, [self.address]
)[0]
Expand Down
Loading