diff --git a/packages/packages.json b/packages/packages.json index f29e6b605..225b63449 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -16,14 +16,14 @@ "contract/valory/staking_token/0.1.0": "bafybeiep4r6qyilbfgzdvx6t7zvpgaioxqktmxm7puwtnbpb2ftlib43gy", "contract/valory/relayer/0.1.0": "bafybeicawmds6czx7db2lcktvexwrp245jpekgulndtos5s5zdid3ilvq4", "skill/valory/market_manager_abci/0.1.0": "bafybeicztk62pslofv6ui3aw3giw2tnvlfwfmatqbyvvzv4ampneu6isqa", - "skill/valory/decision_maker_abci/0.1.0": "bafybeiffnd7vlryxtfenwcbbvx7qzszkko4oop2n2e6jg2ahdm3zholxsq", - "skill/valory/trader_abci/0.1.0": "bafybeigygq76epkhl67uuvrjlrall65r4ilcwna2jokgjunrzg45gf2ihy", - "skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeie4rfme364fp2m3oo447622mc75tfk4hqftiebau2tpycftd5iffe", + "skill/valory/decision_maker_abci/0.1.0": "bafybeicx3vjswhjzgfa2ddl7zxqzncuqbvej3m2unseje5lsajdhio7mji", + "skill/valory/trader_abci/0.1.0": "bafybeiapf3qng7wczem3t7s5cysdlhzc3xign4sqkhyopro3p7x33hdugu", + "skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeic7wcls7yvvnpqrl7vfrab3dt37vyoytu2hef7emmc5556puqjpri", "skill/valory/staking_abci/0.1.0": "bafybeictd5pxhscuhqntvctb7l5lfjausxt2m22rg5mkaiuj4cwwcxpvne", "skill/valory/check_stop_trading_abci/0.1.0": "bafybeifmi64g4ki6zwbcncb35ovhd4sllw4xrszrkturpeqdhgf5bkiini", - "agent/valory/trader/0.1.0": "bafybeibhv5pummw2ngrag6sk6a7phnffp2dfsswhvtxayzizqthea52kru", - "service/valory/trader/0.1.0": "bafybeiey356lnc35rxky3gvikcpcwerdjycyb6nmrw4qlpucar3ykd2jmm", - "service/valory/trader_pearl/0.1.0": "bafybeiezfz7h5g26qimscimnawyeeo3qwgm6smszc7jgwrxest7bgqoq5q" + "agent/valory/trader/0.1.0": "bafybeieoklf7wjrnupyv4egny5hfn6mbxtrjwwtrqyznjoxodiy72cj3na", + "service/valory/trader/0.1.0": "bafybeidzaf6gytftigur646mo4fw2p5urq4cpvbf5t2gbw3fz5v6sseppq", + "service/valory/trader_pearl/0.1.0": "bafybeieqyhrucksqrxxhy2ad4blgsjd3iwhg4m6q3lgm2wlj2wlhz52xjq" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/trader/aea-config.yaml b/packages/valory/agents/trader/aea-config.yaml index 439aa089e..97ed8ecb7 100644 --- a/packages/valory/agents/trader/aea-config.yaml +++ b/packages/valory/agents/trader/aea-config.yaml @@ -45,10 +45,10 @@ skills: - valory/reset_pause_abci:0.1.0:bafybeigrdlxed3xlsnxtjhnsbl3cojruihxcqx4jxhgivkd5i2fkjncgba - valory/termination_abci:0.1.0:bafybeib5l7jhew5ic6iq24dd23nidcoimzqkrk556gqywhoziatj33zvwm - valory/transaction_settlement_abci:0.1.0:bafybeic7q7recyka272udwcupblwbkc3jkodgp74fvcdxb7urametg5dae -- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeie4rfme364fp2m3oo447622mc75tfk4hqftiebau2tpycftd5iffe +- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeic7wcls7yvvnpqrl7vfrab3dt37vyoytu2hef7emmc5556puqjpri - valory/market_manager_abci:0.1.0:bafybeicztk62pslofv6ui3aw3giw2tnvlfwfmatqbyvvzv4ampneu6isqa -- valory/decision_maker_abci:0.1.0:bafybeiffnd7vlryxtfenwcbbvx7qzszkko4oop2n2e6jg2ahdm3zholxsq -- valory/trader_abci:0.1.0:bafybeigygq76epkhl67uuvrjlrall65r4ilcwna2jokgjunrzg45gf2ihy +- valory/decision_maker_abci:0.1.0:bafybeicx3vjswhjzgfa2ddl7zxqzncuqbvej3m2unseje5lsajdhio7mji +- valory/trader_abci:0.1.0:bafybeiapf3qng7wczem3t7s5cysdlhzc3xign4sqkhyopro3p7x33hdugu - valory/staking_abci:0.1.0:bafybeictd5pxhscuhqntvctb7l5lfjausxt2m22rg5mkaiuj4cwwcxpvne - valory/check_stop_trading_abci:0.1.0:bafybeifmi64g4ki6zwbcncb35ovhd4sllw4xrszrkturpeqdhgf5bkiini - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm @@ -252,8 +252,6 @@ models: collateral_balance: ${int:10000000000000000000} mech_cost: ${int:10000000000000000} pool_fee: ${int:20000000000000000} - outcome_token_amounts: ${list:[11000000000000000000,9000000000000000000]} - outcome_token_marginal_prices: ${list:[0.4,0.6]} sep: ${str:,} dataset_filename: ${str:benchmark_data.csv} question_field: ${str:question} diff --git a/packages/valory/services/trader/service.yaml b/packages/valory/services/trader/service.yaml index 17f90c3ea..883a95061 100644 --- a/packages/valory/services/trader/service.yaml +++ b/packages/valory/services/trader/service.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeigtuothskwyvrhfosps2bu6suauycolj67dpuxqvnicdrdu7yhtvq fingerprint_ignore_patterns: [] -agent: valory/trader:0.1.0:bafybeibhv5pummw2ngrag6sk6a7phnffp2dfsswhvtxayzizqthea52kru +agent: valory/trader:0.1.0:bafybeieoklf7wjrnupyv4egny5hfn6mbxtrjwwtrqyznjoxodiy72cj3na number_of_agents: 4 deployment: agent: @@ -155,8 +155,6 @@ type: skill collateral_balance: ${BENCHMARKING_MODE_COLLATERAL_BALANCE:int:10000000000000000000} mech_cost: ${BENCHMARKING_MODE_MECH_COST:int:10000000000000000} pool_fee: ${BENCHMARKING_MODE_POOL_FEE:int:20000000000000000} - outcome_token_amounts: ${OUTCOME_TOKEN_AMOUNTS:list:[11000000000000000000,9000000000000000000]} - outcome_token_marginal_prices: ${OUTCOME_TOKEN_MARGINAL_PRICES:list:[0.55,0.45]} sep: ${BENCHMARKING_MODE_SEP:str:,} dataset_filename: ${BENCHMARKING_MODE_DATASET_FILENAME:str:benchmark_data.csv} question_field: ${BENCHMARKING_MODE_QUESTION_FIELD:str:question} diff --git a/packages/valory/services/trader_pearl/service.yaml b/packages/valory/services/trader_pearl/service.yaml index 21c758135..de5cc2158 100644 --- a/packages/valory/services/trader_pearl/service.yaml +++ b/packages/valory/services/trader_pearl/service.yaml @@ -8,7 +8,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeibg7bdqpioh4lmvknw3ygnllfku32oca4eq5pqtvdrdsgw6buko7e fingerprint_ignore_patterns: [] -agent: valory/trader:0.1.0:bafybeibhv5pummw2ngrag6sk6a7phnffp2dfsswhvtxayzizqthea52kru +agent: valory/trader:0.1.0:bafybeieoklf7wjrnupyv4egny5hfn6mbxtrjwwtrqyznjoxodiy72cj3na number_of_agents: 1 deployment: agent: diff --git a/packages/valory/skills/decision_maker_abci/behaviours/base.py b/packages/valory/skills/decision_maker_abci/behaviours/base.py index ee2bb2ca4..cc91d2a9a 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/base.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/base.py @@ -267,9 +267,10 @@ def is_first_period(self) -> bool: @property def sampled_bet(self) -> Bet: - """Get the sampled bet.""" + """Get the sampled bet and reset the bets list.""" self.read_bets() - return self.bets[self.synchronized_data.sampled_bet_index] + bet_index = self.synchronized_data.sampled_bet_index + return self.bets[bet_index] @property def collateral_token(self) -> str: @@ -286,6 +287,16 @@ def wei_to_native(wei: int) -> float: """Convert WEI to native token.""" return wei / 10**18 + def get_active_sampled_bet(self) -> Bet: + """Function to get the selected bet that is active without reseting self.bets.""" + bet_index = self.synchronized_data.sampled_bet_index + if len(self.bets) == 0: + msg = "The length of self.bets is 0" + self.context.logger.info(msg) + self.read_bets() + + return self.bets[bet_index] + def _collateral_amount_info(self, amount: int) -> str: """Get a description of the collateral token's amount.""" is_wxdai = True if self.benchmarking_mode.enabled else self.is_wxdai diff --git a/packages/valory/skills/decision_maker_abci/behaviours/blacklisting.py b/packages/valory/skills/decision_maker_abci/behaviours/blacklisting.py index acbe4c42b..ae50fcab7 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/blacklisting.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/blacklisting.py @@ -63,9 +63,7 @@ def async_act(self) -> Generator: with self.context.benchmark_tool.measure(self.behaviour_id).local(): self.read_bets() - # skip blacklisting when benchmarking as we should be based solely on the input data of the simulation - if not self.benchmarking_mode.enabled: - self._blacklist() + self._blacklist() self.store_bets() bets_hash = ( None if self.benchmarking_mode.enabled else self.hash_stored_bets() diff --git a/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py b/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py index 4c0636dc7..bd4273312 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py @@ -22,6 +22,7 @@ import csv import json from copy import deepcopy +from datetime import datetime from math import prod from typing import Any, Dict, Generator, List, Optional, Tuple, Union @@ -55,6 +56,7 @@ SLIPPAGE = 1.05 WRITE_TEXT_MODE = "w+t" COMMA = "," +TOKEN_PRECISION = 10**18 class DecisionReceiveBehaviour(DecisionMakerBaseBehaviour): @@ -101,7 +103,20 @@ def _next_dataset_row(self) -> Optional[Dict[str, str]]: dataset_filepath = ( self.params.store_path / self.benchmarking_mode.dataset_filename ) - next_mock_data_row = self.synchronized_data.next_mock_data_row + active_sampled_bet = self.get_active_sampled_bet() + sampled_bet_id = active_sampled_bet.id + + # we have now one reader pointer per market + available_rows_for_market = self.shared_state.bet_id_row_manager[sampled_bet_id] + if available_rows_for_market: + next_mock_data_row = available_rows_for_market[0] + else: + # no more bets available for this market + msg = f"No more mock responses for the market with id: {sampled_bet_id}" + self.context.logger.info(msg) + self.shared_state.last_benchmarking_has_run = True + self._rows_exceeded = True + return None row_with_headers: Optional[Dict[str, str]] = None with open(dataset_filepath) as read_dataset: @@ -115,10 +130,6 @@ def _next_dataset_row(self) -> Optional[Dict[str, str]]: self._rows_exceeded = True return None - next_row: Optional[Dict[str, str]] = next(reader, {}) - if not next_row: - self.shared_state.last_benchmarking_has_run = True - msg = f"Processing question in row with index {next_mock_data_row}: {row_with_headers}" self.context.logger.info(msg) return row_with_headers @@ -178,7 +189,7 @@ def _get_decision( self._get_response() if self._mech_response is None: - self.context.logger.info("The benchmarking has finished!") + self.context.logger.info("The mech response is None") return None self.context.logger.info(f"Decision has been received:\n{self.mech_response}") @@ -286,37 +297,28 @@ def _calc_binary_shares( return 0, 0 _, _, _, num_shares, available_shares = self._compute_new_tokens_distribution( - token_amounts, prices, net_bet_amount, vote + token_amounts.copy(), prices, net_bet_amount, vote ) return num_shares, available_shares - def _get_mocked_bet(self) -> Bet: - """Prepare the mocked bet based on the stored liquidity info.""" - shared_state = self.shared_state - question_id = shared_state.mock_question_id - benchmarking_mode = self.benchmarking_mode - current_liquidity_dictionary = shared_state.liquidity_amounts - outcome_token_amounts = current_liquidity_dictionary.setdefault( - question_id, benchmarking_mode.outcome_token_amounts.copy() - ) - outcome_token_marginal_prices = shared_state.liquidity_prices.setdefault( - question_id, benchmarking_mode.outcome_token_marginal_prices.copy() - ) - return Bet( - id="", - market="", - title="", - collateralToken="", - creator="", - fee=self.benchmarking_mode.pool_fee, - openingTimestamp=0, - outcomeSlotCount=2, - outcomeTokenAmounts=outcome_token_amounts, - outcomeTokenMarginalPrices=outcome_token_marginal_prices, - outcomes=["Yes", "No"], - scaledLiquidityMeasure=10, - ) + def _update_market_liquidity(self) -> None: + """Update the current market's liquidity information.""" + active_sampled_bet = self.get_active_sampled_bet() + question_id = active_sampled_bet.id + # check if share state information is empty and we need to initialize + empty_dict = len(self.shared_state.liquidity_amounts) == 0 + new_market = question_id not in self.shared_state.liquidity_amounts.keys() + if empty_dict or new_market: + self.shared_state.current_liquidity_amounts = ( + active_sampled_bet.outcomeTokenAmounts + ) + self.shared_state.current_liquidity_prices = ( + active_sampled_bet.outcomeTokenMarginalPrices + ) + self.shared_state.liquidity_cache[ + question_id + ] = active_sampled_bet.scaledLiquidityMeasure def _calculate_new_liquidity(self, net_bet_amount: int, vote: int) -> LiquidityInfo: """Calculate and return the new liquidity information.""" @@ -331,7 +333,7 @@ def _calculate_new_liquidity(self, net_bet_amount: int, vote: int) -> LiquidityI _, _, ) = self._compute_new_tokens_distribution( - token_amounts, prices, net_bet_amount, vote + token_amounts.copy(), prices, net_bet_amount, vote ) new_other = other_tokens_in_pool + other_shares @@ -350,8 +352,17 @@ def _calculate_new_liquidity(self, net_bet_amount: int, vote: int) -> LiquidityI new_selected, ) + def _compute_scaled_liquidity_measure( + self, token_amounts: List[int], token_prices: List[float] + ) -> float: + """Function to compute the scaled liquidity measure from token amounts and prices.""" + return ( + sum(amount * price for amount, price in zip(token_amounts, token_prices)) + / TOKEN_PRECISION + ) + def _update_liquidity_info(self, net_bet_amount: int, vote: int) -> LiquidityInfo: - """Update the liquidity information and the prices after placing a bet for a market.""" + """Update the liquidity information at shared state and the prices after placing a bet for a market.""" liquidity_info = self._calculate_new_liquidity(net_bet_amount, vote) l0_start, l1_start = liquidity_info.validate_start_information() @@ -362,17 +373,32 @@ def _update_liquidity_info(self, net_bet_amount: int, vote: int) -> LiquidityInf l0_start * prices[0], l1_start * prices[1], ] - + active_sampled_bet = self.get_active_sampled_bet() + market_id = active_sampled_bet.id self.shared_state.current_liquidity_prices = liquidity_info.get_new_prices( liquidity_constants ) self.shared_state.current_liquidity_amounts = liquidity_info.get_end_liquidity() + log_message = ( + f"New liquidity amounts: {self.shared_state.current_liquidity_amounts}" + ) + self.context.logger.info(log_message) + + # update the scaled liquidity Measure + self.shared_state.liquidity_cache[ + market_id + ] = self._compute_scaled_liquidity_measure( + self.shared_state.current_liquidity_amounts, + self.shared_state.current_liquidity_prices, + ) + return liquidity_info def rebet_allowed( self, prediction_response: PredictionResponse, potential_net_profit: int ) -> bool: """Whether a rebet is allowed or not.""" + # WARNING: Every time you call self.sampled_bet a reset in self.bets is done so any changes there will be lost bet = self.sampled_bet previous_response = deepcopy(bet.prediction_response) previous_liquidity = bet.position_liquidity @@ -397,11 +423,14 @@ def _is_profitable( if prediction_response.vote is None: return False, 0 - bet = ( - self.sampled_bet - if not self.benchmarking_mode.enabled - else self._get_mocked_bet() - ) + if self.benchmarking_mode.enabled: + bet = self.get_active_sampled_bet() # no reset + self.context.logger.info(f"Bet used for benchmarking: {bet}") + self._update_shared_data_liquidity() + else: + # this call is destroying what it was in self.bets + bet = self.sampled_bet + selected_type_tokens_in_pool, other_tokens_in_pool = self._get_bet_sample_info( bet, prediction_response.vote ) @@ -449,31 +478,56 @@ def _is_profitable( f"from buying {self.wei_to_native(num_shares)} shares for the option {bet.get_outcome(prediction_response.vote)}.\n" f"Decision for profitability of this market: {is_profitable}." ) + if is_profitable: + is_profitable = self.rebet_allowed( + prediction_response, potential_net_profit + ) if self.benchmarking_mode.enabled: if is_profitable: + # update the information at the shared state liquidity_info = self._update_liquidity_info( net_bet_amount, prediction_response.vote ) + bet.outcomeTokenAmounts = self.shared_state.current_liquidity_amounts + bet.outcomeTokenMarginalPrices = ( + self.shared_state.current_liquidity_prices + ) + bet.scaledLiquidityMeasure = self.shared_state.liquidity_cache[bet.id] + self.store_bets() self._write_benchmark_results( prediction_response, bet_amount, liquidity_info ) else: self._write_benchmark_results(prediction_response) - if is_profitable: - is_profitable = self.rebet_allowed( - prediction_response, potential_net_profit - ) - return is_profitable, bet_amount - def _update_selected_bet(self) -> None: + def _update_selected_bet( + self, prediction_response: Optional[PredictionResponse] + ) -> None: """Update the selected bet.""" - # update the bet's timestamp of processing and its number of bets for the given - sampled_bet = self.sampled_bet - sampled_bet.n_bets += 1 - sampled_bet.processed_timestamp = self.synced_timestamp + # update the bet's timestamp of processing and its number of bets for the given id + if self.benchmarking_mode.enabled: + active_sampled_bet = self.get_active_sampled_bet() + active_sampled_bet.processed_timestamp = ( + self.shared_state.get_simulated_now_timestamp( + self.bets, self.params.safe_voting_range + ) + ) + self.context.logger.info(f"Updating bet id: {active_sampled_bet.id}") + self.context.logger.info( + f"with the timestamp:{datetime.fromtimestamp(active_sampled_bet.processed_timestamp)}" + ) + if prediction_response is not None: + active_sampled_bet.n_bets += 1 + + else: + # update the bet's timestamp of processing and its number of bets for the given + sampled_bet = self.sampled_bet + sampled_bet.n_bets += 1 + sampled_bet.processed_timestamp = self.synced_timestamp + self.store_bets() def async_act(self) -> Generator: @@ -493,9 +547,6 @@ def async_act(self) -> Generator: self.store_bets() bets_hash = self.hash_stored_bets() - if self.benchmarking_mode.enabled: - next_mock_data_row = self.synchronized_data.next_mock_data_row + 1 - elif ( prediction_response is not None and self.benchmarking_mode.enabled @@ -505,9 +556,17 @@ def async_act(self) -> Generator: prediction_response, bet_amount, ) - next_mock_data_row = self.synchronized_data.next_mock_data_row + 1 - - self._update_selected_bet() + # always remove the processed trade from the benchmarking input file + # now there is one reader pointer per market + if self.benchmarking_mode.enabled: + # always remove the processed trade from the benchmarking input file + # now there is one reader pointer per market + bet = self.get_active_sampled_bet() + rows_queue = self.shared_state.bet_id_row_manager[bet.id] + if rows_queue: + rows_queue.pop(0) + + self._update_selected_bet(prediction_response) payload = DecisionReceivePayload( self.context.agent_address, bets_hash, diff --git a/packages/valory/skills/decision_maker_abci/behaviours/decision_request.py b/packages/valory/skills/decision_maker_abci/behaviours/decision_request.py index 8051d63e1..b887fb660 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/decision_request.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/decision_request.py @@ -19,9 +19,10 @@ """This module contains the behaviour of the skill which is responsible for requesting a decision from the mech.""" +import csv import json from dataclasses import asdict -from typing import Any, Dict, Generator, Optional +from typing import Any, Dict, Generator, List, Optional from uuid import uuid4 from packages.valory.skills.decision_maker_abci.behaviours.base import ( @@ -71,6 +72,22 @@ def setup(self) -> None: msg = f"Prepared metadata {self.metadata!r} for the request." self.context.logger.info(msg) + def initialize_bet_id_row_manager(self) -> Dict[str, List[int]]: + """Initialization of the dictionary used to traverse mocked tool responses.""" + bets_mapping: Dict[str, List[int]] = {} + dataset_filepath = ( + self.params.store_path / self.benchmarking_mode.dataset_filename + ) + + with open(dataset_filepath, mode="r") as file: + reader = csv.DictReader(file) + for row_number, row in enumerate(reader, start=1): + question_id = row[self.benchmarking_mode.question_id_field] + if question_id not in bets_mapping: + bets_mapping[question_id] = [] + bets_mapping[question_id].append(row_number) + return bets_mapping + def async_act(self) -> Generator: """Do the action.""" with self.context.benchmark_tool.measure(self.behaviour_id).local(): @@ -82,6 +99,12 @@ def async_act(self) -> Generator: if not self.n_slots_supported: mocking_mode = None + if self.benchmarking_mode.enabled: + # check if the bet_id_row_manager has been loaded already + if len(self.shared_state.bet_id_row_manager) == 0: + bets_mapping = self.initialize_bet_id_row_manager() + self.shared_state.bet_id_row_manager = bets_mapping + agent = self.context.agent_address payload = DecisionRequestPayload(agent, payload_content, mocking_mode) yield from self.finish_behaviour(payload) diff --git a/packages/valory/skills/decision_maker_abci/behaviours/sampling.py b/packages/valory/skills/decision_maker_abci/behaviours/sampling.py index b6fd79ba6..3b9f2089b 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/sampling.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/sampling.py @@ -20,6 +20,7 @@ """This module contains the behaviour for sampling a bet.""" import random +from datetime import datetime from typing import Any, Generator, List, Optional from packages.valory.skills.decision_maker_abci.behaviours.base import ( @@ -50,7 +51,10 @@ def setup(self) -> None: self.read_bets() has_bet_in_the_past = any(bet.n_bets > 0 for bet in self.bets) if has_bet_in_the_past: - random.seed(self.synchronized_data.most_voted_randomness) + if self.benchmarking_mode.enabled: + random.seed(self.benchmarking_mode.randomness) + else: + random.seed(self.synchronized_data.most_voted_randomness) self.should_rebet = random.random() <= self.params.rebet_chance # nosec rebetting_status = "enabled" if self.should_rebet else "disabled" self.context.logger.info(f"Rebetting {rebetting_status}.") @@ -60,10 +64,9 @@ def has_liquidity_changed(self, bet: Bet) -> bool: previous_bet_liquidity = self.shared_state.liquidity_cache.get(bet.id, None) return bet.scaledLiquidityMeasure != previous_bet_liquidity - def processable_bet(self, bet: Bet) -> bool: + def processable_bet(self, bet: Bet, now: int) -> bool: """Whether we can process the given bet.""" - now = self.synced_timestamp - # Note: `openingTimestamp` is the timestamp when a question stops being available for voting. + within_opening_range = bet.openingTimestamp <= ( now + self.params.sample_bets_closing_days * UNIX_DAY ) @@ -73,6 +76,7 @@ def processable_bet(self, bet: Bet) -> bool: - self.params.opening_margin - self.params.safe_voting_range ) + within_ranges = within_opening_range and within_safe_range # rebetting is allowed only if we have already placed at least one bet in this market. @@ -111,8 +115,20 @@ def _sampled_bet_idx(self, bets: List[Bet]) -> int: def _sample(self) -> Optional[int]: """Sample a bet, mark it as processed, and return its index.""" - available_bets = list(filter(self.processable_bet, self.bets)) - + # modify time "NOW" in benchmarking mode + if self.benchmarking_mode.enabled: + safe_voting_range = ( + self.params.opening_margin + self.params.safe_voting_range + ) + now = self.shared_state.get_simulated_now_timestamp( + self.bets, safe_voting_range + ) + self.context.logger.info(f"Simulating date: {datetime.fromtimestamp(now)}") + else: + now = self.synced_timestamp + available_bets = list( + filter(lambda bet: self.processable_bet(bet, now=now), self.bets) + ) if len(available_bets) == 0: msg = "There were no unprocessed bets available to sample from!" self.context.logger.warning(msg) @@ -135,11 +151,30 @@ def async_act(self) -> Generator: """Do the action.""" with self.context.benchmark_tool.measure(self.behaviour_id).local(): idx = self._sample() + benchmarking_finished = False + day_increased = False + if idx is None and self.benchmarking_mode.enabled: + self.context.logger.info( + "No more markets to bet in the simulated day. Increasing simulated day." + ) + self.shared_state.increase_one_day_simulation() + benchmarking_finished = self.shared_state.check_benchmarking_finished() + if benchmarking_finished: + self.context.logger.info( + "No more days to simulate in benchmarking mode." + ) + day_increased = True self.store_bets() if idx is None: bets_hash = None else: bets_hash = self.hash_stored_bets() - payload = SamplingPayload(self.context.agent_address, bets_hash, idx) + payload = SamplingPayload( + self.context.agent_address, + bets_hash, + idx, + benchmarking_finished, + day_increased, + ) yield from self.finish_behaviour(payload) diff --git a/packages/valory/skills/decision_maker_abci/fsm_specification.yaml b/packages/valory/skills/decision_maker_abci/fsm_specification.yaml index 08d15d3d3..9e29e8bfe 100644 --- a/packages/valory/skills/decision_maker_abci/fsm_specification.yaml +++ b/packages/valory/skills/decision_maker_abci/fsm_specification.yaml @@ -9,6 +9,7 @@ alphabet_in: - MECH_RESPONSE_ERROR - MOCK_MECH_REQUEST - MOCK_TX +- NEW_SIMULATED_RESAMPLE - NONE - NO_MAJORITY - NO_OP @@ -63,7 +64,7 @@ states: - SubscriptionRound - ToolSelectionRound transition_func: - (BenchmarkingRandomnessRound, DONE): ToolSelectionRound + (BenchmarkingRandomnessRound, DONE): SamplingRound (BenchmarkingRandomnessRound, NO_MAJORITY): BenchmarkingRandomnessRound (BenchmarkingRandomnessRound, ROUND_TIMEOUT): BenchmarkingRandomnessRound (BetPlacementRound, DONE): FinishedDecisionMakerRound @@ -74,7 +75,7 @@ transition_func: (BetPlacementRound, ROUND_TIMEOUT): BetPlacementRound (BlacklistingRound, DONE): FinishedWithoutDecisionRound (BlacklistingRound, FETCH_ERROR): ImpossibleRound - (BlacklistingRound, MOCK_TX): ToolSelectionRound + (BlacklistingRound, MOCK_TX): FinishedWithoutDecisionRound (BlacklistingRound, NONE): ImpossibleRound (BlacklistingRound, NO_MAJORITY): BlacklistingRound (BlacklistingRound, ROUND_TIMEOUT): BlacklistingRound @@ -88,7 +89,6 @@ transition_func: (ClaimRound, NO_MAJORITY): ClaimRound (ClaimRound, ROUND_TIMEOUT): ClaimRound (ClaimRound, SUBSCRIPTION_ERROR): ClaimRound - (DecisionReceiveRound, BENCHMARKING_FINISHED): BenchmarkingDoneRound (DecisionReceiveRound, DONE): BetPlacementRound (DecisionReceiveRound, MECH_RESPONSE_ERROR): BlacklistingRound (DecisionReceiveRound, NO_MAJORITY): DecisionReceiveRound @@ -107,16 +107,18 @@ transition_func: (RandomnessRound, NO_MAJORITY): RandomnessRound (RandomnessRound, ROUND_TIMEOUT): RandomnessRound (RedeemRound, DONE): FinishedDecisionMakerRound - (RedeemRound, MOCK_TX): ToolSelectionRound + (RedeemRound, MOCK_TX): SamplingRound (RedeemRound, NONE): ImpossibleRound (RedeemRound, NO_MAJORITY): RedeemRound (RedeemRound, NO_REDEEMING): FinishedWithoutRedeemingRound (RedeemRound, REDEEM_ROUND_TIMEOUT): FinishedWithoutRedeemingRound + (SamplingRound, BENCHMARKING_ENABLED): ToolSelectionRound + (SamplingRound, BENCHMARKING_FINISHED): BenchmarkingDoneRound (SamplingRound, DONE): SubscriptionRound (SamplingRound, FETCH_ERROR): ImpossibleRound + (SamplingRound, NEW_SIMULATED_RESAMPLE): SamplingRound (SamplingRound, NONE): FinishedWithoutDecisionRound (SamplingRound, NO_MAJORITY): SamplingRound - (SamplingRound, ROUND_TIMEOUT): SamplingRound (SubscriptionRound, DONE): FinishedSubscriptionRound (SubscriptionRound, MOCK_TX): ToolSelectionRound (SubscriptionRound, NONE): SubscriptionRound diff --git a/packages/valory/skills/decision_maker_abci/models.py b/packages/valory/skills/decision_maker_abci/models.py index 7b6b3a7c0..36cce15b0 100644 --- a/packages/valory/skills/decision_maker_abci/models.py +++ b/packages/valory/skills/decision_maker_abci/models.py @@ -21,7 +21,9 @@ import os import re +import time from dataclasses import dataclass, field +from datetime import datetime, timedelta from pathlib import Path from string import Template from typing import ( @@ -56,6 +58,7 @@ from packages.valory.skills.decision_maker_abci.policy import EGreedyPolicy from packages.valory.skills.decision_maker_abci.redeem_info import Trade from packages.valory.skills.decision_maker_abci.rounds import DecisionMakerAbciApp +from packages.valory.skills.market_manager_abci.bets import Bet from packages.valory.skills.market_manager_abci.models import ( MarketManagerParams, Subgraph, @@ -209,13 +212,21 @@ def __init__(self, *args: Any, skill_context: SkillContext, **kwargs: Any) -> No self.req_to_callback: Dict[str, Callable] = {} self.mock_data: Optional[BenchmarkingMockData] = None # a mapping from market id to scaled liquidity measure + # also used for the benchmarking mode self.liquidity_cache: Dict[str, float] = {} + # list with the simulated timestamps for the benchmarking mode + self.simulated_days: List[int] = [] + self.simulated_days_idx: int = 0 # latest liquidity information (only relevant to the benchmarking mode) self.liquidity_amounts: Dict[str, List[int]] = {} self.liquidity_prices: Dict[str, List[float]] = {} # whether this is the last run of the benchmarking mode self.last_benchmarking_has_run: bool = False + # the mapping from bet id to the row number in the dataset + # the key is the market id/question_id + self.bet_id_row_manager: Dict[str, List[int]] = {} + @property def mock_question_id(self) -> Any: """Get the mock question id.""" @@ -255,6 +266,62 @@ def current_liquidity_amounts(self, value: List[int]) -> None: """Set the current liquidity amounts.""" self.liquidity_amounts[self.mock_question_id] = value + @property + def bet_id_row_manager(self) -> Dict[str, List[int]]: + """Get the next_mock_data_row.""" + return self._bet_id_row_manager + + @bet_id_row_manager.setter + def bet_id_row_manager(self, mapping: Dict[str, List[int]]) -> None: + """Set the next_mock_data_row.""" + self._bet_id_row_manager = mapping + + def _initialize_simulated_now_timestamps( + self, bets: List[Bet], safe_voting_range: int + ) -> None: + """Creates the list of simulated days for the benchmarking mode""" + self.simulated_days_idx = 0 + # Find the maximum timestamp from openingTimestamp field + max_timestamp = max(bet.openingTimestamp for bet in bets) + # adding some time range to allow voting + # in the sampling round the within_safe condition is designed to check + # the openingtimestamp of the market strickly less than the safe voting range + # so we need to create a timestamp that passes this condition for the max openingtimestamp + max_timestamp = max_timestamp - safe_voting_range - 1 + + # Get current timestamp + now_timestamp = int(time.time()) + # Convert timestamps to datetime objects + max_date = datetime.fromtimestamp(max_timestamp) + current_date = datetime.fromtimestamp(now_timestamp) + self.context.logger.info( + f"Simulating timestamps between {current_date} and {max_date}" + ) + # Generate list of timestamps with one day intervals + timestamps = [] + while current_date <= max_date: + timestamps.append(int(current_date.timestamp())) + current_date += timedelta(days=1) + self.context.logger.info(f"Simulated timestamps: {timestamps}") + self.simulated_days = timestamps + + def increase_one_day_simulation(self) -> None: + """Increased the index used for the current simulated day.""" + self.simulated_days_idx += 1 + + def check_benchmarking_finished(self) -> bool: + """Checks if we simulated already all days.""" + return self.simulated_days_idx >= len(self.simulated_days) + + def get_simulated_now_timestamp( + self, bets: List[Bet], safe_voting_range: int + ) -> int: + """Gets the current simulated day timestamp.""" + if len(self.simulated_days) == 0: + self._initialize_simulated_now_timestamps(bets, safe_voting_range) + + return self.simulated_days[self.simulated_days_idx] + def setup(self) -> None: """Set up the model.""" super().setup() @@ -467,12 +534,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.collateral_balance: int = self._ensure("collateral_balance", kwargs, int) self.mech_cost: int = self._ensure("mech_cost", kwargs, int) self.pool_fee: int = self._ensure("pool_fee", kwargs, int) - self.outcome_token_amounts: List[int] = self._ensure( - "outcome_token_amounts", kwargs, List[int] - ) - self.outcome_token_marginal_prices: List[float] = self._ensure( - "outcome_token_marginal_prices", kwargs, List[float] - ) self.sep: str = self._ensure("sep", kwargs, str) self.dataset_filename: Path = Path( self._ensure("dataset_filename", kwargs, str) diff --git a/packages/valory/skills/decision_maker_abci/payloads.py b/packages/valory/skills/decision_maker_abci/payloads.py index fdfe8d2b5..0b8e9c2e6 100644 --- a/packages/valory/skills/decision_maker_abci/payloads.py +++ b/packages/valory/skills/decision_maker_abci/payloads.py @@ -42,6 +42,8 @@ class SamplingPayload(UpdateBetsPayload): """Represents a transaction payload for the sampling of a bet.""" index: Optional[int] + benchmarking_finished: Optional[bool] + day_increased: Optional[bool] @dataclass(frozen=True) diff --git a/packages/valory/skills/decision_maker_abci/rounds.py b/packages/valory/skills/decision_maker_abci/rounds.py index 3b3b3d1de..72a5062cd 100644 --- a/packages/valory/skills/decision_maker_abci/rounds.py +++ b/packages/valory/skills/decision_maker_abci/rounds.py @@ -96,7 +96,7 @@ class DecisionMakerAbciApp(AbciApp[Event]): - no op: 20. - blacklist: 20. 1. BenchmarkingRandomnessRound - - done: 6. + - done: 3. - round timeout: 1. - no majority: 1. 2. RandomnessRound @@ -107,7 +107,9 @@ class DecisionMakerAbciApp(AbciApp[Event]): - done: 4. - none: 16. - no majority: 3. - - round timeout: 3. + - new simulated resample: 3. + - benchmarking enabled: 6. + - benchmarking finished: 21. - fetch error: 20. 4. SubscriptionRound - done: 18. @@ -139,11 +141,10 @@ class DecisionMakerAbciApp(AbciApp[Event]): - no majority: 8. - tie: 9. - unprofitable: 9. - - benchmarking finished: 21. - round timeout: 8. 9. BlacklistingRound - done: 16. - - mock tx: 6. + - mock tx: 16. - none: 20. - no majority: 9. - round timeout: 9. @@ -157,7 +158,7 @@ class DecisionMakerAbciApp(AbciApp[Event]): - none: 20. 11. RedeemRound - done: 13. - - mock tx: 6. + - mock tx: 3. - no redeeming: 17. - no majority: 11. - redeem round timeout: 17. @@ -198,12 +199,13 @@ class DecisionMakerAbciApp(AbciApp[Event]): Event.BENCHMARKING_DISABLED: BenchmarkingModeDisabledRound, Event.NO_MAJORITY: CheckBenchmarkingModeRound, Event.ROUND_TIMEOUT: CheckBenchmarkingModeRound, - # added because of `autonomy analyse fsm-specs` falsely reporting them as missing from the transition + # added because of `autonomy analyse fsm-specs` + # falsely reporting them as missing from the transition Event.NO_OP: ImpossibleRound, Event.BLACKLIST: ImpossibleRound, }, BenchmarkingRandomnessRound: { - Event.DONE: ToolSelectionRound, + Event.DONE: SamplingRound, Event.ROUND_TIMEOUT: BenchmarkingRandomnessRound, Event.NO_MAJORITY: BenchmarkingRandomnessRound, }, @@ -216,8 +218,11 @@ class DecisionMakerAbciApp(AbciApp[Event]): Event.DONE: SubscriptionRound, Event.NONE: FinishedWithoutDecisionRound, Event.NO_MAJORITY: SamplingRound, - Event.ROUND_TIMEOUT: SamplingRound, - # this is here because of `autonomy analyse fsm-specs` falsely reporting it as missing from the transition + Event.NEW_SIMULATED_RESAMPLE: SamplingRound, + Event.BENCHMARKING_ENABLED: ToolSelectionRound, + Event.BENCHMARKING_FINISHED: BenchmarkingDoneRound, + # this is here because of `autonomy analyse fsm-specs` + # falsely reporting it as missing from the transition MarketManagerEvent.FETCH_ERROR: ImpossibleRound, }, SubscriptionRound: { @@ -256,37 +261,43 @@ class DecisionMakerAbciApp(AbciApp[Event]): Event.NO_MAJORITY: DecisionReceiveRound, Event.TIE: BlacklistingRound, Event.UNPROFITABLE: BlacklistingRound, - Event.BENCHMARKING_FINISHED: BenchmarkingDoneRound, - Event.ROUND_TIMEOUT: DecisionReceiveRound, # loop on the same state until Mech deliver is received + # loop on the same state until Mech deliver is received + Event.ROUND_TIMEOUT: DecisionReceiveRound, }, BlacklistingRound: { Event.DONE: FinishedWithoutDecisionRound, - Event.MOCK_TX: ToolSelectionRound, - Event.NONE: ImpossibleRound, # degenerate round on purpose, should never have reached here + Event.MOCK_TX: FinishedWithoutDecisionRound, + # degenerate round on purpose, should never have reached here + Event.NONE: ImpossibleRound, Event.NO_MAJORITY: BlacklistingRound, Event.ROUND_TIMEOUT: BlacklistingRound, - # this is here because of `autonomy analyse fsm-specs` falsely reporting it as missing from the transition + # this is here because of `autonomy analyse fsm-specs` + # falsely reporting it as missing from the transition MarketManagerEvent.FETCH_ERROR: ImpossibleRound, }, BetPlacementRound: { Event.DONE: FinishedDecisionMakerRound, # skip the bet placement tx Event.MOCK_TX: RedeemRound, - Event.INSUFFICIENT_BALANCE: RefillRequiredRound, # degenerate round on purpose, owner must refill the safe + # degenerate round on purpose, owner must refill the safe + Event.INSUFFICIENT_BALANCE: RefillRequiredRound, Event.NO_MAJORITY: BetPlacementRound, Event.ROUND_TIMEOUT: BetPlacementRound, - # this is here because of `autonomy analyse fsm-specs` falsely reporting it as missing from the transition + # this is here because of `autonomy analyse fsm-specs` + # falsely reporting it as missing from the transition Event.NONE: ImpossibleRound, }, RedeemRound: { Event.DONE: FinishedDecisionMakerRound, - Event.MOCK_TX: ToolSelectionRound, + Event.MOCK_TX: SamplingRound, Event.NO_REDEEMING: FinishedWithoutRedeemingRound, Event.NO_MAJORITY: RedeemRound, # in case of a round timeout, there likely is something wrong with redeeming - # it could be the RPC, or some other issue. We don't want to be stuck trying to redeem. + # it could be the RPC, or some other issue. + # We don't want to be stuck trying to redeem. Event.REDEEM_ROUND_TIMEOUT: FinishedWithoutRedeemingRound, - # this is here because of `autonomy analyse fsm-specs` falsely reporting it as missing from the transition + # this is here because of `autonomy analyse fsm-specs` falsely + # reporting it as missing from the transition Event.NONE: ImpossibleRound, }, HandleFailedTxRound: { diff --git a/packages/valory/skills/decision_maker_abci/skill.yaml b/packages/valory/skills/decision_maker_abci/skill.yaml index e395f6f39..ab453072d 100644 --- a/packages/valory/skills/decision_maker_abci/skill.yaml +++ b/packages/valory/skills/decision_maker_abci/skill.yaml @@ -12,45 +12,45 @@ fingerprint: README.md: bafybeia367zzdwndvlhw27rvnwodytjo3ms7gbc3q7mhrrjqjgfasnk47i __init__.py: bafybeih563ujnigeci2ldzh7hakbau6a222vsed7leg3b7lq32vcn3nm4a behaviours/__init__.py: bafybeih6ddz2ocvm6x6ytvlbcz6oi4snb5ee5xh5h65nq4w2qf7fd7zfky - behaviours/base.py: bafybeidjmsljdaw7y5chzzkub63mzf5ydpauch5ugojds5jwpeyvovkq3y + behaviours/base.py: bafybeifjgxzhwzxiky3okgtv4ojumm7fj7bom6qe3ysdvs3cpu32w446g4 behaviours/bet_placement.py: bafybeihmia64t2payxfqcnfdqg675ui2yp3hnyfwb2xhj2hn7wl237b4re - behaviours/blacklisting.py: bafybeicqwj7o4l7qcym5xjqwq45jngqkhyf44mn6qise2ysyfnlnwz7pdy + behaviours/blacklisting.py: bafybeifitqx2omj5qdwokizhqjkxvybtsyxo22dxkucbtxaocafzgbseku behaviours/check_benchmarking.py: bafybeiao2lyj7apezkqrpgsyzb3dwvrdgsrgtprf6iuhsmlsufvxfl5bci behaviours/claim_subscription.py: bafybeigbqkhc6mb73rbwaks32tfiqx6u2xza43uiy6rvbtrnqd6m4fru3e - behaviours/decision_receive.py: bafybeifb7aeu6g4xzmiyfc5ltipigsikfot3foux55gw5oaz2o5u5a4ume - behaviours/decision_request.py: bafybeiabjzzcwcfbfmtoftjhewmkgbhxfnigbc5cwmmxl6cob5gv64jwwa + behaviours/decision_receive.py: bafybeiga2eddrkbwnqcvzonbens4kj3i3fvfkdngoml5neoob23jgxtcpy + behaviours/decision_request.py: bafybeia22omb7tvocyfe3z2ucn5au5mcas7dg37ha42u7znefzrewjpk7y behaviours/handle_failed_tx.py: bafybeidxpc6u575ymct5tdwutvzov6zqfdoio5irgldn3fw7q3lg36mmxm behaviours/order_subscription.py: bafybeicrmdvhci5prfldvuf3bclbbqi6j7lpv6hmphw3qwgmkmwat3od44 behaviours/randomness.py: bafybeiaoj3awyyg2onhpsdsn3dyczs23gr4smuzqcbw3e5ocljwxswjkce behaviours/reedem.py: bafybeiaxwp4lx62owcaqfp6xcqh6567f5yvwnl4rage2f5hmq4nltkzjjy behaviours/round_behaviour.py: bafybeih63hpia2bwwzu563hxs5yd3t5ycvxvkfnhvxbzghbyy3mw3xjl3i - behaviours/sampling.py: bafybeie6ab5pdwal5glhg75am2637lijz5kwqx4ctk2lgqjyllmnw335ca + behaviours/sampling.py: bafybeidrcyeecrh2wzw3n56iwyon56rommc7nyjnlcdzhldpvoh25sfeoe behaviours/storage_manager.py: bafybeiez6daaj2bufxdcsghtmqybyrzdh74z26cc4ajsqsiy5krgjo2tla behaviours/tool_selection.py: bafybeienlxcgjs3ogyofli3d7q3p5rst3mcxxcnwqf7qolqjeefjtixeke dialogues.py: bafybeigpwuzku3we7axmxeamg7vn656maww6emuztau5pg3ebsoquyfdqm - fsm_specification.yaml: bafybeiabt2fonv63hozgr7bt4d5auom76iufzh6etpnmbwekktckz7644a + fsm_specification.yaml: bafybeiaokkmrfs5dewl5vmfeqwfqrwgzjhjml4t5o6krdellcqprrdxyqq handlers.py: bafybeigod6gbjrxy4mbmulbzsbokeaoycoqys64vqtxnumishfukzf73za io_/__init__.py: bafybeifxgmmwjqzezzn3e6keh2bfo4cyo7y5dq2ept3stfmgglbrzfl5rq io_/loader.py: bafybeih3sdsx5dhe4kzhtoafexjgkutsujwqy3zcdrlrkhtdks45bc7exa - models.py: bafybeifhmwxoix5fqka4phtmv6324osei52zwirg3ogziondvlq33brnby - payloads.py: bafybeicloiy4ax7dlipwp4czlueflgjgtlev4a6vhn2m7ztoehnemiiko4 + models.py: bafybeieqknkktfrs5e4maq4cosevabkbneszw4dp44sz7mavr4hpdy6yem + payloads.py: bafybeihzwxhdc6a6ek2lvsyzkex4ypsl7fnahszal3dxtr7ce6wp7nmgmm policy.py: bafybeihlzs4o5e7yfmfzcvvrzkf4bhxfsg5gxnzsrpepwgfugh45gafye4 redeem_info.py: bafybeifiiix4gihfo4avraxt34sfw35v6dqq45do2drrssei2shbps63mm - rounds.py: bafybeihczrvkid4lqoai7myqqs4f3g5diqsnjotxtqhxhm5c7oowzifbqy + rounds.py: bafybeidky5eoxti37j3j6ue3uyna6ibljskzzfadhehyj6jqry2lwoi7ou states/__init__.py: bafybeid23llnyp6j257dluxmrnztugo5llsrog7kua53hllyktz4dqhqoy - states/base.py: bafybeicrzw6p4dq52adkdjlduplt3expzajqmd3nkcpsw5yalfc3dwv75q + states/base.py: bafybeifiqeedqo2etvp4s7owkza6ykepho4s3nvkf43rcwgh4pado2wdiy states/bet_placement.py: bafybeibalhxhp2c4oljmiwqi6ds3g36fgtabmf42mb5sgq6z22znrcbhda states/blacklisting.py: bafybeiapelgjhbjjn4uq4z5gspyirqzwzgccg5anktrp5kxdwamfnfw5mi states/check_benchmarking.py: bafybeiabv6pq7q45jd3nkor5afmlycqgec5ctuwcfbdukkjjm4imesv4ni states/claim_subscription.py: bafybeiampifhdoztggwj6gthl2hfzecmjcwnm6nic2o47q4je7j4x3ujne - states/decision_receive.py: bafybeicr7bxjbgzo4thqlsczsyebabhplfztkd3c3og3wm6cxhv56pad5q + states/decision_receive.py: bafybeifalcb4oxenrz63at3c267eqvo2ntfwsc6lur2jgm2damc7foegoa states/decision_request.py: bafybeiarv3r5j7cfvxmudki2llbdl2pvf24p5mvsva6bdgrylnwdyag5xy states/final_states.py: bafybeicjrrojo3gmfaxzicwloyorlnqgzl6a2avevo4nvhoh424zwzmbti states/handle_failed_tx.py: bafybeihewm2vernvhktuorljdupjqcg2p5vs6wvsira2d62wkoyo5xlzjm states/order_subscription.py: bafybeidrcef2op3nhq4tjgri3ct5hfhmc22xw2ep6dk4ro6tg7ob6cmioa states/randomness.py: bafybeiceoo4nx3t4dofpwczw3v5mclramwmzpwjs6hv7l56arodrjx4l5u states/redeem.py: bafybeica6cn4xg7shea2wjhbqnddgxe5zao2hkmceltze7qknxdhtsoaxe - states/sampling.py: bafybeihriyyskmlupjiwytx2pdpftms7plkjku5bip64c3ztx7oi3n43ci + states/sampling.py: bafybeihnziujwxmxl623id7zrkaefzs3tvb2vdxjujcyyn3gcun3k2gcdu states/tool_selection.py: bafybeiak5ihuie4nxh3sguiea6pcdgyxr4k4xyzvq6o2uj5xpf7urocawy tests/__init__.py: bafybeiakpi3k3kc7wrjj7hrluvjcj36lu2gezpmrctwiz5yg2fe7ggnf3i tests/behaviours/__init__.py: bafybeic7icz7lfhfepdkqkase7y7zn3a6pwdw6fx4ah2hajmgejawpolc4 @@ -71,11 +71,11 @@ fingerprint: tests/states/test_order_subscription.py: bafybeiag37uk5mitjm4yadcpr67icfmmzeucdlgvqoct4y4jeyhnebyyoi tests/states/test_randomness.py: bafybeib3eqjv6mhlprzda7d4viddn5alrfqteq6juyg3ccejseoywcsbey tests/states/test_redeem.py: bafybeiezdnfrxukb2xpwffrr357g2anmdkwy7wo3nphvlggipq5xrdzr7a - tests/states/test_sampling.py: bafybeibyglipxdl6f25qfxf36v2n3kckrpmwyuqcenfeqzhjujpwon6o2u + tests/states/test_sampling.py: bafybeifvbzikke6wtex2p5j7fsnpdbj4qqxl5vh2lm2m2apgvuqdonoyzm tests/states/test_tool_selection.py: bafybeib7js3dj7647t33o5ybfqftwytxktwrvhbri5yuyymg6znj6y7xxa tests/test_dialogues.py: bafybeibulo64tgfrq4e5qbcqnmifrlehkqciwuavublints353zaj2mlpa tests/test_handlers.py: bafybeihpkgtjjm3uegpup6zkznpoaxqpu6kmp3ujiggrzbe73p5fzlq7im - tests/test_payloads.py: bafybeifc2os3orozmsxbrcfp4c4vrweojo6g4ebxinr5ilescraw6qm6sa + tests/test_payloads.py: bafybeicqrk7f4gpn3snddjpebwgnzbj3uu4rl3pni2tuihc3db2y2jgfam tests/test_rounds.py: bafybeigifftusd4ew42tyvyrr55o2uehhcik2gdq3atkpjwwlqdeskedty utils/__init__.py: bafybeiazrfg3kwfdl5q45azwz6b6mobqxngxpf4hazmrnkhinpk4qhbbf4 utils/nevermined.py: bafybeigallaqxhqopznhjhefr6bukh4ojkz5vdtqyzod5dksshrf24fjgi diff --git a/packages/valory/skills/decision_maker_abci/states/base.py b/packages/valory/skills/decision_maker_abci/states/base.py index 320457259..9608decfb 100644 --- a/packages/valory/skills/decision_maker_abci/states/base.py +++ b/packages/valory/skills/decision_maker_abci/states/base.py @@ -66,6 +66,7 @@ class Event(Enum): ROUND_TIMEOUT = "round_timeout" REDEEM_ROUND_TIMEOUT = "redeem_round_timeout" NO_MAJORITY = "no_majority" + NEW_SIMULATED_RESAMPLE = "new_simulated_resample" class SynchronizedData(MarketManagerSyncedData, TxSettlementSyncedData): @@ -79,6 +80,16 @@ def sampled_bet_index(self) -> int: """Get the sampled bet.""" return int(self.db.get_strict("sampled_bet_index")) + @property + def benchmarking_finished(self) -> int: + """Get the flag of benchmarking finished.""" + return int(self.db.get_strict("benchmarking_finished")) + + @property + def simulated_day(self) -> int: + """Get the flag of simulated_day.""" + return int(self.db.get_strict("simulated_day")) + @property def is_mech_price_set(self) -> bool: """Get whether mech's price is known.""" diff --git a/packages/valory/skills/decision_maker_abci/states/decision_receive.py b/packages/valory/skills/decision_maker_abci/states/decision_receive.py index c5a17ecda..1f1080c26 100644 --- a/packages/valory/skills/decision_maker_abci/states/decision_receive.py +++ b/packages/valory/skills/decision_maker_abci/states/decision_receive.py @@ -66,7 +66,4 @@ def end_block(self) -> Optional[Tuple[SynchronizedData, Enum]]: if event == Event.DONE and not synced_data.is_profitable: return synced_data, Event.UNPROFITABLE - if event == Event.MECH_RESPONSE_ERROR and synced_data.mocking_mode: - return synced_data, Event.BENCHMARKING_FINISHED - return synced_data, event diff --git a/packages/valory/skills/decision_maker_abci/states/sampling.py b/packages/valory/skills/decision_maker_abci/states/sampling.py index 7fbfe419a..bda0279b4 100644 --- a/packages/valory/skills/decision_maker_abci/states/sampling.py +++ b/packages/valory/skills/decision_maker_abci/states/sampling.py @@ -19,9 +19,13 @@ """This module contains the sampling state of the decision-making abci app.""" -from typing import Any, Type +from enum import Enum +from typing import Any, Optional, Tuple, Type, cast -from packages.valory.skills.abstract_round_abci.base import get_name +from packages.valory.skills.abstract_round_abci.base import ( + BaseSynchronizedData, + get_name, +) from packages.valory.skills.decision_maker_abci.payloads import SamplingPayload from packages.valory.skills.decision_maker_abci.states.base import ( Event, @@ -41,4 +45,30 @@ class SamplingRound(UpdateBetsRound): selection_key: Any = ( UpdateBetsRound.selection_key, get_name(SynchronizedData.sampled_bet_index), + get_name(SynchronizedData.benchmarking_finished), + get_name(SynchronizedData.simulated_day), ) + + def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Enum]]: + """Process the end of the block.""" + res = super().end_block() + if res is None: + return None + + synced_data, event = cast(Tuple[SynchronizedData, Enum], res) + + if synced_data.benchmarking_finished: + self.context.logger.info( + "No more markets to bet. The benchmarking has finished!" + ) + return synced_data, Event.BENCHMARKING_FINISHED + + if synced_data.simulated_day: + self.context.logger.info( + "Entering the sampling Round for a new simulated day" + ) + # re-enter the SamplingRound + return synced_data, Event.NEW_SIMULATED_RESAMPLE + if event == Event.DONE and self.context.benchmarking_mode.enabled: + return synced_data, Event.BENCHMARKING_ENABLED + return res diff --git a/packages/valory/skills/decision_maker_abci/tests/states/test_sampling.py b/packages/valory/skills/decision_maker_abci/tests/states/test_sampling.py index 00b53cb00..50594d9bd 100644 --- a/packages/valory/skills/decision_maker_abci/tests/states/test_sampling.py +++ b/packages/valory/skills/decision_maker_abci/tests/states/test_sampling.py @@ -36,6 +36,8 @@ class MockSynchronizedData(SynchronizedData): """A mock class for SynchronizedData to provide necessary attributes.""" sampled_bet_index = 0 # Default value for sampled_bet_index + benchmarking_finished = False + simulated_day = False def __init__(self, db: AbciAppDB): """Initialize MockSynchronizedData with the given db.""" @@ -77,7 +79,11 @@ def test_sampling_round_properties( def test_sampling_payload_initialization(self) -> None: """Test the initialization of the SamplingPayload.""" payload = SamplingPayload( - sender="mock_sender", bets_hash="mock_bets_hash", index=0 + sender="mock_sender", + bets_hash="mock_bets_hash", + index=0, + benchmarking_finished=False, + day_increased=False, ) # Added index assert payload is not None assert payload.sender == "mock_sender" @@ -95,9 +101,10 @@ def test_sampling_round_selection_key( sampling_round = setup_sampling_round expected_selection_key = ( UpdateBetsRound.selection_key, - get_name( - SynchronizedData.sampled_bet_index - ), # Pass the property, not the value + get_name(SynchronizedData.sampled_bet_index), + get_name(SynchronizedData.benchmarking_finished), + get_name(SynchronizedData.simulated_day), + # Pass the property, not the value ) assert sampling_round.selection_key == expected_selection_key diff --git a/packages/valory/skills/decision_maker_abci/tests/test_payloads.py b/packages/valory/skills/decision_maker_abci/tests/test_payloads.py index 99c391367..980b1082d 100644 --- a/packages/valory/skills/decision_maker_abci/tests/test_payloads.py +++ b/packages/valory/skills/decision_maker_abci/tests/test_payloads.py @@ -52,7 +52,12 @@ ), ( SamplingPayload, - {"index": 1, "bets_hash": "dummy_bets_hash"}, + { + "index": 1, + "bets_hash": "dummy_bets_hash", + "benchmarking_finished": False, + "day_increased": False, + }, ), ( MultisigTxPayload, diff --git a/packages/valory/skills/trader_abci/fsm_specification.yaml b/packages/valory/skills/trader_abci/fsm_specification.yaml index accad83eb..c4dc6bf81 100644 --- a/packages/valory/skills/trader_abci/fsm_specification.yaml +++ b/packages/valory/skills/trader_abci/fsm_specification.yaml @@ -20,6 +20,7 @@ alphabet_in: - MOCK_MECH_REQUEST - MOCK_TX - NEGATIVE +- NEW_SIMULATED_RESAMPLE - NEXT_CHECKPOINT_NOT_REACHED_YET - NONE - NO_MAJORITY @@ -93,7 +94,7 @@ states: - UpdateBetsRound - ValidateTransactionRound transition_func: - (BenchmarkingRandomnessRound, DONE): ToolSelectionRound + (BenchmarkingRandomnessRound, DONE): SamplingRound (BenchmarkingRandomnessRound, NO_MAJORITY): BenchmarkingRandomnessRound (BenchmarkingRandomnessRound, ROUND_TIMEOUT): BenchmarkingRandomnessRound (BetPlacementRound, DONE): PreTxSettlementRound @@ -104,7 +105,7 @@ transition_func: (BetPlacementRound, ROUND_TIMEOUT): BetPlacementRound (BlacklistingRound, DONE): RedeemRound (BlacklistingRound, FETCH_ERROR): ImpossibleRound - (BlacklistingRound, MOCK_TX): ToolSelectionRound + (BlacklistingRound, MOCK_TX): RedeemRound (BlacklistingRound, NONE): ImpossibleRound (BlacklistingRound, NO_MAJORITY): BlacklistingRound (BlacklistingRound, ROUND_TIMEOUT): BlacklistingRound @@ -144,7 +145,6 @@ transition_func: (CollectSignatureRound, DONE): FinalizationRound (CollectSignatureRound, NO_MAJORITY): ResetRound (CollectSignatureRound, ROUND_TIMEOUT): CollectSignatureRound - (DecisionReceiveRound, BENCHMARKING_FINISHED): ResetAndPauseRound (DecisionReceiveRound, DONE): BetPlacementRound (DecisionReceiveRound, MECH_RESPONSE_ERROR): BlacklistingRound (DecisionReceiveRound, NO_MAJORITY): DecisionReceiveRound @@ -190,7 +190,7 @@ transition_func: (RandomnessTransactionSubmissionRound, NO_MAJORITY): RandomnessTransactionSubmissionRound (RandomnessTransactionSubmissionRound, ROUND_TIMEOUT): RandomnessTransactionSubmissionRound (RedeemRound, DONE): PreTxSettlementRound - (RedeemRound, MOCK_TX): ToolSelectionRound + (RedeemRound, MOCK_TX): SamplingRound (RedeemRound, NONE): ImpossibleRound (RedeemRound, NO_MAJORITY): RedeemRound (RedeemRound, NO_REDEEMING): CallCheckpointRound @@ -204,11 +204,13 @@ transition_func: (ResetRound, DONE): RandomnessTransactionSubmissionRound (ResetRound, NO_MAJORITY): HandleFailedTxRound (ResetRound, RESET_TIMEOUT): HandleFailedTxRound + (SamplingRound, BENCHMARKING_ENABLED): ToolSelectionRound + (SamplingRound, BENCHMARKING_FINISHED): ResetAndPauseRound (SamplingRound, DONE): SubscriptionRound (SamplingRound, FETCH_ERROR): ImpossibleRound + (SamplingRound, NEW_SIMULATED_RESAMPLE): SamplingRound (SamplingRound, NONE): RedeemRound (SamplingRound, NO_MAJORITY): SamplingRound - (SamplingRound, ROUND_TIMEOUT): SamplingRound (SelectKeeperTransactionSubmissionARound, DONE): CollectSignatureRound (SelectKeeperTransactionSubmissionARound, INCORRECT_SERIALIZATION): HandleFailedTxRound (SelectKeeperTransactionSubmissionARound, NO_MAJORITY): ResetRound diff --git a/packages/valory/skills/trader_abci/skill.yaml b/packages/valory/skills/trader_abci/skill.yaml index 73b509fc0..20f14a299 100644 --- a/packages/valory/skills/trader_abci/skill.yaml +++ b/packages/valory/skills/trader_abci/skill.yaml @@ -11,7 +11,7 @@ fingerprint: behaviours.py: bafybeigc6hszbu66ccajny5eh7thfgsrlr36je4mzziwp4mupgvtaeu6aa composition.py: bafybeifxerfvssuhodqmtvkz6umlmrmdqjv5ptpszhnwlavzxaavdpdyly dialogues.py: bafybeiebofyykseqp3fmif36cqmmyf3k7d2zbocpl6t6wnlpv4szghrxbm - fsm_specification.yaml: bafybeibgm4crhqny63ax36447cglnycvlwnwuwse62bob64zbya4vjfgnm + fsm_specification.yaml: bafybeicbwdpl7w65yljzuyvx4y5kaoon5cqsiskxtg5r25mc5r7ggqnqyi handlers.py: bafybeibbxybbi66em63ad33cllymypr3za3f5xvor3m2krhuxoyxnqjnxu models.py: bafybeih2vkf4ln7n7ar27iemho7w7sdr4clmhbnhbcznmsri6mc2skkky4 tests/__init__.py: bafybeiadatapyjh3e7ucg2ehz77oms3ihrbutwb2cs2tkjehy54utwvuyi @@ -27,8 +27,8 @@ skills: - valory/transaction_settlement_abci:0.1.0:bafybeic7q7recyka272udwcupblwbkc3jkodgp74fvcdxb7urametg5dae - valory/termination_abci:0.1.0:bafybeib5l7jhew5ic6iq24dd23nidcoimzqkrk556gqywhoziatj33zvwm - valory/market_manager_abci:0.1.0:bafybeicztk62pslofv6ui3aw3giw2tnvlfwfmatqbyvvzv4ampneu6isqa -- valory/decision_maker_abci:0.1.0:bafybeiffnd7vlryxtfenwcbbvx7qzszkko4oop2n2e6jg2ahdm3zholxsq -- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeie4rfme364fp2m3oo447622mc75tfk4hqftiebau2tpycftd5iffe +- valory/decision_maker_abci:0.1.0:bafybeicx3vjswhjzgfa2ddl7zxqzncuqbvej3m2unseje5lsajdhio7mji +- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeic7wcls7yvvnpqrl7vfrab3dt37vyoytu2hef7emmc5556puqjpri - valory/staking_abci:0.1.0:bafybeictd5pxhscuhqntvctb7l5lfjausxt2m22rg5mkaiuj4cwwcxpvne - valory/check_stop_trading_abci:0.1.0:bafybeifmi64g4ki6zwbcncb35ovhd4sllw4xrszrkturpeqdhgf5bkiini - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm diff --git a/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml b/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml index e69087ff1..dbe1730b0 100644 --- a/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml +++ b/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml @@ -23,7 +23,7 @@ protocols: - valory/ledger_api:1.0.0:bafybeihdk6psr4guxmbcrc26jr2cbgzpd5aljkqvpwo64bvaz7tdti2oni skills: - valory/abstract_round_abci:0.1.0:bafybeib733xfbndtpvkf44mtk7oyodnficgloo6xhn7xmqxxeos33es65u -- valory/decision_maker_abci:0.1.0:bafybeiffnd7vlryxtfenwcbbvx7qzszkko4oop2n2e6jg2ahdm3zholxsq +- valory/decision_maker_abci:0.1.0:bafybeicx3vjswhjzgfa2ddl7zxqzncuqbvej3m2unseje5lsajdhio7mji - valory/staking_abci:0.1.0:bafybeictd5pxhscuhqntvctb7l5lfjausxt2m22rg5mkaiuj4cwwcxpvne - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm behaviours: