Source code for curvesim.sim

"""
A simulation runs trades against Curve pools, using a strategy that may
utilize different types of informed or noise trades.

The :mod:`simulation pipeline framework <curvesim.pipelines>` allows the
user to build custom strategies for simulation.

Most users will want to use the `autosim` function, which supports
"optimal" arbitrages via the
:func:`volume-limited arbitrage pipeline <curvesim.pipelines.vol_limited_arb.pipeline>`.
The primary use-case is to determine optimal amplitude (A) and fee
parameters given historical price and volume feeds.
"""
from curvesim.logging import get_logger
from curvesim.pipelines.vol_limited_arb import pipeline as volume_limited_arbitrage
from curvesim.pool_data import get_metadata
from curvesim.utils import get_pairs

logger = get_logger(__name__)


[docs]def autosim( pool=None, chain="mainnet", pool_metadata=None, env="prod", **kwargs, ): """ The autosim() function simulates existing Curve pools with a range of parameters (e.g., the amplitude parameter, A, and/or the exchange fee). The function fetches pool properties (e.g., current pool size) and 2 months of price/volume data and runs multiple simulations in parallel. Curve pools from any chain supported by the Convex Community Subgraphs can be simulated directly by inputting the pool's address. Parameters ---------- pool: str, optional This 0x-prefixed string identifies the pool by address. .. note:: Either `pool` or `pool_metadata` must be provided. chain: str, default='mainnet' Identifier for blockchain or layer2. Supported values are: "mainnet", "arbitrum", "optimism", "fantom", "avalanche" "matic", "xdai" pool_metadata: PoolMetaDataInterface, optional Pool state and metadata necessary to instantiate a pool object. .. note:: Either `pool` or `pool_metadata` must be provided. A: int or iterable of int, optional Amplification coefficient. This controls the curvature of the stableswap bonding curve. Increased values makes the curve flatter in a greater neighborhood of equal balances. For basepool, use **A_base**. D: int, optional Total pool liquidity given in 18 decimal precision. Defaults to on-chain data. For basepool, use **D_base**. tokens: int, optional Total LP token supply. Defaults to on-chain data. For basepool, use **tokens_base**. fee: int or iterable of int, optional Fees taken for both liquidity providers and the DAO. Units are in fixed-point so that 10**10 is 100%, e.g. 4 * 10**6 is 4 bps and 2 * 10**8 is 2%. For basepool, use **fee_base**. fee_mul : int Fee multiplier for dynamic fee pools. For basepool, use **fee_mul_base**. admin_fee : int, default=0 * 10**9 Fees taken for the DAO. For factory pools, it is half of the total fees, as was typical for previous non-factory pools. Units are fixed-point percentage of `fee`, e.g. 5 * 10**9 is 50% of the total fees. test: bool, default=False Overrides variable_params to use four test values: .. code-block:: {"A": [100, 1000], "fee": [3000000, 4000000]} days: int, default=60 Number of days to fetch data for. src: str, default='coingecko' Valid values for data source are 'coingecko' or 'local' data_dir: str, default='data' Relative path to saved data folder. vol_mult : dict, float, or int, default computed from data Value(s) multiplied by market volume to specify volume limits (overrides vol_mode). dict should map from trade-pair tuples to values, e.g.: .. code-block:: {('DAI', 'USDC'): 0.1, ('DAI', 'USDT'): 0.1, ('USDC', 'USDT'): 0.1} vol_mode : int, default=1 Modes for limiting trade volume. 1: limits trade volumes proportionally to market volume for each pair 2: limits trade volumes equally across pairs 3: mode 2 for trades with meta-pool asset, mode 1 for basepool-only trades ncpu : int, default=os.cpu_count() Number of cores to use. env: str, default='prod' Environment for the Curve subgraph, which pulls pool and volume snapshots. Returns ------- dict Dictionary of results, each value being a pandas.Series. """ assert any([pool, pool_metadata]), "Must input 'pool' or 'pool_metadata'" pool_metadata = pool_metadata or get_metadata(pool, chain, env) p_var, p_fixed, kwargs = _parse_arguments(pool_metadata, **kwargs) results = volume_limited_arbitrage( pool_metadata, variable_params=p_var, fixed_params=p_fixed, **kwargs, ) return results
def _parse_arguments(pool_metadata, **kwargs): pool_args = [ "A", "D", "balances", "fee", "fee_mul", "tokens", "admin_fee", "gamma", "fee_gamma", "mid_fee", "out_fee", ] pool_args += [arg + "_base" for arg in pool_args[:-1]] variable_params = {} fixed_params = {} rest_of_params = {} for key, val in kwargs.items(): if key in pool_args: if isinstance(val, int): fixed_params[key] = val elif all(isinstance(v, int) for v in val): variable_params[key] = val else: raise TypeError(f"Argument {key} must be an int or iterable of ints") elif key == "vol_mult" and isinstance(val, (int, float)): coin_pairs = get_pairs(pool_metadata.coin_names) rest_of_params[key] = dict.fromkeys(coin_pairs, val) else: rest_of_params[key] = val return variable_params, fixed_params, rest_of_params