From a7b3240d3c001dff42fc46376265d19f069f86c6 Mon Sep 17 00:00:00 2001 From: Charlie Date: Fri, 25 Oct 2024 18:08:16 +0100 Subject: [PATCH] feat: fuzz vault --- vega_sim/api/trading.py | 4 + vega_sim/scenario/fuzzed_markets/agents.py | 147 ++++++++++++++++++++- vega_sim/scenario/fuzzing/scenario.py | 13 ++ vega_sim/service.py | 4 + 4 files changed, 167 insertions(+), 1 deletion(-) diff --git a/vega_sim/api/trading.py b/vega_sim/api/trading.py index 1df516410..e4440397f 100644 --- a/vega_sim/api/trading.py +++ b/vega_sim/api/trading.py @@ -56,6 +56,7 @@ def submit_order( reduce_only: bool = False, post_only: bool = False, iceberg_opts: Optional[vega_protos.commands.IcebergOpts] = None, + vault_id: Optional[str] = None, ) -> Optional[str]: """ Submit orders as specified to required pre-existing market. @@ -127,6 +128,7 @@ def submit_order( reduce_only=reduce_only, post_only=post_only, iceberg_opts=iceberg_opts, + vault_id=vault_id, ) # Sign the transaction with an order submission command @@ -525,6 +527,7 @@ def order_submission( reduce_only: bool = False, post_only: bool = False, iceberg_opts: Optional[vega_protos.commands.v1.IcebergOpts] = None, + vault_id: Optional[str] = None, ) -> OrderSubmission: """Creates a Vega OrderSubmission object. @@ -585,6 +588,7 @@ def order_submission( type=order_type, reduce_only=reduce_only, post_only=post_only, + vault_id=vault_id, ) # Update OrderSubmission object with optional fields if specified diff --git a/vega_sim/scenario/fuzzed_markets/agents.py b/vega_sim/scenario/fuzzed_markets/agents.py index 90c0a3228..394e5a551 100644 --- a/vega_sim/scenario/fuzzed_markets/agents.py +++ b/vega_sim/scenario/fuzzed_markets/agents.py @@ -4,7 +4,7 @@ import string import uuid import hashlib -from datetime import datetime +from datetime import datetime, timedelta from typing import Dict, Iterable, List, NamedTuple, Optional from uuid import uuid4 @@ -2005,3 +2005,148 @@ def step(self, vega_state) -> None: transaction_type="cancel_amm", wallet_name=self.wallet_name, ) + + +class FuzzedVault(StateAgentWithWallet): + """ + + Agent creates a vault and generates n keys which it randomly assigns + ownership of the vault. + + Initially: + + - create a default redemption schedule + - create a sensible pool + - generate n keys and deposit a random amount + + Each step: + + - randomly assign ownership to a key using a random key + - randomly generate an order using the correct key + - randomly generate an order using any key + - randomly generate a redemption request using any key + - randomly generate a deposit using any key + + Note for now we are skipping fuzzing pool updates. + """ + + NAME_BASE = "FuzzedVault" + + def __init__( + self, + base_key_name: str, + market_name: str, + max_deposit_amount: float, + max_order_size: float, + 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__(base_key_name, tag, wallet_name, state_update_freq) + + self.market_name = market_name + self.max_deposit_amount = max_deposit_amount + self.max_order_size = max_order_size + self.initial_asset_mint = initial_asset_mint + + self.keys = set() + + 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, + ): + self.vega = vega + # Set a root public key to align with convention, then generate + # a set of n public keys to assign to the vault. + self._public_key = None + for i in range(10): + key_name = self.key_name + str(i) + self.vega.create_key( + name=key_name, + wallet_name=self.wallet_name, + ) + self.keys.add(key_name) + if self._public_key is None: + self._public_key = vega.wallet.public_key( + name=key_name, wallet_name=self.wallet_name + ) + # Randomly assign the first owner and set the key name + self.owner = self.random_state.choice(list(self.keys)) + self.key_name = self.owner + 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], + ] + now = self.vega.get_blockchain_time(in_seconds=True) + redemption_dates = [ + vega_protos.vega.RedemptionDate( + redemption_date=int(now + i * 300), + redemption_type=self.random_state.choice( + [ + vega_protos.vega.RedemptionType.REDEMPTION_TYPE_NORMAL, + vega_protos.vega.RedemptionType.REDEMPTION_TYPE_FREE_CASH_ONLY, + ] + ), + max_fraction=str(self.random_state.uniform(1e-6, 1)), + ) + for i in range(20) + ] + for key in list(self.keys): + self.vega.mint( + key_name=key, + wallet_name=self.wallet_name, + asset=self.asset_id, + amount=self.initial_asset_mint, + ) + self.vega.wait_for_total_catchup() + self.vault_id = self.vega.create_vault( + key_name=self.owner, + wallet_name=self.wallet_name, + asset=self.asset_id, + fee_period=timedelta(seconds=120), + management_fee_factor=0.001, + performance_fee_factor=0.001, + redemption_dates=redemption_dates, + cut_off_period_length=timedelta(seconds=120), + ) + + def step(self, vega_state) -> None: + side = self.random_state.choice(["SIDE_BUY", "SIDE_SELL"]) + volume = self.random_state.uniform(0, self.max_order_size) + self.vega.submit_market_order( + trading_key=self.owner, + market_id=self.market_id, + side=side, + volume=volume, + wait=False, + fill_or_kill=False, + trading_wallet=self.wallet_name, + vault_id=self.vault_id, + ) + key = self.random_state.choice(list(self.keys)) + deposit_amount = self.random_state.uniform(0, self.max_deposit_amount) + self.vega.deposit_to_vault( + key_name=key, + wallet_name=self.wallet_name, + vault_id=self.vault_id, + amount=deposit_amount, + asset_id=self.asset_id, + ) + key = self.random_state.choice(list(self.keys)) + redemption_amount = self.random_state.uniform(0, self.max_deposit_amount) + self.vega.withdraw_from_vault( + key_name=key, + wallet_name=self.wallet_name, + vault_id=self.vault_id, + amount=redemption_amount, + asset_id=self.asset_id, + ) diff --git a/vega_sim/scenario/fuzzing/scenario.py b/vega_sim/scenario/fuzzing/scenario.py index a56753db7..cf52ca0b6 100644 --- a/vega_sim/scenario/fuzzing/scenario.py +++ b/vega_sim/scenario/fuzzing/scenario.py @@ -24,6 +24,7 @@ FuzzyReferralProgramManager, FuzzyVolumeRebateProgramManager, FuzzyVolumeDiscountProgramManager, + FuzzedVault, ) @@ -137,7 +138,19 @@ def configure_agents( for benchmark_config in self.benchmark_configs: market_name = benchmark_config.market_config.instrument.name market_code = benchmark_config.market_config.instrument.code + if self.__fuzz_traders: + extra_agents.append( + FuzzedVault( + base_key_name=f"FuzzedVault_{market_code}", + wallet_name="FuzzedVault", + market_name=market_name, + initial_asset_mint=1e6, + max_deposit_amount=1e3, + max_order_size=100 / benchmark_config.price_process[0], + tag=f"{market_code}", + ) + ) extra_agents.extend( [ ReferralAgentWrapper( diff --git a/vega_sim/service.py b/vega_sim/service.py index 27c134679..ac682b47c 100644 --- a/vega_sim/service.py +++ b/vega_sim/service.py @@ -1110,6 +1110,7 @@ def submit_market_order( trading_wallet: Optional[str] = None, reduce_only: bool = False, post_only: bool = False, + vault_id: Optional[str] = None, ) -> str: """Places a simple Market order, either as Fill-Or-Kill or Immediate-Or-Cancel. @@ -1152,6 +1153,7 @@ def submit_market_order( trading_key=trading_key, reduce_only=reduce_only, post_only=post_only, + vault_id=vault_id, ) def submit_order( @@ -1173,6 +1175,7 @@ def submit_order( peak_size: Optional[float] = None, minimum_visible_size: Optional[float] = None, round_to_tick: bool = True, + vault_id: Optional[str] = None, ) -> Optional[str]: """ Submit orders as specified to required pre-existing market. @@ -1308,6 +1311,7 @@ def submit_order( if (peak_size is not None and minimum_visible_size is not None) else None ), + vault_id=vault_id, ) def get_blockchain_time(self, in_seconds: bool = False) -> int: