From bf3f3b13115c3402ed8b942f56a23a72a0705d38 Mon Sep 17 00:00:00 2001 From: Charlie Date: Thu, 16 May 2024 09:50:37 +0100 Subject: [PATCH] feat: add fuzzed amm to fuzzing scenario --- vega_sim/scenario/fuzzed_markets/agents.py | 87 +++++++++ vega_sim/scenario/fuzzed_markets/fuzzers.py | 189 ++++++++++++++++++++ vega_sim/scenario/fuzzing/scenario.py | 22 +++ 3 files changed, 298 insertions(+) diff --git a/vega_sim/scenario/fuzzed_markets/agents.py b/vega_sim/scenario/fuzzed_markets/agents.py index 9015a8627..7d96bad78 100644 --- a/vega_sim/scenario/fuzzed_markets/agents.py +++ b/vega_sim/scenario/fuzzed_markets/agents.py @@ -1613,3 +1613,90 @@ def finalise(self): f" {self.accepted_proposals}/{self.proposals} valid governance transfer" " proposals." ) + + +class FuzzedAutomatedMarketMaker(StateAgentWithWallet): + + NAME_BASE = "FuzzedAutomatedMarketMaker" + + def __init__( + self, + key_name: str, + market_name: str, + submit_probability: float = 0.50, + amend_probability: float = 0.10, + cancel_probability: float = 0.05, + initial_asset_mint: float = 1e9, + random_state: Optional[RandomState] = None, + tag: Optional[str] = None, + wallet_name: Optional[str] = None, + state_update_freq: Optional[int] = None, + ): + super().__init__(key_name, tag, wallet_name, state_update_freq) + + self.market_name = market_name + self.submit_probability = submit_probability + self.amend_probability = amend_probability + self.cancel_probability = cancel_probability + self.initial_asset_mint = initial_asset_mint + + self.random_state = random_state if random_state is not None else RandomState() + + def initialise( + self, + vega: VegaServiceNull, + create_key: bool = True, + mint_key: bool = False, + ): + super().initialise(vega, create_key, mint_key) + + self.market_id = self.vega.find_market_id(name=self.market_name) + self.asset_id = self.vega.market_to_asset[self.market_id] + + asset_ids = [ + self.vega.market_to_settlement_asset[self.market_id], + self.vega.market_to_base_asset[self.market_id], + self.vega.market_to_quote_asset[self.market_id], + ] + for asset_id in asset_ids: + if asset_id is not None and mint_key: + # Top up asset + self.vega.mint( + wallet_name=self.wallet_name, + asset=asset_id, + amount=self.initial_asset_mint, + key_name=self.key_name, + ) + self.vega.wait_for_total_catchup() + + def step(self, vega_state) -> None: + if self.random_state.rand() < self.submit_probability: + transaction = fuzzers.fuzz_submit_amm( + vega=self.vega, rs=self.random_state, bias=0.8 + ) + self.vega.submit_transaction( + key_name=self.key_name, + transaction=transaction, + transaction_type="submit_amm", + wallet_name=self.wallet_name, + ) + if self.random_state.rand() < self.amend_probability: + transaction = fuzzers.fuzz_amend_amm( + vega=self.vega, rs=self.random_state, bias=0.8 + ) + self.vega.submit_transaction( + key_name=self.key_name, + transaction=transaction, + transaction_type="amend_amm", + wallet_name=self.wallet_name, + ) + if self.random_state.rand() < self.cancel_probability: + transaction = fuzzers.fuzz_cancel_amm( + vega=self.vega, rs=self.random_state, bias=0.8 + ) + self.vega.submit_transaction( + key_name=self.key_name, + transaction=transaction, + transaction_type="cancel_amm", + wallet_name=self.wallet_name, + ) diff --git a/vega_sim/scenario/fuzzed_markets/fuzzers.py b/vega_sim/scenario/fuzzed_markets/fuzzers.py index 33fb2897a..094c471f9 100644 --- a/vega_sim/scenario/fuzzed_markets/fuzzers.py +++ b/vega_sim/scenario/fuzzed_markets/fuzzers.py @@ -5,6 +5,8 @@ import vega_sim.builders as build import vega_sim.proto.vega as vega_protos +from typing import Union + def valid(rs, bias) -> bool: if rs.rand() < bias: @@ -766,3 +768,190 @@ def _pick_size_delta(): size_delta=size_delta, price=price, ) + + +def fuzz_submit_amm( + vega: VegaService, rs: RandomState, bias: float +) -> vega_protos.commands.v1.commands.SubmitAMM: + + def _pick_market_id(): + if len(market_ids) == 0: + return None + return rs.choice(market_ids) + + def _pick_commitment_amount(): + return rs.rand() * 10000 + + def _pick_slippage_tolerance(): + return rs.rand() + + def _pick_proposed_fee(): + return rs.rand() + + def _pick_base(): + if valid(rs, bias): + factor = rs.uniform(1, 2) + else: + factor = rs.uniform(0, 2) + return market_data.mid_price * factor + + def _pick_lower_bound(): + if valid(rs, bias): + factor = rs.uniform(0, 1) + else: + factor = rs.uniform(0, 2) + return rs.choice([None, base * factor]) + + def _pick_upper_bound(): + if valid(rs, bias): + factor = rs.uniform(1, 2) + else: + factor = rs.uniform(0, 2) + return rs.choice([None, base * factor]) + + def _pick_leverage_at_lower_bound(): + if valid(rs, bias): + if lower_bound is None: + return None + return rs.rand() * 100 + + def _pick_leverage_at_upper_bound(): + if valid(rs, bias): + if upper_bound is None: + return None + return rs.rand() * 100 + + market_ids = [key for key, _ in vega.market_to_asset.items()] + market_id = _pick_market_id() + market_data = vega.market_data_from_feed(market_id) + + proposed_fee = _pick_proposed_fee() + commitment_amount = _pick_commitment_amount() + slippage_tolerance = _pick_slippage_tolerance() + + base = _pick_base() + lower_bound = _pick_lower_bound() + upper_bound = _pick_upper_bound() + leverage_at_lower_bound = _pick_leverage_at_lower_bound() + leverage_at_upper_bound = _pick_leverage_at_upper_bound() + + return build.commands.commands.submit_amm( + asset_decimals=vega.asset_decimals, + market_asset_map=vega.market_to_asset, + market_price_decimals=vega.market_price_decimals, + market_id=market_id, + commitment_amount=commitment_amount, + slippage_tolerance=slippage_tolerance, + proposed_fee=proposed_fee, + base=base, + lower_bound=lower_bound, + upper_bound=upper_bound, + leverage_at_lower_bound=leverage_at_lower_bound, + leverage_at_upper_bound=leverage_at_upper_bound, + ) + + +def fuzz_amend_amm( + vega: VegaService, rs: RandomState, bias: float +) -> vega_protos.commands.v1.commands.AmendAMM: + + def _pick_market_id(): + if len(market_ids) == 0: + return None + return rs.choice(market_ids) + + def _pick_commitment_amount(): + return rs.rand() * 10000 + + def _pick_slippage_tolerance(): + return rs.rand() + + def _pick_proposed_fee(): + return rs.rand() + + def _pick_base(): + if valid(rs, bias): + factor = rs.uniform(1, 2) + else: + factor = rs.uniform(0, 2) + return market_data.mid_price * factor + + def _pick_lower_bound(): + if valid(rs, bias): + factor = rs.uniform(0, 1) + else: + factor = rs.uniform(0, 2) + return rs.choice([None, base * factor]) + + def _pick_upper_bound(): + if valid(rs, bias): + factor = rs.uniform(1, 2) + else: + factor = rs.uniform(0, 2) + return rs.choice([None, base * factor]) + + def _pick_leverage_at_lower_bound(): + if valid(rs, bias): + if lower_bound is None: + return None + return rs.rand() * 100 + + def _pick_leverage_at_upper_bound(): + if valid(rs, bias): + if upper_bound is None: + return None + return rs.rand() * 100 + + market_ids = [key for key, _ in vega.market_to_asset.items()] + market_id = _pick_market_id() + market_data = vega.market_data_from_feed(market_id) + + proposed_fee = _pick_proposed_fee() + commitment_amount = _pick_commitment_amount() + slippage_tolerance = _pick_slippage_tolerance() + + base = _pick_base() + lower_bound = _pick_lower_bound() + upper_bound = _pick_upper_bound() + leverage_at_lower_bound = _pick_leverage_at_lower_bound() + leverage_at_upper_bound = _pick_leverage_at_upper_bound() + + return build.commands.commands.amend_amm( + asset_decimals=vega.asset_decimals, + market_asset_map=vega.market_to_asset, + market_price_decimals=vega.market_price_decimals, + market_id=market_id, + commitment_amount=commitment_amount, + slippage_tolerance=slippage_tolerance, + proposed_fee=proposed_fee, + base=base, + lower_bound=lower_bound, + upper_bound=upper_bound, + leverage_at_lower_bound=leverage_at_lower_bound, + leverage_at_upper_bound=leverage_at_upper_bound, + ) + + +def fuzz_cancel_amm( + vega: VegaService, rs: RandomState, bias: float +) -> vega_protos.commands.v1.commands.CancelAMM: + + def _pick_market_id(): + if len(market_ids) == 0: + return None + return rs.choice(market_ids) + + def _pick_method(): + values = vega_protos.vega.DispatchMetric.values() + if valid(rs, bias): + values.pop(0) + return rs.choice(values) + + market_ids = [key for key, _ in vega.market_to_asset.items()] + market_id = _pick_market_id() + method = _pick_method() + + return build.commands.commands.cancel_amm( + market_id=market_id, + method=method, + ) diff --git a/vega_sim/scenario/fuzzing/scenario.py b/vega_sim/scenario/fuzzing/scenario.py index 26984c0b1..a3f13d7e5 100644 --- a/vega_sim/scenario/fuzzing/scenario.py +++ b/vega_sim/scenario/fuzzing/scenario.py @@ -18,6 +18,7 @@ from vega_sim.scenario.fuzzed_markets.agents import ( FuzzingAgent, FuzzyLiquidityProvider, + FuzzedAutomatedMarketMaker, FuzzyReferralProgramManager, FuzzyVolumeDiscountProgramManager, ) @@ -138,6 +139,27 @@ def configure_agents( for i_referrer, i_agent in itertools.product(range(2), range(2)) ] ) + extra_agents.extend( + [ + ReferralAgentWrapper( + agent=FuzzedAutomatedMarketMaker( + wallet_name="FuzzedAutomatedMarketMaker", + key_name=( + f"FuzzedAutomatedMarketMaker{str(i_referrer).zfill(3)}_{str(i_agent).zfill(3)}" + ), + market_name=market_name, + submit_probability=0.5, + amend_probability=0.5, + cancel_probability=0.1, + initial_asset_mint=1e6, + tag=f"{market_code}_{str(i_referrer).zfill(3)}_{str(i_agent).zfill(3)}", + ), + referrer_wallet_name="ReferralAgentWrapper", + referrer_key_name=f"ReferralAgentWrapper_{str(i_referrer).zfill(3)}", + ) + for i_referrer, i_agent in itertools.product(range(2), range(2)) + ] + ) # Create a reward funder for each asset and each reward type asset_names = set()