diff --git a/packages/packages.json b/packages/packages.json index 4da909a..0d20f25 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -3,10 +3,10 @@ "contract/valory/conditional_tokens/0.1.0": "bafybeifbhritgoqecuilj35uzrrokm2ngfmwkoafbmzxgvpa4q3wpi6i64", "contract/valory/fpmm_deterministic_factory/0.1.0": "bafybeih5yb4df6plwsxicp4jtywvpqmjnvfw55zw35yuycwxrx7mehoowy", "contract/valory/realtio/0.1.0": "bafybeihfdvkrjkhezqeaxj3eluv2v4syvnxni6jbuc3knqb4rawrcivcke", - "skill/valory/market_creation_manager_abci/0.1.0": "bafybeia6sk3zu5cwlxryqnl6jp73bplri32oyhodofzj4gyxu56l26ueuq", - "skill/valory/market_maker_abci/0.1.0": "bafybeihl5s3edflvboifkg44kipx54mk7oxpzeag7vzpgd4cpwjgcthrou", - "agent/valory/market_maker/0.1.0": "bafybeiftycxdzvw62ouvlohwsjl4mldtnihn4lsztdazra4ylrmydc6zoe", - "service/valory/market_maker/0.1.0": "bafybeiai5qv7pzpuuy7vnmogq6eyeqcs5e53im7b262xjhetibc2culfuq", + "skill/valory/market_creation_manager_abci/0.1.0": "bafybeie67exdnu5phmap5q7xfwgjhfitgyld7bgwvzr2z2nhe7apwwqlaa", + "skill/valory/market_maker_abci/0.1.0": "bafybeidfmvqcnzcd6zy6r56xek4rqjremlcfc37x6is6qggp4mdsblmpyq", + "agent/valory/market_maker/0.1.0": "bafybeialdamcroevqq7d77jiuxg7ga7sq6uzc23uqauiaa3hjskzga2hfu", + "service/valory/market_maker/0.1.0": "bafybeia4h3jxysymq3c7v6ri4lcq4ybchixgytlqw43s5ijgoocc4cadly", "contract/valory/wxdai/0.1.0": "bafybeidalocwbhmbto6ii6adldtpcughtdt6j3v4tv36utevjk2wrdyqie", "contract/valory/fpmm/0.1.0": "bafybeiai2ruj27nnglvn7yc5atojyojo3fkmofw6wrjgz2ybps2uwdizx4" }, diff --git a/packages/valory/agents/market_maker/aea-config.yaml b/packages/valory/agents/market_maker/aea-config.yaml index b8bd0cf..65b80af 100644 --- a/packages/valory/agents/market_maker/aea-config.yaml +++ b/packages/valory/agents/market_maker/aea-config.yaml @@ -37,9 +37,9 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeiflcfufixmsrhobf56bn5745m2iipcfqyulwk2qegtnagb3kvaaxi - valory/abstract_round_abci:0.1.0:bafybeiaqcl7h2famylusiffigwem7tevkcsyocdu5xd42jkmgq6kvowzgq -- valory/market_maker_abci:0.1.0:bafybeihl5s3edflvboifkg44kipx54mk7oxpzeag7vzpgd4cpwjgcthrou +- valory/market_maker_abci:0.1.0:bafybeidfmvqcnzcd6zy6r56xek4rqjremlcfc37x6is6qggp4mdsblmpyq - valory/registration_abci:0.1.0:bafybeic2ynseiak7jpta7jfwuqwyp453b4p7lolr4wihxmpn633uekv5am -- valory/market_creation_manager_abci:0.1.0:bafybeia6sk3zu5cwlxryqnl6jp73bplri32oyhodofzj4gyxu56l26ueuq +- valory/market_creation_manager_abci:0.1.0:bafybeie67exdnu5phmap5q7xfwgjhfitgyld7bgwvzr2z2nhe7apwwqlaa - valory/reset_pause_abci:0.1.0:bafybeidzajbe3erygeh2xbd6lrjv7nsptznjuzrt24ykgvhgotdeyhfnba - valory/termination_abci:0.1.0:bafybeie4zvjfxvdu7qrulmur3chpjz3kpj5m4bjsxvpk4gvj5zbyyayfaa - valory/transaction_settlement_abci:0.1.0:bafybeiaefgqbs7zsn5xe5kdwrujj7ivygkn3ujpw6crnvi3knvxw75qmja diff --git a/packages/valory/services/market_maker/service.yaml b/packages/valory/services/market_maker/service.yaml index 5eb5fd5..f88eb21 100644 --- a/packages/valory/services/market_maker/service.yaml +++ b/packages/valory/services/market_maker/service.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeibwz3af6326msp4h3kqehijvmyhaytvyfbo3o2npc2w4b6zrg6pfq fingerprint_ignore_patterns: [] -agent: valory/market_maker:0.1.0:bafybeiftycxdzvw62ouvlohwsjl4mldtnihn4lsztdazra4ylrmydc6zoe +agent: valory/market_maker:0.1.0:bafybeialdamcroevqq7d77jiuxg7ga7sq6uzc23uqauiaa3hjskzga2hfu number_of_agents: 1 deployment: agent: diff --git a/packages/valory/skills/market_creation_manager_abci/behaviours.py b/packages/valory/skills/market_creation_manager_abci/behaviours.py index 5207e44..d5fea05 100644 --- a/packages/valory/skills/market_creation_manager_abci/behaviours.py +++ b/packages/valory/skills/market_creation_manager_abci/behaviours.py @@ -22,6 +22,7 @@ import json import random from abc import ABC +from collections import defaultdict from datetime import datetime, timedelta, timezone from string import Template from typing import ( @@ -172,8 +173,12 @@ FPMM_QUERY = Template( """{ fixedProductMarketMakers( - where: {creator: "$creator"} - first: 100 + where: { + creator: "$creator" + openingTimestamp_gte:"$openingTimestamp_gte" + openingTimestamp_lte:"$openingTimestamp_lte" + } + first: 1000 orderBy: creationTimestamp orderDirection: desc ) { @@ -448,69 +453,84 @@ def async_act(self) -> Generator: with self.context.benchmark_tool.measure(self.behaviour_id).local(): sender = self.context.agent_address - latest_open_markets = yield from self._collect_latest_open_markets() + current_timestamp = self.last_synced_timestamp + self.context.logger.info(f"current_timestamp={current_timestamp}") + + openingTimestamp_gte = current_timestamp + _ONE_DAY + self.context.logger.info(f"openingTimestamp_gte={openingTimestamp_gte}") + + openingTimestamp_lte = current_timestamp + ( + self.params.approve_market_event_days_offset * _ONE_DAY + ) + self.context.logger.info(f"openingTimestamp_lte={openingTimestamp_lte}") + + # Compute required openingTimestamp (between now and now + approve_market_event_days_offset) + required_opening_ts = [] + current_day_start_timestamp = ( + openingTimestamp_gte - (openingTimestamp_gte % _ONE_DAY) + _ONE_DAY + ) + while current_day_start_timestamp <= openingTimestamp_lte: + required_opening_ts.append(current_day_start_timestamp) + current_day_start_timestamp += _ONE_DAY + + self.context.logger.info(f"required_opening_ts={required_opening_ts}") + + # Get existing (open) markets count per openingTimestamp (between now and now + approve_market_event_days_offset) + latest_open_markets = yield from self._collect_latest_open_markets( + openingTimestamp_gte, openingTimestamp_lte + ) + existing_market_count: Dict[int, int] = defaultdict(int) + + for market in latest_open_markets["fixedProductMarketMakers"]: + ts = int(market.get("openingTimestamp")) + existing_market_count[ts] += 1 + + self.context.logger.info(f"existing_market_count={existing_market_count}") + + # Determine number of markets required to be approved per openingTimestamp (between now and now + approve_market_event_days_offset) + required_markets_to_approve: Dict[int, int] = defaultdict(int) + N = self.params.markets_to_approve_per_day + + for ts in required_opening_ts: + required_markets_to_approve[ts] = max( + 0, N - existing_market_count.get(ts, 0) + ) + + num_markets_to_approve = sum(required_markets_to_approve.values()) + self.context.logger.info( - f"Collected latest open markets: {latest_open_markets}" + f"markets_to_approve={required_markets_to_approve}" ) + self.context.logger.info(f"num_markets_to_approve={num_markets_to_approve}") - current_timestamp = self.last_synced_timestamp + # Determine largest creation timestamp in markets with openingTimestamp between now and now + approve_market_event_days_offset creation_timestamps = [ int(entry["creationTimestamp"]) for entry in latest_open_markets.get("fixedProductMarketMakers", {}) ] largest_creation_timestamp = max(creation_timestamps) - - latest_approve_market_timestamp = ( - self.synchronized_data.approved_markets_timestamp - ) - - # Determine num_markets_to_approve so that each day there are N closing markets. - opening_timestamps = [ - int(entry["openingTimestamp"]) - for entry in latest_open_markets.get("fixedProductMarketMakers", {}) - ] - sorted_opening_timestamps = sorted(opening_timestamps, reverse=True) - # TODO Make params - N = self.params.markets_to_approve_per_day - latest_opening_timestamps = sorted_opening_timestamps[:N] - approve_market_event_days_offset = ( - self.params.approve_market_event_days_offset - ) - start_timestamp = ( - current_timestamp + approve_market_event_days_offset * _ONE_DAY - ) - end_timestamp = ( - current_timestamp + (approve_market_event_days_offset + 1) * _ONE_DAY - ) - num_markets_to_approve = sum( - 1 - for timestamp in latest_opening_timestamps - if timestamp <= start_timestamp + self.context.logger.info( + f"largest_creation_timestamp={largest_creation_timestamp}" ) + # Collect misc data related to market approval min_approve_markets_epoch_seconds = ( self.params.min_approve_markets_epoch_seconds ) + self.context.logger.info( + f"min_approve_markets_epoch_seconds={min_approve_markets_epoch_seconds}" + ) approved_markets_count = self.synchronized_data.approved_markets_count - self.context.logger.info(f"approved_markets_count={approved_markets_count}") - self.context.logger.info(f"current_timestamp={current_timestamp}") - self.context.logger.info(f"start_timestamp={start_timestamp}") - self.context.logger.info(f"end_timestamp={end_timestamp}") - self.context.logger.info( - f"largest_creation_timestamp={largest_creation_timestamp}" + + latest_approve_market_timestamp = ( + self.synchronized_data.approved_markets_timestamp ) self.context.logger.info( f"latest_approve_market_execution={latest_approve_market_timestamp}" ) - self.context.logger.info( - f"min_approve_markets_epoch_seconds={min_approve_markets_epoch_seconds}" - ) - self.context.logger.info( - f"latest_opening_timestamps={latest_opening_timestamps}" - ) - self.context.logger.info(f"num_markets_to_approve={num_markets_to_approve}") + # Main logic of the behaviour if ( self.params.max_approved_markets >= 0 and approved_markets_count >= self.params.max_approved_markets @@ -536,20 +556,36 @@ def async_act(self) -> Generator: content = CollectProposedMarketsRound.SKIP_MARKET_APPROVAL_PAYLOAD else: self.context.logger.info("Timeout to approve markets reached.") - approve_market_event_days_offset = ( - self.params.approve_market_event_days_offset + + min_timestamp_to_approve = min( + ( + ts + for ts, value in required_markets_to_approve.items() + if value > 0 + ), + default=0, ) + + # On the market approval server, resolution_time is one day less than openingTimestamp proposed_markets = yield from self._collect_latest_proposed_markets( - start_timestamp, - end_timestamp, + min_timestamp_to_approve - _ONE_DAY, + min_timestamp_to_approve, ) + + proposed_markets_timestamps: Dict[int, int] = defaultdict(int) + + for market_data in proposed_markets["proposed_markets"].values(): + proposed_markets_timestamps[market_data["resolution_time"]] += 1 + self.context.logger.info( - f"Collected proposed markets: {proposed_markets}" + f"proposed_markets_timestamps={proposed_markets_timestamps}" ) content_data = {} content_data.update(latest_open_markets) content_data.update(proposed_markets) - content_data["num_markets_to_approve"] = num_markets_to_approve + content_data["num_markets_to_approve"] = required_markets_to_approve[ + min_timestamp_to_approve + ] content_data["timestamp"] = current_timestamp content = json.dumps(content_data, sort_keys=True) @@ -557,6 +593,7 @@ def async_act(self) -> Generator: sender=sender, content=content, ) + with self.context.benchmark_tool.measure(self.behaviour_id).consensus(): yield from self.send_a2a_transaction(payload) yield from self.wait_until_round_end() @@ -600,7 +637,7 @@ def _collect_latest_proposed_markets( "proposed_markets": { market_id: market_info for market_id, market_info in response_data["proposed_markets"].items() - if from_timestamp <= market_info["resolution_time"] <= to_timestamp + if from_timestamp <= market_info["resolution_time"] < to_timestamp } } @@ -611,12 +648,16 @@ def _collect_latest_proposed_markets( return filtered_markets_data def _collect_latest_open_markets( - self, + self, openingTimestamp_gte: int, openingTimestamp_lte: int ) -> Generator[None, None, Dict[str, Any]]: """Collect FPMM from subgraph.""" creator = self.params.approve_market_creator response = yield from self.get_subgraph_result( - query=FPMM_QUERY.substitute(creator=creator) + query=FPMM_QUERY.substitute( + creator=creator, + openingTimestamp_gte=openingTimestamp_gte, + openingTimestamp_lte=openingTimestamp_lte, + ) ) # TODO Handle retries diff --git a/packages/valory/skills/market_creation_manager_abci/skill.yaml b/packages/valory/skills/market_creation_manager_abci/skill.yaml index 1a51312..64aef38 100644 --- a/packages/valory/skills/market_creation_manager_abci/skill.yaml +++ b/packages/valory/skills/market_creation_manager_abci/skill.yaml @@ -10,7 +10,7 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: .gitignore: bafybeihdfdezgtr3s2lzq5y3oaitfrdy4u4pehionju2bdez35wcjgqx6y __init__.py: bafybeibkyjt4covc4yhd22aw7kav45zozk3exdv344emt3lilln64soaxm - behaviours.py: bafybeieiutqdsgzxlpkwlbkavwf2o6am4cayplmliedllanrwdzkskupvu + behaviours.py: bafybeiaerlo26yxcg55cbverp5lxirowzzr3glqjeobukpvp76tmwmjjyq dialogues.py: bafybeicmaufkl7vdomnfciv7lw4536ssld7x4uemdapuhsyvfpd4ncibza fsm_specification.yaml: bafybeiglegr5e55k3kra4movl5klfoqc6c2lm4cbh3fzicilwq6lhmnmxa handlers.py: bafybeietxjfli2i57kb7heoy772rcq2znusl36gg7jjj5g3pddw7egny3q diff --git a/packages/valory/skills/market_maker_abci/skill.yaml b/packages/valory/skills/market_maker_abci/skill.yaml index 7abe235..2978f0b 100644 --- a/packages/valory/skills/market_maker_abci/skill.yaml +++ b/packages/valory/skills/market_maker_abci/skill.yaml @@ -24,7 +24,7 @@ skills: - valory/abstract_round_abci:0.1.0:bafybeiaqcl7h2famylusiffigwem7tevkcsyocdu5xd42jkmgq6kvowzgq - valory/registration_abci:0.1.0:bafybeic2ynseiak7jpta7jfwuqwyp453b4p7lolr4wihxmpn633uekv5am - valory/reset_pause_abci:0.1.0:bafybeidzajbe3erygeh2xbd6lrjv7nsptznjuzrt24ykgvhgotdeyhfnba -- valory/market_creation_manager_abci:0.1.0:bafybeia6sk3zu5cwlxryqnl6jp73bplri32oyhodofzj4gyxu56l26ueuq +- valory/market_creation_manager_abci:0.1.0:bafybeie67exdnu5phmap5q7xfwgjhfitgyld7bgwvzr2z2nhe7apwwqlaa - valory/termination_abci:0.1.0:bafybeie4zvjfxvdu7qrulmur3chpjz3kpj5m4bjsxvpk4gvj5zbyyayfaa - valory/transaction_settlement_abci:0.1.0:bafybeiaefgqbs7zsn5xe5kdwrujj7ivygkn3ujpw6crnvi3knvxw75qmja behaviours: