From 23e052709ad1e28ad87029e00f0ea321956dfa87 Mon Sep 17 00:00:00 2001 From: Charlie <99198652+cdummett@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:30:53 +0100 Subject: [PATCH] feat: add fuzzing of the rebate program to overnight runs (#692) * chore: update tag * feat: add hvmr methods and fuzzing --- .env | 2 +- Jenkinsfile | 2 +- vega_sim/api/governance.py | 51 +++++++ vega_sim/scenario/fuzzed_markets/agents.py | 155 ++++++++++++++++++--- vega_sim/scenario/fuzzing/scenario.py | 10 ++ vega_sim/service.py | 56 ++++++++ vega_sim/vegahome/genesis.json | 8 ++ 7 files changed, 265 insertions(+), 19 deletions(-) diff --git a/.env b/.env index b43506884..710a32325 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -VEGA_SIM_VEGA_TAG=9b5afc95625be367f459feb9503a0d25749fc32a +VEGA_SIM_VEGA_TAG=0da1f2aba7fa15e8919727484ab728a46d6cf259 VEGA_SIM_CONSOLE_TAG=develop VEGA_DEFAULT_KEY_NAME='Key 1' VEGA_SIM_NETWORKS_INTERNAL_TAG=main diff --git a/Jenkinsfile b/Jenkinsfile index 979133d11..d2965513e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,7 +14,7 @@ pipeline { disableConcurrentBuilds(abortPrevious: true) } parameters { - string( name: 'VEGA_VERSION', defaultValue: '9b5afc95625be367f459feb9503a0d25749fc32a', + string( name: 'VEGA_VERSION', defaultValue: '0da1f2aba7fa15e8919727484ab728a46d6cf259', description: 'Git branch, tag or hash of the vegaprotocol/vega repository') string( name: 'VEGACAPSULE_VERSION', defaultValue: 'main', description: 'Git branch, tag or hash of the vegaprotocol/vegacapsule repository') diff --git a/vega_sim/api/governance.py b/vega_sim/api/governance.py index 3601a5ea4..1f11c56c9 100644 --- a/vega_sim/api/governance.py +++ b/vega_sim/api/governance.py @@ -1161,6 +1161,57 @@ def update_volume_discount_program( ).proposal.id +def update_volume_rebate_program( + key_name: str, + wallet: Wallet, + data_client: vac.VegaTradingDataClientV2, + benefit_tiers: Optional[list[dict]] = None, + end_of_program_timestamp: Optional[int] = None, + window_length: Optional[int] = None, + wallet_name: Optional[str] = None, + closing_time: Optional[int] = None, + enactment_time: Optional[int] = None, + time_forward_fn: Optional[Callable[[], None]] = None, +): + volume_rebate_program = vega_protos.governance.VolumeRebateProgramChanges( + end_of_program_timestamp=end_of_program_timestamp, + window_length=window_length, + ) + if benefit_tiers is not None: + for benefit_tier in benefit_tiers: + volume_rebate_program.benefit_tiers.extend( + [ + vega_protos.vega.VolumeRebateBenefitTier( + minimum_party_maker_volume_fraction=str( + benefit_tier["minimum_party_maker_volume_fraction"] + ), + additional_maker_rebate=str( + benefit_tier["additional_maker_rebate"] + ), + ) + ] + ) + + proposal = _build_generic_proposal( + pub_key=wallet.public_key(wallet_name=wallet_name, name=key_name), + data_client=data_client, + closing_time=closing_time, + enactment_time=enactment_time, + ) + proposal.terms.update_volume_rebate_program.CopyFrom( + vega_protos.governance.UpdateVolumeRebateProgram(changes=volume_rebate_program) + ) + print(proposal) + return _make_and_wait_for_proposal( + wallet_name=wallet_name, + wallet=wallet, + proposal=proposal, + data_client=data_client, + time_forward_fn=time_forward_fn, + key_name=key_name, + ).proposal.id + + def new_transfer( asset_decimals: Dict[str, int], data_client: vac.VegaTradingDataClientV2, diff --git a/vega_sim/scenario/fuzzed_markets/agents.py b/vega_sim/scenario/fuzzed_markets/agents.py index 5a8184dd8..d905e1758 100644 --- a/vega_sim/scenario/fuzzed_markets/agents.py +++ b/vega_sim/scenario/fuzzed_markets/agents.py @@ -577,7 +577,7 @@ def step(self, vega_state): or midprice == 0 ): return - if self.random_state.rand() > self.step_bias: + if self.random_state.rand() < self.step_bias: return # If we have spare money in our general we need to increase our position @@ -670,7 +670,7 @@ def step(self, vega_state): self.commitment_amount = 0 return - if self.random_state.rand() > self.step_bias: + if self.random_state.rand() < self.step_bias: return if self.commitment_amount < self.commitment_factor * (total_balance): @@ -1372,6 +1372,8 @@ class FuzzyReferralProgramManager(StateAgentWithWallet): controlled frequency. """ + NAME_BASE = "fuzzy_referral_program_manager" + def __init__( self, key_name: str, @@ -1522,6 +1524,8 @@ class FuzzyVolumeDiscountProgramManager(StateAgentWithWallet): controlled frequency. """ + NAME_BASE = "fuzzy_volume_discount_program_manager" + def __init__( self, key_name: str, @@ -1563,7 +1567,7 @@ def initialise(self, vega: VegaServiceNull, create_key: bool = True, mint_key=Tr self._sensible_proposal() def step(self, vega_state): - if self.random_state.rand() > self.step_bias: + if self.random_state.rand() < self.step_bias: for i in range(self.attempts_per_step): try: self._fuzzed_proposal() @@ -1594,21 +1598,21 @@ def _sensible_proposal(self): benefit_tiers=[ { "minimum_running_notional_taker_volume": 1000, - "infrastructure_discount_fee": 0.01, - "liquidity_discount_fee": 0.01, - "maker_discount_fee": 0.01, + "infrastructure_discount_factor": 0.01, + "liquidity_discount_factor": 0.01, + "maker_discount_factor": 0.01, }, { "minimum_running_notional_taker_volume": 2000, - "infrastructure_discount_fee": 0.01, - "liquidity_discount_fee": 0.01, - "maker_discount_fee": 0.01, + "infrastructure_discount_factor": 0.01, + "liquidity_discount_factor": 0.01, + "maker_discount_factor": 0.01, }, { "minimum_running_notional_taker_volume": 3000, - "infrastructure_discount_fee": 0.01, - "liquidity_discount_fee": 0.01, - "maker_discount_fee": 0.01, + "infrastructure_discount_factor": 0.01, + "liquidity_discount_factor": 0.01, + "maker_discount_factor": 0.01, }, ], window_length=1, @@ -1624,9 +1628,126 @@ def _fuzzed_proposal(self): "minimum_running_notional_taker_volume": self.random_state.randint( 1, 1e6 ), - "infrastructure_discount_fee": self.random_state.rand(), - "liquidity_discount_fee": self.random_state.rand(), - "maker_discount_fee": self.random_state.rand(), + "infrastructure_discount_factor": self.random_state.rand(), + "liquidity_discount_factor": self.random_state.rand(), + "maker_discount_factor": self.random_state.rand(), + } + for _ in range(self.random_state.randint(1, 5)) + ], + window_length=self.random_state.randint(1, 100), + end_of_program_timestamp=datetime.fromtimestamp( + self.vega.get_blockchain_time(in_seconds=True) + + self.random_state.normal( + loc=600 * self.vega.seconds_per_block, + scale=300 * self.vega.seconds_per_block, + ) + ), + ) + + +class FuzzyVolumeRebateProgramManager(StateAgentWithWallet): + """Agent proposes sensible and fuzzed update volume rebate program + proposals at a controlled frequency. + """ + + NAME_BASE = "fuzzy_volume_rebate_program_manager" + + def __init__( + self, + key_name: str, + step_bias=0.5, + attempts_per_step=100, + stake_key: bool = False, + wallet_name: Optional[str] = None, + random_state: Optional[RandomState] = None, + tag: Optional[str] = None, + ): + self.key_name = key_name + self.wallet_name = wallet_name + self.tag = tag + self.stake_key = stake_key + + self.step_bias = step_bias + self.attempts_per_step = attempts_per_step + + self.random_state = random_state if random_state is not None else RandomState() + + def initialise(self, vega: VegaServiceNull, create_key: bool = True, mint_key=True): + super().initialise(vega, create_key) + self.vega.wait_for_total_catchup() + if mint_key: + self.vega.mint( + wallet_name=self.wallet_name, + asset=self.vega.find_asset_id(symbol="VOTE", enabled=True), + amount=1e4, + key_name=self.key_name, + ) + self.vega.wait_for_total_catchup() + if self.stake_key: + self.vega.stake( + amount=1, + key_name=self.key_name, + wallet_name=self.wallet_name, + ) + self.vega.wait_for_total_catchup() + self._sensible_proposal() + + def step(self, vega_state): + if self.random_state.rand() < self.step_bias: + for i in range(self.attempts_per_step): + try: + self._fuzzed_proposal() + return + except ( + HTTPError, + ProposalNotAcceptedError, + builders.exceptions.VegaProtoValueError, + ): + continue + logging.info( + "All fuzzed UpdateVolumeRebate proposals failed, submitting sensible" + " proposal." + ) + try: + # Updating program requires method to get the current blockchain time. Ensure + # datanode is synced before requesting the current blockchain time. + self.vega.wait_for_datanode_sync() + self._sensible_proposal() + except ProposalNotAcceptedError: + logging.warning("Sensible UpdateVolumeRebate proposal failed.") + + def _sensible_proposal(self): + self.vega.update_volume_rebate_program( + forward_time_to_enactment=False, + proposal_key=self.key_name, + wallet_name=self.wallet_name, + benefit_tiers=[ + { + "minimum_party_maker_volume_fraction": str(0.002), + "additional_maker_rebate": f"{0.0002/10:.9f}", + }, + { + "minimum_party_maker_volume_fraction": str(0.020), + "additional_maker_rebate": f"{0.0002/5:.9f}", + }, + { + "minimum_party_maker_volume_fraction": str(0.200), + "additional_maker_rebate": f"{0.0002/2:.9f}", + }, + ], + window_length=5, + ) + + def _fuzzed_proposal(self): + return + self.vega.update_volume_rebate_program( + forward_time_to_enactment=False, + proposal_key=self.key_name, + wallet_name=self.wallet_name, + benefit_tiers=[ + { + "minimum_party_maker_volume_fraction": f"{self.random_state.rand():.9f}", + "additional_maker_rebate": f"{self.random_state.lognormal(-10, 0.5):.9f}", } for _ in range(self.random_state.randint(1, 5)) ], @@ -1695,7 +1816,7 @@ def initialise( ) def step(self, vega_state): - if self.random_state.rand() > self.step_bias: + if self.random_state.rand() < self.step_bias: return for _ in range(self.attempts_per_step): try: @@ -1782,7 +1903,7 @@ def initialise( ) def step(self, vega_state): - if self.random_state.rand() > self.step_bias: + if self.random_state.rand() < self.step_bias: return for _ in range(self.attempts_per_step): try: diff --git a/vega_sim/scenario/fuzzing/scenario.py b/vega_sim/scenario/fuzzing/scenario.py index 985fcd434..63ee34f47 100644 --- a/vega_sim/scenario/fuzzing/scenario.py +++ b/vega_sim/scenario/fuzzing/scenario.py @@ -21,6 +21,7 @@ FuzzyLiquidityProvider, FuzzedAutomatedMarketMaker, FuzzyReferralProgramManager, + FuzzyVolumeRebateProgramManager, FuzzyVolumeDiscountProgramManager, ) @@ -69,6 +70,15 @@ def configure_agents( ) ) # Add a fuzzed referral program + extra_agents.append( + FuzzyVolumeRebateProgramManager( + wallet_name="FuzzyReferralProgramManager", + key_name=f"FuzzyReferralProgramManager", + step_bias=0.01, + attempts_per_step=10, + ) + ) + # Add a fuzzed referral program extra_agents.append( FuzzyReferralProgramManager( wallet_name="FuzzyReferralProgramManager", diff --git a/vega_sim/service.py b/vega_sim/service.py index 3c765bdd5..183c25176 100644 --- a/vega_sim/service.py +++ b/vega_sim/service.py @@ -3682,6 +3682,62 @@ def update_volume_discount_program( self.wait_for_thread_catchup() return proposal_id + def update_volume_rebate_program( + self, + proposal_key: str, + benefit_tiers: Optional[list[dict]] = None, + end_of_program_timestamp: Optional[int] = None, + window_length: Optional[int] = None, + wallet_name: Optional[str] = None, + vote_closing_time: Optional[int] = None, + vote_enactment_time: Optional[int] = None, + approve_proposal: bool = True, + forward_time_to_enactment: bool = True, + ): + blockchain_time_seconds = self.get_blockchain_time(in_seconds=True) + closing_time = ( + blockchain_time_seconds + self.seconds_per_block * 40 + if vote_closing_time is None + else int(vote_closing_time.timestamp()) + ) + enactment_time = ( + blockchain_time_seconds + self.seconds_per_block * 50 + if vote_enactment_time is None + else int(vote_enactment_time.timestamp()) + ) + end_of_program_timestamp = ( + enactment_time + self.seconds_per_block * 60 * 60 * 24 * 7 * 52 + if end_of_program_timestamp is None + else int(end_of_program_timestamp.timestamp()) + ) + proposal_id = gov.update_volume_rebate_program( + key_name=proposal_key, + wallet=self.wallet, + data_client=self.trading_data_client_v2, + benefit_tiers=benefit_tiers, + end_of_program_timestamp=end_of_program_timestamp, + window_length=window_length, + wallet_name=wallet_name, + closing_time=closing_time, + enactment_time=enactment_time, + time_forward_fn=lambda: self.wait_fn(2), + ) + if approve_proposal: + gov.approve_proposal( + proposal_id=proposal_id, + wallet=self.wallet, + wallet_name=wallet_name, + key_name=proposal_key, + ) + + if forward_time_to_enactment: + time_to_enactment = enactment_time - self.get_blockchain_time( + in_seconds=True + ) + self.wait_fn(int(time_to_enactment / self.seconds_per_block) + 1) + self.wait_for_thread_catchup() + return proposal_id + def get_current_volume_discount_program( self, ) -> data.VolumeDiscountProgram: diff --git a/vega_sim/vegahome/genesis.json b/vega_sim/vegahome/genesis.json index 4e88f040b..02f5f18d3 100644 --- a/vega_sim/vegahome/genesis.json +++ b/vega_sim/vegahome/genesis.json @@ -130,6 +130,14 @@ "governance.proposal.VolumeDiscountProgram.minVoterBalance": "1", "governance.proposal.VolumeDiscountProgram.requiredMajority": "0.5", "governance.proposal.VolumeDiscountProgram.requiredParticipation": "0.00001", + "governance.proposal.VolumeRebateProgram.maxClose": "8760h0m0s", + "governance.proposal.VolumeRebateProgram.maxEnact": "8760h0m0s", + "governance.proposal.VolumeRebateProgram.minClose": "1s", + "governance.proposal.VolumeRebateProgram.minEnact": "1s", + "governance.proposal.VolumeRebateProgram.minProposerBalance": "1", + "governance.proposal.VolumeRebateProgram.minVoterBalance": "1", + "governance.proposal.VolumeRebateProgram.requiredMajority": "0.5", + "governance.proposal.VolumeRebateProgram.requiredParticipation": "0.00001", "governance.proposal.transfer.maxClose": "8760h0m0s", "governance.proposal.transfer.maxEnact": "8760h0m0s", "governance.proposal.transfer.minClose": "1s",