Files
OpenBB/openbb_terminal/cryptocurrency/discovery/discovery_controller.py
jose-donato c5d5b511ac Adds crypto fees revenue command (#4067)
* fix: initial commit

* fix: extra stuff

* fix: lint

* fix: ruff

* fix: linting

* fix: ruff

* fix: pylint

* fix: maybe fixed tests maybe not

* fix: pr issues

* fix: tests

* fix: updated test locally

* fix: generate sdk cmds

* fix: added source

* fix: tests maybe

* fix: lint

* fix: tests

* test

* test

* test

---------

Co-authored-by: James Maslek <jmaslek11@gmail.com>
Co-authored-by: DidierRLopes <dro.lopes@campus.fct.unl.pt>
2023-06-13 13:50:17 +00:00

802 lines
26 KiB
Python

"""Cryptocurrency Discovery Controller"""
__docformat__ = "numpy"
# pylint: disable=R0904, C0302, W0622, C0201
import argparse
import logging
from datetime import datetime, timedelta
from typing import List, Optional
from openbb_terminal.core.session.current_user import get_current_user
from openbb_terminal.cryptocurrency.discovery import (
coinmarketcap_model,
coinmarketcap_view,
coinpaprika_model,
coinpaprika_view,
cryptostats_view,
dappradar_model,
dappradar_view,
pycoingecko_model,
pycoingecko_view,
)
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
EXPORT_ONLY_RAW_DATA_ALLOWED,
check_positive,
valid_date,
)
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.rich_config import MenuText, console, get_ordered_list_sources
from openbb_terminal.stocks import stocks_helper
logger = logging.getLogger(__name__)
class DiscoveryController(BaseController):
"""Discovery Controller class"""
CHOICES_COMMANDS = [
"search",
"top",
"trending",
"gainers",
"losers",
"nft_mktp_chains",
"nft_mktp",
"dapps",
"fees",
"dapp_categories",
"dapp_chains",
"dapp_metrics",
"defi_chains",
"tokens",
]
PATH = "/crypto/disc/"
CHOICES_GENERATION = True
def __init__(self, queue: Optional[List[str]] = None):
"""Constructor"""
super().__init__(queue)
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
choices: dict = self.choices_default
ordered_list_sources_top = get_ordered_list_sources(f"{self.PATH}top")
if ordered_list_sources_top and ordered_list_sources_top[0] == "CoinGecko":
choices["top"]["--sort"] = {
c: {}
for c in stocks_helper.format_parse_choices(
pycoingecko_view.COINS_COLUMNS
)
}
else:
choices["top"]["--sort"] = {
c: {}
for c in stocks_helper.format_parse_choices(
coinmarketcap_model.FILTERS
)
}
choices["top"]["-s"] = choices["top"]["--sort"]
self.completer = NestedCompleter.from_nested_dict(choices)
def print_help(self):
"""Print help"""
mt = MenuText("crypto/disc/")
mt.add_cmd("top")
mt.add_cmd("trending")
mt.add_cmd("gainers")
mt.add_cmd("losers")
mt.add_cmd("search")
mt.add_cmd("nft_mktp_chains")
mt.add_cmd("nft_mktp")
mt.add_cmd("dapps")
mt.add_cmd("fees")
mt.add_cmd("dapp_categories")
mt.add_cmd("dapp_chains")
mt.add_cmd("dapp_metrics")
mt.add_cmd("defi_chains")
mt.add_cmd("tokens")
console.print(text=mt.menu_text, menu="Cryptocurrency - Discovery")
@log_start_end(log=logger)
def call_fees(self, other_args: List[str]):
"""Process fees command"""
parser = argparse.ArgumentParser(
prog="fees",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Cryptos where users pay most fees on [Source: CryptoStats]
""",
)
parser.add_argument(
"--mc",
action="store_true",
dest="marketcap",
default=False,
help="Include the market cap rank",
)
parser.add_argument(
"--tvl",
action="store_true",
dest="tvl",
default=False,
help="Include the total value locked",
)
parser.add_argument(
"-d",
"--date",
dest="date",
type=valid_date,
help="Initial date. Default: yesterday",
default=(datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d"),
)
parser.add_argument(
"-s",
"--sort",
dest="sortby",
nargs="+",
help="Sort by given column. Default: One Day Fees",
default="One Day Fees",
choices=["One Day Fees", "Market Cap Rank"],
metavar="SORTBY",
)
parser.add_argument(
"-r",
"--reverse",
action="store_true",
dest="reverse",
default=False,
help=(
"Data is sorted in descending order by default. "
"Reverse flag will sort it in an ascending way. "
"Only works when raw data is displayed."
),
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10
)
if ns_parser:
cryptostats_view.display_fees(
marketcap=ns_parser.marketcap,
tvl=ns_parser.tvl,
date=ns_parser.date,
limit=ns_parser.limit,
sortby=ns_parser.sortby,
ascend=ns_parser.reverse,
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_top(self, other_args):
"""Process top command"""
ordered_list_sources_top = get_ordered_list_sources(f"{self.PATH}top")
if ordered_list_sources_top and ordered_list_sources_top[0] == "CoinGecko":
argument_sort_default = "Market Cap Rank"
else:
argument_sort_default = "CMC_Rank"
parser = argparse.ArgumentParser(
prog="top",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""Display N coins from the data source, if the data source is CoinGecko it
can receive a category as argument (-c decentralized-finance-defi or -c stablecoins)
and will show only the top coins in that category.
can also receive sort arguments (these depend on the source), e.g., --sort Volume [$]
You can sort by {Symbol,Name,Price [$],Market Cap,Market Cap Rank,Volume [$]} with CoinGecko
Number of coins to show: -l 10
""",
)
parser.add_argument(
"-c",
"--category",
default="",
dest="category",
help="Category (e.g., stablecoins). Empty for no category. Only works for 'CoinGecko' source.",
choices=pycoingecko_model.get_categories_keys(),
metavar="CATEGORY",
)
parser.add_argument(
"-l",
"--limit",
default=10,
dest="limit",
help="Limit of records",
type=check_positive,
)
parser.add_argument(
"-s",
"--sort",
dest="sortby",
nargs="+",
help="Sort by given column. Default: Market Cap Rank",
default=stocks_helper.format_parse_choices([argument_sort_default]),
metavar="SORTBY",
)
parser.add_argument(
"-r",
"--reverse",
action="store_true",
dest="reverse",
default=False,
help=(
"Data is sorted in descending order by default. "
"Reverse flag will sort it in an ascending way. "
"Only works when raw data is displayed."
),
)
if other_args and other_args[0][0] != "-":
other_args.insert(0, "-c")
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
if ns_parser.source == "CoinGecko":
pycoingecko_view.display_coins(
sortby=" ".join(ns_parser.sortby),
category=ns_parser.category,
limit=ns_parser.limit,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
ascend=ns_parser.reverse,
)
elif ns_parser.source == "CoinMarketCap":
coinmarketcap_view.display_cmc_top_coins(
limit=ns_parser.limit,
sortby=ns_parser.sortby,
ascend=ns_parser.reverse,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_dapps(self, other_args):
"""Process dapps command"""
parser = argparse.ArgumentParser(
prog="dapps",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows available decentralized applications [Source: https://dappradar.com/]
Accepts --chain argument to filter by blockchain
--page argument to show a specific page. Default: 1
--limit argument to limit the number of records per page. Default: 15
""",
)
parser.add_argument(
"-c",
"--chain",
dest="chain",
help="Filter by blockchain",
metavar="CHAIN",
)
parser.add_argument(
"-p",
"--page",
dest="page",
type=check_positive,
help="Page number",
default=1,
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
type=check_positive,
help="Number of records to display per page",
default=15,
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
dappradar_view.display_dapps(
chain=ns_parser.chain,
page=ns_parser.page,
resultPerPage=ns_parser.limit,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_dapp_categories(self, other_args):
"""Process dapp_categories command"""
parser = argparse.ArgumentParser(
prog="dapp_categories",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows available dapp categories [Source: https://dappradar.com/]
""",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
dappradar_view.display_dapp_categories(
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_dapp_chains(self, other_args):
"""Process dapp_chains command"""
parser = argparse.ArgumentParser(
prog="dapp_chains",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows available dapp chains [Source: https://dappradar.com/]
""",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
dappradar_view.display_dapp_chains(
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_dapp_metrics(self, other_args):
"""Process dapp_metrics command"""
parser = argparse.ArgumentParser(
prog="dapp_metrics",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows dapp metrics [Source: https://dappradar.com/]
Accepts --dappId argument to specify the dapp
--chain argument to filter by blockchain for multi-chain dapps
--time_range argument to specify the time range. Default: 7d (can be 24h, 7d, 30d)
""",
)
parser.add_argument(
"-d",
"--dappId",
dest="dappId",
help="Dapp ID",
metavar="DAPP_ID",
)
parser.add_argument(
"-c",
"--chain",
dest="chain",
help="Filter by blockchain",
metavar="CHAIN",
)
parser.add_argument(
"-t",
"--time_range",
dest="time_range",
help="Time range",
metavar="TIME_RANGE",
choices=["24h", "7d", "30d"],
default="7d",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
dappradar_view.display_dapp_metrics(
dappId=ns_parser.dappId,
chain=ns_parser.chain,
time_range=ns_parser.time_range,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_gainers(self, other_args):
"""Process gainers command"""
parser = argparse.ArgumentParser(
prog="gainers",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows Largest Gainers - coins which gain the most in given period.
You can use parameter --interval to set which timeframe are you interested in: {14d,1h,1y,200d,24h,30d,7d}
You can look on only N number of records with --limit,
You can sort by {Symbol,Name,Price [$],Market Cap,Market Cap Rank,Volume [$]} with --sort.
""",
)
parser.add_argument(
"-i",
"--interval",
dest="interval",
type=str,
help="time period, one from {14d,1h,1y,200d,24h,30d,7d}",
default="1h",
choices=pycoingecko_model.API_PERIODS,
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
type=check_positive,
help="Number of records to display",
default=15,
)
parser.add_argument(
"-s",
"--sort",
dest="sortby",
nargs="+",
help="Sort by given column. Default: Market Cap Rank",
default=["market_cap"],
choices=stocks_helper.format_parse_choices(
pycoingecko_model.GAINERS_LOSERS_COLUMNS
),
metavar="SORTBY",
)
parser.add_argument(
"-r",
"--reverse",
action="store_true",
dest="reverse",
default=False,
help=(
"Data is sorted in descending order by default. "
"Reverse flag will sort it in an ascending way. "
"Only works when raw data is displayed."
),
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
pycoingecko_view.display_gainers(
interval=ns_parser.interval,
limit=ns_parser.limit,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
sortby=" ".join(ns_parser.sortby),
ascend=ns_parser.reverse,
)
@log_start_end(log=logger)
def call_losers(self, other_args):
"""Process losers command"""
parser = argparse.ArgumentParser(
prog="losers",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows Largest Losers - coins which price dropped the most in given period
You can use parameter --interval to set which timeframe are you interested in: {14d,1h,1y,200d,24h,30d,7d}
You can look on only N number of records with --limit,
You can sort by {Symbol,Name,Price [$],Market Cap,Market Cap Rank,Volume [$]} with --sort.
""",
)
parser.add_argument(
"-i",
"--interval",
dest="interval",
type=str,
help="time period, one from {14d,1h,1y,200d,24h,30d,7d}",
default="1h",
choices=pycoingecko_model.API_PERIODS,
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
type=check_positive,
help="Number of records to display",
default=15,
)
parser.add_argument(
"-s",
"--sort",
dest="sortby",
nargs="+",
help="Sort by given column. Default: Market Cap Rank",
default=["Market Cap"],
choices=stocks_helper.format_parse_choices(
pycoingecko_model.GAINERS_LOSERS_COLUMNS
),
metavar="SORTBY",
)
parser.add_argument(
"-r",
"--reverse",
action="store_true",
dest="reverse",
default=False,
help=(
"Data is sorted in descending order by default. "
"Reverse flag will sort it in an ascending way. "
"Only works when raw data is displayed."
),
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
pycoingecko_view.display_losers(
interval=ns_parser.interval,
limit=ns_parser.limit,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
sortby=" ".join(ns_parser.sortby),
ascend=ns_parser.reverse,
)
@log_start_end(log=logger)
def call_trending(self, other_args):
"""Process trending command"""
parser = argparse.ArgumentParser(
prog="trending",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""Discover trending coins (Top-7) on CoinGecko in the last 24 hours
""",
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
EXPORT_ONLY_RAW_DATA_ALLOWED,
)
if ns_parser:
pycoingecko_view.display_trending(
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_search(self, other_args):
"""Process search command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="search",
description="""Search over CoinPaprika API
You can display only N number of results with --limit parameter.
You can sort data by id, name , category --sort parameter and also with --reverse flag to sort descending.
To choose category in which you are searching for use --cat/-c parameter. Available categories:
currencies|exchanges|icos|people|tags|all
Displays:
id, name, category""",
)
parser.add_argument(
"-q",
"--query",
help="phrase for search",
dest="query",
nargs="+",
type=str,
required="-h" not in other_args,
)
parser.add_argument(
"-c",
"--category",
help="Categories to search: currencies|exchanges|icos|people|tags|all. Default: all",
dest="category",
default="all",
type=str,
choices=coinpaprika_model.CATEGORIES,
)
parser.add_argument(
"-l",
"--limit",
default=10,
dest="limit",
help="Limit of records",
type=check_positive,
)
parser.add_argument(
"-s",
"--sort",
dest="sortby",
type=str,
help="Sort by given column. Default: id",
default="id",
choices=coinpaprika_model.FILTERS,
)
parser.add_argument(
"-r",
"--reverse",
action="store_true",
dest="reverse",
default=False,
help=(
"Data is sorted in descending order by default. "
"Reverse flag will sort it in an ascending way. "
"Only works when raw data is displayed."
),
)
if other_args and other_args[0][0] != "-":
other_args.insert(0, "-q")
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
coinpaprika_view.display_search_results(
limit=ns_parser.limit,
sortby=ns_parser.sortby,
ascend=ns_parser.reverse,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
query=" ".join(ns_parser.query),
category=ns_parser.category,
)
@log_start_end(log=logger)
def call_nft_mktp_chains(self, other_args):
"""Process nft_mktp_chains command"""
parser = argparse.ArgumentParser(
prog="nft_mktp_chains",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows NFT marketplace chains [Source: https://dappradar.com/]
""",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
dappradar_view.display_nft_marketplace_chains(
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_defi_chains(self, other_args):
"""Process defi_chains command"""
parser = argparse.ArgumentParser(
prog="defi_chains",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows DeFi chains [Source: https://dappradar.com/]
""",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
dappradar_view.display_defi_chains(
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_nft_mktp(self, other_args):
"""Process nft_mktp command"""
parser = argparse.ArgumentParser(
prog="nft_mktp",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows NFT marketplaces [Source: https://dappradar.com/]
Accepts --chain to filter by blockchain
--sortby {name, avgPrice, volume, traders...} to sort by column
--order {asc, desc} to sort ascending or descending
--limit to limit number of records
""",
)
parser.add_argument(
"-c",
"--chain",
dest="chain",
help="Name of the blockchain to filter by.",
)
parser.add_argument(
"-s",
"--sortby",
dest="sortby",
nargs="+",
help="Sort by given column.",
choices=stocks_helper.format_parse_choices(dappradar_model.NFT_COLUMNS),
metavar="SORTBY",
)
parser.add_argument(
"-o",
"--order",
dest="order",
help="Order of sorting. Default: desc",
default="desc",
)
parser.add_argument(
"-l",
"--limit",
dest="limit",
type=check_positive,
help="Number of records to display",
default=10,
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
dappradar_view.display_nft_marketplaces(
sortby=ns_parser.sortby,
order=ns_parser.order,
chain=ns_parser.chain,
limit=ns_parser.limit,
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)
@log_start_end(log=logger)
def call_tokens(self, other_args):
"""Process tokens command"""
parser = argparse.ArgumentParser(
prog="tokens",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="""
Shows chains that support tokens [Source: https://dappradar.com/]
""",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
dappradar_view.display_token_chains(
export=ns_parser.export,
sheet_name=" ".join(ns_parser.sheet_name)
if ns_parser.sheet_name
else None,
)