From d030d615e2d11b87ab4d0f77ae4aa5a5c7acd9b5 Mon Sep 17 00:00:00 2001 From: Tarique Anwer Date: Sun, 28 Apr 2024 19:07:35 +0530 Subject: [PATCH 1/5] added Projects eps --- tokenterminal/__version__.py | 2 +- tokenterminal/tokenterminal.py | 69 +++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/tokenterminal/__version__.py b/tokenterminal/__version__.py index 5ba58a7..dbbb70b 100644 --- a/tokenterminal/__version__.py +++ b/tokenterminal/__version__.py @@ -1,7 +1,7 @@ __title__ = 'tokenterminal' __description__ = 'Unofficial Token Terminal API client.' __url__ = 'https://github.com/itzmestar/tokenterminal' -__version__ = '1.0.1' +__version__ = '2.0.0' __build__ = 0x010001 __author__ = 'Tarique Anwer' __author_email__ = 'itzmetariq@gmail.com' diff --git a/tokenterminal/tokenterminal.py b/tokenterminal/tokenterminal.py index e004a33..3cbbbd7 100644 --- a/tokenterminal/tokenterminal.py +++ b/tokenterminal/tokenterminal.py @@ -38,48 +38,65 @@ def _get(self, endpoint, params=None, stream=False): def get_all_projects(self): """ - Returns an overview of latest data for all projects, ranging from metadata such as - launch dates, logos brand colors and Twitter followers to more fundamental metrics - such as Revenue, GMV, TVL and P/S ratios. - The project_id can be used as a Path parameter in subsequent endpoints. - - The data is updated every 10 minutes. - :return: + Use this method to retrieve a list of all projects that are available on the API. """ - path = '/v1/projects' + path = '/v2/projects' response = self._get(path) return response - def get_historical_metrics(self, project_id, granularity='project', interval='daily'): + def get_historical_metrics(self, project_id: str, **kwargs): + """ + Use this method to retrieve historical data for a project. + Fill in the required fields on the modal to the right. + """ + path = f'/v2/projects/{project_id}/metrics' + + response = self._get(path, params=kwargs) + return response + + def get_metric_availability(self, project_id: str): + """ + Use this method to retrieve the metric availability for a project. + :param project_id: project_id + :return: """ - Returns project's historical metrics. The project_id can be fetched from the v1/projects endpoint. - The datetime granularity can be controlled with interval, omitting it will default to daily. + path = f'/v2/projects/{project_id}' - The metric data of a given project can be split into components. In Uniswap's case a component is - the trading pair (e.g. USDC-WETH or DAI-WETH), in Compound's case it's the lending market (e.g. ETH, - USDC or WBTC). - By choosing top10 or component for data_granularity, the composition of a given metric can be examined - more thoroughly. Project's that have top10 or component data can be seen from /v1/projects's - metric_availability field. + response = self._get(path) + return response - By default project-level data is shown, ignoring component-level data altogether. + def get_metric_aggregations(self, project_id: str): + """ + Use this method to retrieve metric aggregation data for one project. + :param project_id: project_id + :return: + """ + path = f'/v2/projects/{project_id}/metric-aggregations' - The data is updated once a day. + response = self._get(path) + return response - :param project_id: Project's id, can be fetched from v1/projects endpoint. - :param granularity: The granularity of data, options are project, top10 or component. - Defaults to project. - :param interval: The interval historical data is given, options are daily or monthly. - Defaults to daily. + def get_financial_statement( + self, + project_id: str, + timestamp_granularity: str = None, + interval: str = None + ): + """ + Use this method to retrieve the financial statement report for a project. + :param interval: can be '1m', '1y', '2y', '3y', '5y' or 'max' + :param timestamp_granularity: can be 'year', 'quarter', 'month' or 'week' + :param project_id: The project ID to retrieve financial statement for. :return: """ - path = f'/v1/projects/{project_id}/metrics' + path = f'/v2/projects/{project_id}/financial-statement' params = { - 'data_granularity': granularity, + 'timestamp_granularity': timestamp_granularity, 'interval': interval } response = self._get(path, params=params) return response + From 2046b090580ccff7061820d9be5f60ac802eb811 Mon Sep 17 00:00:00 2001 From: Tarique Anwer Date: Sun, 28 Apr 2024 19:20:24 +0530 Subject: [PATCH 2/5] added market sectors eps --- tokenterminal/tokenterminal.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tokenterminal/tokenterminal.py b/tokenterminal/tokenterminal.py index 3cbbbd7..31f0c59 100644 --- a/tokenterminal/tokenterminal.py +++ b/tokenterminal/tokenterminal.py @@ -100,3 +100,21 @@ def get_financial_statement( response = self._get(path, params=params) return response + def get_market_sectors(self): + """ + Use this method to retrieve a list of all available market sectors that are available on the API. + :return: + """ + path = f'/v2/market-sectors' + + response = self._get(path) + return response + + def get_market_sector(self, market_sector_id: str): + """ + Use this method to retrieve a market sector metadata and the list of all projects belonging to market sector. + """ + path = f'/v2/market-sectors/{market_sector_id}' + + response = self._get(path) + return response From 66ce7df166e9384b6926a63c392497a88e733e58 Mon Sep 17 00:00:00 2001 From: Tarique Anwer Date: Sun, 28 Apr 2024 19:50:04 +0530 Subject: [PATCH 3/5] added Metrics eps --- tokenterminal/tokenterminal.py | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tokenterminal/tokenterminal.py b/tokenterminal/tokenterminal.py index 31f0c59..100301f 100644 --- a/tokenterminal/tokenterminal.py +++ b/tokenterminal/tokenterminal.py @@ -118,3 +118,57 @@ def get_market_sector(self, market_sector_id: str): response = self._get(path) return response + + def get_all_metrics(self): + """ + Use this method to retrieve a list of all metrics available on the API. + :return: + """ + path = f'/v2/metrics' + + response = self._get(path) + return response + + def get_projects_metric_data(self, metric_id: str, project_ids: list = None, start: str = None, end: str = None): + """ + Use this method to retrieve metric data for multiple projects. + You can get specific metric for all projects by omiting project_ids or using project_ids=all. + :param metric_id: Allows you to select one of the metrics available. The full list is available on the metrics endpoint + :param project_ids: Allows you to select one or more of the projects available for a specific metric. + When including multiple projects, separate each one with a comma. Example: aave,uniswap + :param start: Allows you to select the start date for the query. If no start is selected, + the query defaults to launch date or the first date for which there is data available for the chosen metric(s). + :param end: Allows you to select the end date for the query. If no end is selected, + the query defaults to the latest date for which there is data available for the chosen metric(s). + :return: + """ + path = f'/v2/metrics/{metric_id}' + + params = { + 'project_ids': project_ids, + 'start': start, + 'end': end, + } + + response = self._get(path, params=params) + return response + + def get_projects_metric_aggregations(self, project_ids: list[str], metric_ids: list = None): + """ + Use this method to retrieve metric aggregations for multiple projects. + + You can get all aggregations for all projects by omiting project_ids and metric_ids parameters. + Fetching all projects or metrics you can omit the parameter or using parameter value all. + :param project_ids: + :param metric_ids: + :return: + """ + path = f'/v2/metrics/metric-aggregations' + + params = { + 'project_ids': project_ids, + 'metric_ids': metric_ids + } + + response = self._get(path, params=params) + return response From 99e69f003ec17eed3dc0af0792a34ace76f0caa5 Mon Sep 17 00:00:00 2001 From: Tarique Anwer Date: Sun, 28 Apr 2024 20:20:35 +0530 Subject: [PATCH 4/5] added Datasets eps --- tokenterminal/tokenterminal.py | 269 +++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) diff --git a/tokenterminal/tokenterminal.py b/tokenterminal/tokenterminal.py index 100301f..6ed0b1a 100644 --- a/tokenterminal/tokenterminal.py +++ b/tokenterminal/tokenterminal.py @@ -172,3 +172,272 @@ def get_projects_metric_aggregations(self, project_ids: list[str], metric_ids: l response = self._get(path, params=params) return response + + def get_blockchain_comparison_dataset( + self, + chain_ids: list = None, + order_by: str = None, + order_direction: str = None, + limit: int = None + ): + """ + Use this method to retrieve the Blockchain comparison dataset. + + You can get the whole dataset by omiting all parameters. + :param chain_ids: + :param order_by: + :param order_direction: + :param limit: + :return: + """ + path = f'/v2/datasets/blockchain_comparison' + + params = { + 'chain_ids': chain_ids, + 'order_by': order_by, + 'order_direction': order_direction, + 'limit': limit + } + + response = self._get(path, params=params) + return response + + def get_cohort_analysis_dataset( + self, + project_ids: list = None, + market_sector_ids: list = None, + order_by: str = None, + order_direction: str = None, + limit: int = None + ): + """ + Use this method to retrieve the Cohort analysis dataset. + + You can get the whole dataset by omiting all parameters. + :param project_ids: + :param market_sector_ids: + :param order_by: + :param order_direction: + :param limit: + :return: + """ + path = f'/v2/datasets/cohort_analysis' + + params = { + 'project_ids': project_ids, + 'market_sector_ids': market_sector_ids, + 'order_by': order_by, + 'order_direction': order_direction, + 'limit': limit + } + + response = self._get(path, params=params) + return response + + def get_crypto_screener_dataset( + self, + project_ids: list = None, + market_sector_ids: list = None, + order_by: str = None, + order_direction: str = None, + limit: int = None + ): + """ + Use this method to retrieve the Crypto screener dataset. You can get the whole dataset by omiting all parameters. + + Metrics use the following pattern in the response body: __. + + Metric IDs are the same as the ones used in the /v2/metrics endpoint. + Intervals are 24h, 7d, 30d, 90d, 180d, 365d, max. + Aggregations are trend, change, avg, ath, atl. + :param project_ids: + :param market_sector_ids: + :param order_by: + :param order_direction: + :param limit: + :return: + """ + path = f'/v2/datasets/crypto_screener' + + params = { + 'project_ids': project_ids, + 'market_sector_ids': market_sector_ids, + 'order_by': order_by, + 'order_direction': order_direction, + 'limit': limit + } + + response = self._get(path, params=params) + return response + + def get_insider_transactions_dataset( + self, + project_ids: list = None, + chain_ids: list = None, + order_by: str = None, + order_direction: str = None, + limit: int = None + ): + """ + Use this method to retrieve the Insider transactions dataset. + + You can get the whole dataset by omiting all parameters. + :param project_ids: + :param chain_ids: + :param order_by: + :param order_direction: + :param limit: + :return: + """ + path = f'/v2/datasets/insider_transactions' + + params = { + 'project_ids': project_ids, + 'chain_ids': chain_ids, + 'order_by': order_by, + 'order_direction': order_direction, + 'limit': limit + } + + response = self._get(path, params=params) + return response + + def get_top_tokenholders_dataset( + self, + project_ids: list = None, + chain_ids: list = None, + market_sector_ids: list = None, + order_by: str = None, + order_direction: str = None, + limit: int = None + ): + """ + Use this method to retrieve the Top tokenholders dataset. + + You can get the whole dataset by omiting all parameters. + :param project_ids: + :param chain_ids: + :param market_sector_ids: + :param order_by: + :param order_direction: + :param limit: + :return: + """ + path = f'/v2/datasets/top_tokenholders' + + params = { + 'project_ids': project_ids, + 'chain_ids': chain_ids, + 'market_sector_ids': market_sector_ids, + 'order_by': order_by, + 'order_direction': order_direction, + 'limit': limit + } + + response = self._get(path, params=params) + return response + + def get_project_contracts_dataset( + self, + project_ids: list = None, + chain_ids: list = None, + market_sector_ids: list = None, + order_by: str = None, + order_direction: str = None, + limit: int = None + ): + """ + Use this method to retrieve the Project contracts dataset. + + You can get the whole dataset by omiting all parameters. + :param project_ids: + :param chain_ids: + :param market_sector_ids: + :param order_by: + :param order_direction: + :param limit: + :return: + """ + path = f'/v2/datasets/project_contracts' + + params = { + 'project_ids': project_ids, + 'chain_ids': chain_ids, + 'market_sector_ids': market_sector_ids, + 'order_by': order_by, + 'order_direction': order_direction, + 'limit': limit + } + + response = self._get(path, params=params) + return response + + def get_stablecoins_dataset( + self, + project_ids: list = None, + chain_ids: list = None, + market_sector_ids: list = None, + order_by: str = None, + order_direction: str = None, + limit: int = None + ): + """ + Use this method to retrieve the Stablecoins dataset. + + You can get the whole dataset by omiting all parameters. + :param project_ids: + :param chain_ids: + :param market_sector_ids: + :param order_by: + :param order_direction: + :param limit: + :return: + """ + path = f'/v2/datasets/stablecoins' + + params = { + 'project_ids': project_ids, + 'chain_ids': chain_ids, + 'market_sector_ids': market_sector_ids, + 'order_by': order_by, + 'order_direction': order_direction, + 'limit': limit + } + + response = self._get(path, params=params) + return response + + def get_trending_contracts_dataset( + self, + project_ids: list = None, + chain_ids: list = None, + market_sector_ids: list = None, + order_by: str = None, + order_direction: str = None, + limit: int = None + ): + """ + Use this endpoint to retrieve the Trending contracts dataset. + + You can get the whole dataset by omiting all parameters. + :param project_ids: + :param chain_ids: + :param market_sector_ids: + :param order_by: + :param order_direction: + :param limit: + :return: + """ + path = f'/v2/datasets/trending_contracts' + + params = { + 'project_ids': project_ids, + 'chain_ids': chain_ids, + 'market_sector_ids': market_sector_ids, + 'order_by': order_by, + 'order_direction': order_direction, + 'limit': limit + } + + response = self._get(path, params=params) + return response From 73ae642903ac1775f123858a0936f54a0e09492a Mon Sep 17 00:00:00 2001 From: Tarique Anwer Date: Sun, 28 Apr 2024 20:24:21 +0530 Subject: [PATCH 5/5] readme updated --- README.md | 34 ++++++++++++++++++++++++++++++---- qrcode.png | Bin 0 -> 7158 bytes 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 qrcode.png diff --git a/README.md b/README.md index 29b3914..d24fa32 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,11 @@ # Token Terminal -[![Python 3.6](https://img.shields.io/badge/python-3.6-blue.svg)](https://www.python.org/downloads/release/python-360/) [![Python 3.7](https://img.shields.io/badge/python-3.7-blue.svg)](https://www.python.org/downloads/release/python-370/) [![Python 3.8](https://img.shields.io/badge/python-3.8-blue.svg)](https://www.python.org/downloads/release/python-380/) [![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-390/) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -[![Build](https://github.com/itzmestar/tokenterminal/actions/workflows/python-package.yml/badge.svg?branch=main)](https://github.com/itzmestar/tokenterminal/actions/workflows/python-package.yml) - ------- ### Unofficial [Token Terminal API](https://www.tokenterminal.com/) client in python @@ -50,4 +47,33 @@ for project_info in projects_data: project_metrics = token_terminal.get_historical_metrics('0x') for metrics in project_metrics: print(metrics) -``` \ No newline at end of file + +# To retrieve the metric availability for a project. +reponse = token_terminal.get_metric_availability(project_id=project_id) + +# To retrieve metric aggregation data for one project. +reponse = token_terminal.get_metric_aggregations(project_id=project_id) + +# Get financial statement for a project +reponse = token_terminal.get_financial_statement( + project_id=project_id, + timestamp_granularity='week', + interval='1m' + ) + +# Get list of all available market sectors that are available +reponse = token_terminal.get_market_sectors() + +# Get list of all metrics +reponse = token_terminal.get_all_metrics() + +# To retrieve metric aggregations for multiple projects. +project_ids = ['aave', 'uniswap'] +reponse = token_terminal.get_projects_metric_aggregations(project_ids=project_ids) +``` +------- +#### Donate & Help maintain the library + +[![Paypal](qrcode.png)](https://www.paypal.com/ncp/payment/KLFNJN7SH39EN) + +------- \ No newline at end of file diff --git a/qrcode.png b/qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..e13d221cd65580c9aa09f157212c476fabac8183 GIT binary patch literal 7158 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 TzH+BE@&E?9COYL>4}<;#jjD)x literal 0 HcmV?d00001