Files
OpenBB/openbb_terminal/cryptocurrency/pycoingecko_helpers.py
jmaslek b71abcfbf4 Updating some names (#1575)
* quick econ fix

* black

* keys and feature flags

* terminal name 👀

* some more replacements

* some more replacements

* edit pyproject

* gst -> openbb

* add example portfolios back to git

* Update api from gst

* sorry.  skipping some tests

* another round of names

* another round of test edits

* Missed some .gst refs and update timezone

* water mark stuff

* Fixing Names in terminal.spec and name of GTFF_DEFAULTS to OBBFF_DEFAULTS

* fix more GST to OpenBB Terminal

* Logging : merge conflicts with main

* Revert wrong files

Co-authored-by: Andrew <andrew.kenreich@gmail.com>
Co-authored-by: DidierRLopes <dro.lopes@campus.fct.unl.pt>
Co-authored-by: Chavithra PARANA <chavithra@gmail.com>
2022-03-29 09:46:26 -04:00

283 lines
7.5 KiB
Python

"""CoinGecko helpers"""
__docformat__ = "numpy"
import json
import math
import datetime as dt
from datetime import timezone
from typing import Sequence, Optional, Any, Dict, Tuple, Union, List
import textwrap
import logging
import requests
from bs4 import BeautifulSoup
import pandas as pd
from dateutil import parser
from requests.adapters import HTTPAdapter, RetryError
from urllib3.util.retry import Retry
from openbb_terminal.helper_funcs import get_user_agent
from openbb_terminal.rich_config import console
logger = logging.getLogger(__name__)
GECKO_BASE_URL = "https://www.coingecko.com"
DENOMINATION = ("usd", "btc", "eth")
def millify(n: Union[float, int]) -> str:
millnames = ["", "K", "M", "B", "T"]
n = float(n)
millidx = max(
0,
min(
len(millnames) - 1, int(math.floor(0 if n == 0 else math.log10(abs(n)) / 3))
),
)
return f"{n / 10 ** (3 * millidx):.0f}{millnames[millidx]}"
def calc_change(current: Union[float, int], previous: Union[float, int]):
"""Calculates change between two different values"""
if current == previous:
return 0
try:
return ((current - previous) / previous) * 100.0
except ZeroDivisionError:
return float("inf")
def get_btc_price() -> float:
"""Get BTC/USD price from CoinGecko API
Returns
-------
str
latest bitcoin price in usd.
"""
req = requests.get(
"https://api.coingecko.com/api/v3/simple/"
"price?ids=bitcoin&vs_currencies=usd&include_market_cap"
"=false&include_24hr_vol"
"=false&include_24hr_change=false&include_last_updated_at=false"
)
return req.json()["bitcoin"]["usd"]
def _retry_session(
url: str, retries: int = 3, backoff_factor: float = 1.0
) -> requests.Session:
"""Helper methods that retries to make request to CoinGecko
Parameters
----------
url: str
Url to mount a session
retries: int
How many retries
backoff_factor: float
Backoff schema - time periods between retry
Returns
-------
requests.Session
Mounted session
"""
session = requests.Session()
retry = Retry(
total=retries,
read=retries,
connect=retries,
status_forcelist=[500, 502, 503, 504],
backoff_factor=backoff_factor,
)
adapter = HTTPAdapter(max_retries=retry)
session.mount(url, adapter)
return session
def scrape_gecko_data(url: str) -> BeautifulSoup:
"""Helper method that scrape Coin Gecko site.
Parameters
----------
url : str
coin gecko url to scrape e.g: "https://www.coingecko.com/en/discover"
Returns
-------
BeautifulSoup object
"""
headers = {"User-Agent": get_user_agent()}
session = _retry_session("https://www.coingecko.com")
try:
req = session.get(url, headers=headers, timeout=5)
except Exception as error:
logger.exception(error)
console.print(error)
raise RetryError(
"Connection error. Couldn't connect to CoinGecko and scrape the data. "
"Please visit CoinGecko site, and check if it's not under maintenance"
) from error
if req.status_code >= 400:
raise Exception(
f"Couldn't connect to {url}. Status code: {req.status_code}. Reason: {req.reason}"
)
return BeautifulSoup(req.text, features="lxml")
def replace_underscores_to_newlines(cols: list, line: int = 13) -> list:
"""Helper method that replace underscores to white space and breaks it to new line
Parameters
----------
cols
- list of columns names
line
- line length
Returns
-------
list of column names with replaced underscores
"""
return [
textwrap.fill(c.replace("_", " "), line, break_long_words=False)
for c in list(cols)
]
def find_discord(item: Optional[List[Any]]) -> Union[str, Any]:
if isinstance(item, list) and len(item) > 0:
discord = [chat for chat in item if "discord" in chat]
if len(discord) > 0:
return discord[0]
return None
def join_list_elements(elem):
if isinstance(elem, dict):
return ", ".join(k for k, v in elem.items())
if isinstance(elem, list):
return ", ".join(k for k in elem)
return None
def filter_list(lst: Optional[List[Any]]) -> Optional[List[Any]]:
if isinstance(lst, list) and len(lst) > 0:
return [i for i in lst if i != ""]
return lst
def calculate_time_delta(date: dt.datetime) -> int:
now = dt.datetime.now(timezone.utc)
if not isinstance(date, dt.datetime):
date = parser.parse(date)
return (now - date).days
def get_eth_addresses_for_cg_coins(file) -> pd.DataFrame: # pragma: no cover
with open(file, encoding="utf8") as f:
data = json.load(f)
df = pd.DataFrame(data)
df["ethereum"] = df["platforms"].apply(
lambda x: x.get("ethereum") if "ethereum" in x else None
)
return df
def clean_question_marks(dct: dict) -> None:
if isinstance(dct, dict):
for k, v in dct.items():
if v == "?":
dct[k] = None
def replace_qm(df: pd.DataFrame) -> pd.DataFrame:
df.replace({"?": None, " ?": None}, inplace=True)
return df
def get_url(url: str, elem: BeautifulSoup): # pragma: no cover
return url + elem.find("a")["href"]
def clean_row(row: BeautifulSoup) -> list:
"""Helper method that cleans whitespaces and newlines in text returned from BeautifulSoup
Parameters
----------
row
text returned from BeautifulSoup find method
Returns
-------
list of elements
"""
return [r for r in row.text.strip().split("\n") if r not in ["", " "]]
def convert(word: str) -> str:
return "".join(x.capitalize() or "_" for x in word.split("_") if word.isalpha())
def collateral_auditors_parse(
args: Any,
) -> Tuple[Any, Any]: # pragma: no cover
try:
if args and args[0] == "N/A":
collateral = args[1:]
auditors = []
else:
n_elem = int(args[0])
auditors = args[1 : n_elem + 1] # noqa: E203
collateral = args[n_elem + 1 :] # noqa: E203
return auditors, collateral
except ValueError:
return [], []
def swap_columns(df: pd.DataFrame) -> pd.DataFrame:
cols = list(df.columns)
cols = [cols[-1]] + cols[:-1]
df = df[cols]
return df
def changes_parser(changes: list) -> list:
if isinstance(changes, list) and len(changes) < 3:
for _ in range(3 - len(changes)):
changes.append(None)
else:
changes = [None for _ in range(3)]
return changes
def remove_keys(entries: tuple, the_dict: Dict[Any, Any]) -> None:
for key in entries:
if key in the_dict:
del the_dict[key]
def rename_columns_in_dct(dct: dict, mapper: dict) -> dict:
return {mapper.get(k, v): v for k, v in dct.items()}
def create_dictionary_with_prefixes(
columns: Sequence[Any], dct: Dict[Any, Any], constrains: Optional[Tuple] = None
): # type: ignore
results = {}
for column in columns:
ath_data = dct.get(column, {})
for element in ath_data: # type: ignore
if constrains: # type: ignore
if element in constrains: # type: ignore
results[f"{column}_" + element] = ath_data.get(element) # type: ignore
else:
results[f"{column}_" + element] = ath_data.get(element) # type: ignore
return results