diff --git a/README.md b/README.md index f734043553e..5c8a579bed2 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ *** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use. *** https://www.markdownguide.org/basic-syntax/#reference-style-links --> + [![Stargazers][stars-shield]][stars-url] [![Forks][forks-shield]][forks-url] [![Contributors][contributors-shield]][contributors-url] @@ -95,11 +96,11 @@ As a modern Python-based environment, GamestonkTerminal opens access to numerous ## Getting Started -### Docker Installation - *new and improved* +### Docker Installation - _new and improved_ -1. First step is to make sure docker desktop is installed. Install links can be found [here](https://www.docker.com/products/docker-desktop). - To confirm that your docker desktop is downloaded and running, open a command prompt or terminal and enter -`docker info`. If you get the following you are not running the docker desktop: +1. First step is to make sure docker desktop is installed. Install links can be found [here](https://www.docker.com/products/docker-desktop). + To confirm that your docker desktop is downloaded and running, open a command prompt or terminal and enter + `docker info`. If you get the following you are not running the docker desktop: ```text Server: @@ -116,11 +117,11 @@ As a modern Python-based environment, GamestonkTerminal opens access to numerous ``` Upon running this the first time, you should see the various layers downloading (note the random series of letters - numbers will vary). The first time this is run, it will take a few minutes. Subsequent updates will be much faster, + numbers will vary). The first time this is run, it will take a few minutes. Subsequent updates will be much faster, as the changes will be in the MB instead of GB. ![Screen Shot 2021-09-08 at 10 41 08 AM](https://user-images.githubusercontent.com/18151143/132531075-7d7f7e71-4fcb-435c-9bb3-466d7077eba4.png) - Once the download is complete, confirm that the image has been created by doing `docker images`. You should see + Once the download is complete, confirm that the image has been created by doing `docker images`. You should see something similar to ```text @@ -132,9 +133,9 @@ As a modern Python-based environment, GamestonkTerminal opens access to numerous You are now ready to run the terminal. - ```docker run -it --rm ghcr.io/gamestonkterminal/gst-poetry:latest``` + `docker run -it --rm ghcr.io/gamestonkterminal/gst-poetry:latest` - This will open up the terminal in your command prompt or terminal. Note that this has provided now environment file, + This will open up the terminal in your command prompt or terminal. Note that this has provided now environment file, so you will not be able to view plots or use keys at this stage. To read more on adding the environment keys and how to configure your X-server to show plots, hop over to the @@ -147,7 +148,7 @@ If you'd like to see a video recording of the installation process, @JohnnyDanks This project supports Python 3.7, 3.8 and 3.9. Our current recommendation is to use this project with Anaconda's Python distribution - either full -[__Anaconda3 Latest__](https://repo.anaconda.com/archive/) or [__Miniconda3 Latest__](https://repo.anaconda.com/archive/). +[**Anaconda3 Latest**](https://repo.anaconda.com/archive/) or [**Miniconda3 Latest**](https://repo.anaconda.com/archive/). Several features in this project utilize Machine Learning. Machine Learning Python dependencies are optional. If you decided to add Machine Learning features at a later point, you will likely have better user experience with Anaconda's Python distribution. @@ -166,12 +167,12 @@ Anaconda's Python distribution. ```bash conda install -c anaconda git - ```` + ``` 3. Clone the Project - Via HTTPS: `git clone https://github.com/GamestonkTerminal/GamestonkTerminal.git` - - via SSH: `git clone git@github.com:GamestonkTerminal/GamestonkTerminal.git` + - via SSH: `git clone git@github.com:GamestonkTerminal/GamestonkTerminal.git` 4. Navigate into the project's folder @@ -187,7 +188,7 @@ Anaconda's Python distribution. ```bash conda env create -n gst --file build/conda/conda-3-8-env.yaml - ```` + ``` 6. Activate the virtual environment @@ -230,7 +231,7 @@ Anaconda's Python distribution. **NOTE:** When you close the terminal and re-open it, the only command you need to re-call is `conda activate gst` before you call `python terminal.py` again. -**TROUBLESHOOT:** If you are having troubles to install, check our *newest* +**TROUBLESHOOT:** If you are having troubles to install, check our _newest_ troubleshoot page ### Advanced User Install - Machine Learning @@ -245,7 +246,7 @@ the purpose of this project, however, these may be older versions. Hence, it is a virtual python environment prior to installing these. This allows to keep dependencies required by different projects in separate places. -*If you would like to use optional Machine Learning features:* +_If you would like to use optional Machine Learning features:_ - Update your [feature_flags.py](/gamestonk_terminal/feature_flags.py) with: @@ -276,7 +277,7 @@ If this fails due to the fact that you had modified some python files, and there git stash ``` -Then, re-run `poetry install` or `pip install -r requirements.txt` to get any new dependencies. +Then, re-run `poetry install` or `pip install -r requirements.txt` to get any new dependencies. Once installation is finished, you're ready to gamestonk. @@ -311,32 +312,36 @@ These are the ones where a key is necessary: - Whale Alert API: - Ethplorer API: - Cryptopanic API: +- Glassnode API: +- Coinglass API: When these are obtained, don't forget to update [config_terminal.py](/gamestonk_terminal/config_terminal.py). Alternatively, you can also set them to the following environment variables: -| Website | Variables | -| :--- | :--- | -| [Alpha Vantage](https://www.alphavantage.co) | GT_API_KEY_ALPHAVANTAGE | -| [Binance](https://binance.com) | GT_API_BINANCE_KEY
GT_API_BINANCE_SECRET | -| [CoinMarketCap](https://coinmarketcap.com) | GT_CMC_API_KEY
GT_CMC_API_KEY | -| [DEGIRO](https://www.degiro.fr) | GT_DG_USERNAME
GT_DG_PASSWORD
GT_DG_TOTP_SECRET | -| [FRED](https://fred.stlouisfed.org) | GT_API_FRED_KEY | -| [Financial Modeling Prep](https://financialmodelingprep.com) | GT_API_KEY_FINANCIALMODELINGPREP | -| [Finhub](https://finnhub.io) | GT_API_FINNHUB_KEY | -| [News](https://newsapi.org) | GT_API_NEWS_TOKEN | -| [Oanda](https://developer.oanda.com) | GT_OANDA_TOKEN
GT_OANDA_ACCOUNT | -| [Polygon](https://polygon.io) | GT_API_POLYGON_KEY | -| [Quandl](https://www.quandl.com) | GT_API_KEY_QUANDL | -| [Reddit](https://www.reddit.com) | GT_API_REDDIT_CLIENT_ID
GT_API_REDDIT_CLIENT_SECRET
GT_API_REDDIT_USERNAME
GT_API_REDDIT_USER_AGENT
GT_API_REDDIT_PASSWORD| -| [SentimentInvestor](https://sentimentinvestor.com) | GT_API_SENTIMENTINVESTOR_TOKEN
GT_API_SENTIMENTINVESTOR_KEY | -| [Tradier](https://developer.tradier.com) | GT_TRADIER_TOKEN | -| [Twitter](https://developer.twitter.com) | GT_API_TWITTER_KEY
GT_API_TWITTER_SECRET_KEY
GT_API_TWITTER_BEARER_TOKEN | -| [Coinbase](https://docs.pro.coinbase.com/) | GT_API_COINBASE_KEY
GT_API_COINBASE_SECRET
GT_API_COINBASE_PASS_PHRASE | -| [Whale Alert](https://docs.whale-alert.io/) | GT_API_WHALE_ALERT_KEY | -| [Ethplorer](https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API) | GT_API_ETHPLORER_KEY | -| [Cryptopanic](https://cryptopanic.com/developers/api/) | GT_API_CRYPTO_PANIC_KEY | +| Website | Variables | +| :------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| [Alpha Vantage](https://www.alphavantage.co) | GT_API_KEY_ALPHAVANTAGE | +| [Binance](https://binance.com) | GT_API_BINANCE_KEY
GT_API_BINANCE_SECRET | +| [CoinMarketCap](https://coinmarketcap.com) | GT_CMC_API_KEY
GT_CMC_API_KEY | +| [DEGIRO](https://www.degiro.fr) | GT_DG_USERNAME
GT_DG_PASSWORD
GT_DG_TOTP_SECRET | +| [FRED](https://fred.stlouisfed.org) | GT_API_FRED_KEY | +| [Financial Modeling Prep](https://financialmodelingprep.com) | GT_API_KEY_FINANCIALMODELINGPREP | +| [Finhub](https://finnhub.io) | GT_API_FINNHUB_KEY | +| [News](https://newsapi.org) | GT_API_NEWS_TOKEN | +| [Oanda](https://developer.oanda.com) | GT_OANDA_TOKEN
GT_OANDA_ACCOUNT | +| [Polygon](https://polygon.io) | GT_API_POLYGON_KEY | +| [Quandl](https://www.quandl.com) | GT_API_KEY_QUANDL | +| [Reddit](https://www.reddit.com) | GT_API_REDDIT_CLIENT_ID
GT_API_REDDIT_CLIENT_SECRET
GT_API_REDDIT_USERNAME
GT_API_REDDIT_USER_AGENT
GT_API_REDDIT_PASSWORD | +| [SentimentInvestor](https://sentimentinvestor.com) | GT_API_SENTIMENTINVESTOR_TOKEN
GT_API_SENTIMENTINVESTOR_KEY | +| [Tradier](https://developer.tradier.com) | GT_TRADIER_TOKEN | +| [Twitter](https://developer.twitter.com) | GT_API_TWITTER_KEY
GT_API_TWITTER_SECRET_KEY
GT_API_TWITTER_BEARER_TOKEN | +| [Coinbase](https://docs.pro.coinbase.com/) | GT_API_COINBASE_KEY
GT_API_COINBASE_SECRET
GT_API_COINBASE_PASS_PHRASE | +| [Whale Alert](https://docs.whale-alert.io/) | GT_API_WHALE_ALERT_KEY | +| [Ethplorer](https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API) | GT_API_ETHPLORER_KEY | +| [Cryptopanic](https://cryptopanic.com/developers/api/) | GT_API_CRYPTO_PANIC_KEY | +| [Glassnode](https://docs.glassnode.com/basic-api/api-key#how-to-get-an-api-key) | GT_API_GLASSNODE_KEY | +| [Coinglass](https://coinglass.github.io/API-Reference/#api-key) | GT_API_COINGLASS_KEY | Example: @@ -356,7 +361,7 @@ Note that it is not necessary to have a valid Alpha Vantage key to get daily OHL ### Usage -Start by selecting a context that you would like to work with. If you want to research stocks, you would start with +Start by selecting a context that you would like to work with. If you want to research stocks, you would start with ```bash stocks @@ -418,10 +423,11 @@ Example: -To return to the stocks menu to perform more research in a different menu, just use the `q` command. From the stocks +To return to the stocks menu to perform more research in a different menu, just use the `q` command. From the stocks menu, using `q` again will return you to the main menu where you can enter a different context (crypto for example). + ## Contributing There are 3 main ways of contributing to this project. @@ -436,7 +442,7 @@ go up. 1. Fork the Project 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 3. Install the pre-commit hooks by running: - ```pre-commit install```. + `pre-commit install`. Any time you commit a change, linters will be run automatically. On changes, you will have to re-commit. 4. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 5. Push to your Branch (`git push origin feature/AmazingFeature`) @@ -517,6 +523,7 @@ Feel free to share loss porn, memes or any questions at: + [contributors-shield]: https://img.shields.io/github/contributors/GamestonkTerminal/GamestonkTerminal.svg?style=for-the-badge [contributors-url]: https://github.com/GamestonkTerminal/GamestonkTerminal/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/GamestonkTerminal/GamestonkTerminal.svg?style=for-the-badge diff --git a/gamestonk_terminal/cryptocurrency/onchain/ethgasstation_model.py b/gamestonk_terminal/cryptocurrency/onchain/ethgasstation_model.py index 190a7c042e5..341104c3944 100644 --- a/gamestonk_terminal/cryptocurrency/onchain/ethgasstation_model.py +++ b/gamestonk_terminal/cryptocurrency/onchain/ethgasstation_model.py @@ -1,3 +1,4 @@ +"""ETH Gas Station model""" import pandas as pd import requests diff --git a/gamestonk_terminal/cryptocurrency/onchain/ethgasstation_view.py b/gamestonk_terminal/cryptocurrency/onchain/ethgasstation_view.py index 365596f6cf1..f63c76f3a2f 100644 --- a/gamestonk_terminal/cryptocurrency/onchain/ethgasstation_view.py +++ b/gamestonk_terminal/cryptocurrency/onchain/ethgasstation_view.py @@ -1,3 +1,4 @@ +"""ETH Gas Station view""" import os from tabulate import tabulate diff --git a/gamestonk_terminal/cryptocurrency/overview/overview_controller.py b/gamestonk_terminal/cryptocurrency/overview/overview_controller.py index 95e748ec476..9a34f625ef6 100644 --- a/gamestonk_terminal/cryptocurrency/overview/overview_controller.py +++ b/gamestonk_terminal/cryptocurrency/overview/overview_controller.py @@ -3,6 +3,8 @@ __docformat__ = "numpy" # pylint: disable=R0904, C0302, W0622 import argparse +import difflib +from typing import List from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import ( @@ -18,6 +20,8 @@ from gamestonk_terminal.cryptocurrency.overview import ( pycoingecko_view, coinpaprika_view, cryptopanic_view, + withdrawalfees_model, + withdrawalfees_view, ) from gamestonk_terminal.cryptocurrency.overview.coinpaprika_view import CURRENCIES from gamestonk_terminal.cryptocurrency.overview.coinpaprika_model import ( @@ -58,6 +62,9 @@ class Controller: "cpcontracts", "cbpairs", "news", + "wf", + "ewf", + "wfpe", ] def __init__(self): @@ -103,6 +110,10 @@ Coinbase: cbpairs info about available trading pairs on Coinbase CryptoPanic: news recent crypto news from CryptoPanic aggregator +WithdrawalFees: + wf overall withdrawal fees + ewf overall exchange withdrawal fees + wfpe crypto withdrawal fees per exchange """ print(help_text) @@ -153,6 +164,149 @@ CryptoPanic: """Process Quit command - quit the program.""" return True + @try_except + def call_wf(self, other_args: List[str]): + """Process wf command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="wf", + description=""" + Display top coins withdrawal fees + [Source: https://withdrawalfees.com/] + """, + ) + + parser.add_argument( + "-l", + "--limit", + type=int, + help="Limit number of coins to display withdrawal fees. Default 10", + dest="limit", + default=10, + ) + + parser.add_argument( + "--export", + choices=["csv", "json", "xlsx"], + default="", + type=str, + dest="export", + help="Export dataframe data to csv,json,xlsx file", + ) + + ns_parser = parse_known_args_and_warn(parser, other_args) + + if not ns_parser: + return + + withdrawalfees_view.display_overall_withdrawal_fees( + export=ns_parser.export, top=ns_parser.limit + ) + + @try_except + def call_ewf(self, other_args: List[str]): + """Process ewf command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="ewf", + description=""" + Display exchange withdrawal fees + [Source: https://withdrawalfees.com/] + """, + ) + + parser.add_argument( + "--export", + choices=["csv", "json", "xlsx"], + default="", + type=str, + dest="export", + help="Export dataframe data to csv,json,xlsx file", + ) + + ns_parser = parse_known_args_and_warn(parser, other_args) + + if not ns_parser: + return + + withdrawalfees_view.display_overall_exchange_withdrawal_fees( + export=ns_parser.export + ) + + @try_except + def call_wfpe(self, other_args: List[str]): + """Process wfpe command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="wfpe", + description=""" + Coin withdrawal fees per exchange + [Source: https://withdrawalfees.com/] + """, + ) + + parser.add_argument( + "-c", + "--coin", + default="bitcoin", + type=str, + dest="coin", + help="Coin to check withdrawal fees in long format (e.g., bitcoin, ethereum)", + ) + + parser.add_argument( + "--export", + choices=["csv", "json", "xlsx"], + default="", + type=str, + dest="export", + help="Export dataframe data to csv,json,xlsx file", + ) + + if other_args: + if "-" not in other_args[0]: + other_args.insert(0, "-c") + + ns_parser = parse_known_args_and_warn(parser, other_args) + + if not ns_parser: + return + + if ns_parser.coin: + if ns_parser.coin in withdrawalfees_model.POSSIBLE_CRYPTOS: + withdrawalfees_view.display_crypto_withdrawal_fees( + export=ns_parser.export, symbol=ns_parser.coin + ) + else: + print(f"Coin '{ns_parser.coin}' does not exist.") + + similar_cmd = difflib.get_close_matches( + ns_parser.coin, + withdrawalfees_model.POSSIBLE_CRYPTOS, + n=1, + cutoff=0.75, + ) + if similar_cmd: + print(f"Replacing by '{similar_cmd[0]}'") + withdrawalfees_view.display_crypto_withdrawal_fees( + export=ns_parser.export, symbol=similar_cmd[0] + ) + else: + similar_cmd = difflib.get_close_matches( + ns_parser.coin, + withdrawalfees_model.POSSIBLE_CRYPTOS, + n=1, + cutoff=0.5, + ) + if similar_cmd: + print(f"Did you mean '{similar_cmd[0]}'?") + else: + for coin in withdrawalfees_model.POSSIBLE_CRYPTOS: + print(coin) + @try_except def call_cghold(self, other_args): """Process hold command""" @@ -1652,9 +1806,17 @@ def menu(): while True: # Get input command from user if session and gtff.USE_PROMPT_TOOLKIT: - completer = NestedCompleter.from_nested_dict( - {c: None for c in overview_controller.CHOICES} - ) + choices: dict = {c: {} for c in overview_controller.CHOICES} + + choices["wfpe"] = {c: None for c in withdrawalfees_model.POSSIBLE_CRYPTOS} + choices["wfpe"]["-c"] = { + c: None for c in withdrawalfees_model.POSSIBLE_CRYPTOS + } + choices["wfpe"]["--coin"] = { + c: None for c in withdrawalfees_model.POSSIBLE_CRYPTOS + } + + completer = NestedCompleter.from_nested_dict(choices) an_input = session.prompt( f"{get_flair()} (crypto)>(ov)> ", completer=completer, diff --git a/gamestonk_terminal/cryptocurrency/overview/withdrawalfees_model.py b/gamestonk_terminal/cryptocurrency/overview/withdrawalfees_model.py new file mode 100644 index 00000000000..e292a979da8 --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/overview/withdrawalfees_model.py @@ -0,0 +1,253 @@ +"""Withdrawal Fees model""" +from typing import Any, List +import math +import requests +from bs4 import BeautifulSoup +import pandas as pd + +from gamestonk_terminal.helper_funcs import get_user_agent + +POSSIBLE_CRYPTOS = [ + "bitcoin", + "ethereum", + "binance-coin", + "tether", + "solana", + "cardano", + "usd-coin", + "xrp", + "polkadot", + "terra", + "dogecoin", + "avalanche", + "shiba-inu", + "polygon", + "crypto-com-coin", + "binance-usd", + "wrapped-bitcoin", + "litecoin", + "algorand", + "chainlink", + "tron", + "dai", + "bitcoin-cash", + "terrausd", + "uniswap", + "stellar", + "axie-infinity", + "okb", + "cosmos", + "lido-staked-ether", + "vechain", + "ftx-token", + "elrond", + "internet-computer", + "filecoin", + "decentraland", + "ethereum-classic", + "hedera", + "the-sandbox", + "theta-network", + "fantom", + "near", + "magic-internet-money", + "gala", + "bittorrent", + "monero", + "tezos", + "klaytn", + "the-graph", + "leo-token", + "iota", + "helium", + "flow", + "eos", + "radix", + "loopring", + "bitcoin-sv", + "pancakeswap", + "olympus", + "enjin-coin", + "kusama", + "amp", + "aave", + "stacks", + "ecash", + "maker", + "arweave", + "quant", + "thorchain", + "harmony", + "zcash", + "neo", + "bitcoin-cash-abc", + "basic-attention-token", + "waves", + "kadena", + "theta-fuel", + "holo", + "chiliz", + "kucoin-token", + "celsius-network", + "curve-dao-token", + "dash", + "marinade-staked-sol", + "nexo", + "compound", + "celo", + "huobi-token", + "wonderland", + "frax", + "decred", + "trueusd", + "ecomi", + "e-radix", + "spell-token", + "mina-protocol", + "nem", + "qtum", + "sushi", + "synthetix-network-token", +] + + +def get_overall_withdrawal_fees(top: int = 100) -> pd.DataFrame: + """Scrapes top coins withdrawal fees + [Source: https://withdrawalfees.com/] + + Parameters + ---------- + top: int + Number of coins to search, by default n=100, one page has 100 coins, so 1 page is scraped. + Returns + ------- + pandas.DataFrame: + Coin, Lowest, Average, Median, Highest, Exchanges Compared + """ + + COINS_PER_PAGE = 100 + withdrawal_fees_homepage = BeautifulSoup( + requests.get( + "https://withdrawalfees.com/", + headers={"User-Agent": get_user_agent()}, + ).text, + "lxml", + ) + table = withdrawal_fees_homepage.find_all("table") + tickers_html = withdrawal_fees_homepage.find_all("div", {"class": "name"}) + if table is None or tickers_html is None: + return pd.DataFrame() + df = pd.read_html(str(table))[0] + + df["Coin"] = [ticker.text for ticker in tickers_html] + df["Highest"] = df["Highest"].apply( + lambda x: f'{x[:x.index(".")+3]} ({x[x.index(".")+3:]})' + if "." in x and isinstance(x, str) + else x + ) + + num_pages = int(math.ceil(top / COINS_PER_PAGE)) + if num_pages > 1: + for idx in range(2, num_pages + 1): + withdrawal_fees_homepage = BeautifulSoup( + requests.get( + f"https://withdrawalfees.com/coins/page/{idx}", + headers={"User-Agent": get_user_agent()}, + ).text, + "lxml", + ) + table = withdrawal_fees_homepage.find_all("table") + tickers_html = withdrawal_fees_homepage.find_all("div", {"class": "name"}) + if table is not None and tickers_html is not None: + new_df = pd.read_html(str(table))[0] + new_df["Highest"] = new_df["Highest"].apply( + lambda x: f'{x[:x.index(".")+3]} ({x[x.index(".")+3:]})' + if "." in x + else x + ) + new_df["Coin"] = [ticker.text for ticker in tickers_html] + df = df.append(new_df) + df = df.fillna("") + return df + + +def get_overall_exchange_withdrawal_fees() -> pd.DataFrame: + """Scrapes exchange withdrawal fees + [Source: https://withdrawalfees.com/] + + Parameters + ---------- + + Returns + ------- + pandas.DataFrame: + Exchange, Coins, Lowest, Average, Median, Highest + """ + exchange_withdrawal_fees = BeautifulSoup( + requests.get( + "https://withdrawalfees.com/exchanges", + headers={"User-Agent": get_user_agent()}, + ).text, + "lxml", + ) + table = exchange_withdrawal_fees.find_all("table") + if table is None: + return pd.DataFrame() + df = pd.read_html(str(table))[0] + df = df.fillna("") + return df + + +def get_crypto_withdrawal_fees( + symbol: str, +) -> List[Any]: + """Scrapes coin withdrawal fees per exchange + [Source: https://withdrawalfees.com/] + + Parameters + ---------- + symbol: str + Coin to check withdrawal fees. By default bitcoin + Returns + ------- + List: + - str: Overall statistics (exchanges, lowest, average and median) + - pandas.DataFrame: Exchange, Withdrawal Fee, Minimum Withdrawal Amount + """ + crypto_withdrawal_fees = BeautifulSoup( + requests.get( + f"https://withdrawalfees.com/coins/{symbol}", + headers={"User-Agent": get_user_agent()}, + ).text, + "lxml", + ) + if crypto_withdrawal_fees is None: + return ["", pd.DataFrame()] + table = crypto_withdrawal_fees.find_all("table") + html_stats = crypto_withdrawal_fees.find("div", {"class": "details"}) + + if len(table) == 0 or html_stats is None: + return ["", pd.DataFrame()] + df = pd.read_html(str(table))[0] + df["Withdrawal Fee"] = df["Withdrawal Fee"].apply( + lambda x: f'{x[:x.index(".")+3]} ({x[x.index(".")+3:]})' + if "." in x and isinstance(x, str) + else x + ) + df["Minimum Withdrawal Amount"] = df["Minimum Withdrawal Amount"].apply( + lambda x: f'{x[:x.index(".")+3]} ({x[x.index(".")+3:]})' + if isinstance(x, str) and "." in x + else x + ) + df = df.fillna("") + + stats = html_stats.find_all("div", recursive=False) + exchanges = stats[0].find("div", {"class": "value"}).text + lowest = stats[1].find("div", {"class": "value"}).text + average = stats[2].find("div", {"class": "value"}).text + median = stats[3].find("div", {"class": "value"}).text + stats_string = ( + f"{symbol} is available on {exchanges} exchanges with alowest fee of " + ) + stats_string += f"{lowest}, average of {average} and median of {median}" + return [stats_string, df] diff --git a/gamestonk_terminal/cryptocurrency/overview/withdrawalfees_view.py b/gamestonk_terminal/cryptocurrency/overview/withdrawalfees_view.py new file mode 100644 index 00000000000..5ce8a3cd36d --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/overview/withdrawalfees_view.py @@ -0,0 +1,135 @@ +"""Withdrawal Fees view""" +import os + +from tabulate import tabulate +from gamestonk_terminal.cryptocurrency.overview.withdrawalfees_model import ( + get_crypto_withdrawal_fees, + get_overall_exchange_withdrawal_fees, + get_overall_withdrawal_fees, +) +from gamestonk_terminal.helper_funcs import export_data +from gamestonk_terminal import feature_flags as gtff + + +def display_overall_withdrawal_fees(export: str, top: int) -> None: + """Top coins withdrawal fees + [Source: https://withdrawalfees.com/] + + Parameters + ---------- + export : str + Export dataframe data to csv,json,xlsx file + top: int + Number of coins to search + """ + + df_fees = get_overall_withdrawal_fees(top) + + if df_fees.empty: + print("\nError in withdrawal fees request\n") + else: + print("\nWithdrawal fees on exchanges:") + + if gtff.USE_TABULATE_DF: + print( + tabulate( + df_fees.head(top), + headers=df_fees.columns, + floatfmt=".1f", + showindex=False, + tablefmt="fancy_grid", + ), + "\n", + ) + else: + print(df_fees.to_string(index=False), "\n") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "withdrawal_fees", + df_fees, + ) + + +def display_overall_exchange_withdrawal_fees(export: str) -> None: + """Exchange withdrawal fees + [Source: https://withdrawalfees.com/] + + Parameters + ---------- + export : str + Export dataframe data to csv,json,xlsx file + """ + + df_fees = get_overall_exchange_withdrawal_fees() + + if df_fees.empty: + print("\nError in withdrawal fees request\n") + else: + print("\nWithdrawal fees per exchange:") + + if gtff.USE_TABULATE_DF: + print( + tabulate( + df_fees, + headers=df_fees.columns, + floatfmt=".1f", + showindex=False, + tablefmt="fancy_grid", + ), + "\n", + ) + else: + print(df_fees.to_string(index=False), "\n") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "exchange_withdrawal_fees", + df_fees, + ) + + +def display_crypto_withdrawal_fees(export: str, symbol: str) -> None: + """Coin withdrawal fees per exchange + [Source: https://withdrawalfees.com/] + + Parameters + ---------- + export : str + Export dataframe data to csv,json,xlsx file + symbol: str + Coin to check withdrawal fees + """ + + res = get_crypto_withdrawal_fees(symbol) + stats_string = res[0] + df_fees = res[1] + if df_fees.empty: + print("\nError in withdrawal fees request\n") + else: + print(f"\nWithdrawal fees for {symbol}:") + + print(f"\n{stats_string}\n") + + if gtff.USE_TABULATE_DF: + print( + tabulate( + df_fees, + headers=df_fees.columns, + floatfmt=".1f", + showindex=False, + tablefmt="fancy_grid", + ), + "\n", + ) + else: + print(df_fees.to_string(index=False), "\n") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "crypto_withdrawal_fees", + df_fees, + ) diff --git a/website/content/cryptocurrency/overview/ewf/_index.md b/website/content/cryptocurrency/overview/ewf/_index.md new file mode 100644 index 00000000000..468b06c50d2 --- /dev/null +++ b/website/content/cryptocurrency/overview/ewf/_index.md @@ -0,0 +1,13 @@ +``` +usage: ewf [-t TOP] + [--export {png,jpg,pdf,svg}] +``` + +Display exchange withdrawal fees +[Source: https://withdrawalfees.com/] + +``` +optional arguments: + --export {png,jpg,pdf,svg} + Export or figure into png, jpg, pdf, svg (default: ) +``` diff --git a/website/content/cryptocurrency/overview/wf/_index.md b/website/content/cryptocurrency/overview/wf/_index.md new file mode 100644 index 00000000000..8badf367f2f --- /dev/null +++ b/website/content/cryptocurrency/overview/wf/_index.md @@ -0,0 +1,14 @@ +``` +usage: wf [-l LIMIT] + [--export {png,jpg,pdf,svg}] +``` + +Display withdrawal fees of cryptocurrencies ordered by market cap +[Source: https://withdrawalfees.com/] + +``` +optional arguments: + -l N, --limit N show N number of coins (default: 10) + --export {png,jpg,pdf,svg} + Export or figure into png, jpg, pdf, svg (default: ) +``` diff --git a/website/content/cryptocurrency/overview/wfpe/_index.md b/website/content/cryptocurrency/overview/wfpe/_index.md new file mode 100644 index 00000000000..c01413d600e --- /dev/null +++ b/website/content/cryptocurrency/overview/wfpe/_index.md @@ -0,0 +1,14 @@ +``` +usage: wfpe [-c COIN] + [--export {png,jpg,pdf,svg}] +``` + +Display coin withdrawal fees per exchange +[Source: https://withdrawalfees.com/] + +``` +optional arguments: + -c COIN, --coin COIN cryptocurrency to check withdrawal fees in long format (default: bitcoin) + --export {png,jpg,pdf,svg} + Export or figure into png, jpg, pdf, svg (default: ) +``` diff --git a/website/data/menu/main.yml b/website/data/menu/main.yml index ab24d022591..53cc389d0ed 100755 --- a/website/data/menu/main.yml +++ b/website/data/menu/main.yml @@ -704,6 +704,12 @@ main: ref: "/cryptocurrency/overview/cbpairs" - name: news ref: "/cryptocurrency/overview/news" + - name: wf + ref: "/cryptocurrency/overview/wf" + - name: ewf + ref: "/cryptocurrency/overview/ewf" + - name: wfpe + ref: "/cryptocurrency/overview/wfpe" - name: due diligence ref: "/cryptocurrency/due_diligence" sub: