From 9d36f84ac6df0af5da604a421c0e2f26450e9737 Mon Sep 17 00:00:00 2001 From: jp Date: Tue, 11 Jan 2022 10:32:22 +0100 Subject: [PATCH] Coindix (#1165) * Add separate view for finbrain for crypto curreny sentiment analysis * Move json with symbols to separate directory, little refactoring, update readme * Add screenshot of finbrain for PolkaDot * Add view for coindix * Adjust coindix view. Add hugo docs * Refactor risk of vault part, add link paramter, rename cols * remove try except, add source * fix wrongly resolved conflicts --- .../cryptocurrency/defi/coindix_model.py | 151 ++++++++++++++++++ .../cryptocurrency/defi/coindix_view.py | 95 +++++++++++ .../cryptocurrency/defi/defi_controller.py | 108 ++++++++++++- .../cryptocurrency/defi/vaults/_index.md | 30 ++++ website/data/menu/main.yml | 29 ++++ 5 files changed, 411 insertions(+), 2 deletions(-) create mode 100644 gamestonk_terminal/cryptocurrency/defi/coindix_model.py create mode 100644 gamestonk_terminal/cryptocurrency/defi/coindix_view.py create mode 100644 website/content/cryptocurrency/defi/vaults/_index.md diff --git a/gamestonk_terminal/cryptocurrency/defi/coindix_model.py b/gamestonk_terminal/cryptocurrency/defi/coindix_model.py new file mode 100644 index 00000000000..530508fbf67 --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/defi/coindix_model.py @@ -0,0 +1,151 @@ +"""Coindix model""" +__docformat__ = "numpy" + +from typing import Optional + +import requests +import pandas as pd + +VAULTS_FILTERS = ["name", "chain", "protocol", "apy", "tvl", "risk", "link"] +CHAINS = [ + "ethereum", + "polygon", + "avalanche", + "bsc", + "terra", + "fantom", + "moonriver", + "celo", + "heco", + "okex", + "cronos", + "arbitrum", + "eth", + "harmony", + "fuse", + "defichain", + "solana", + "optimism", +] +PROTOCOLS = [ + "aave", + "acryptos", + "alpaca", + "anchor", + "autofarm", + "balancer", + "bancor", + "beefy", + "belt", + "compound", + "convex", + "cream", + "curve", + "defichain", + "geist", + "lido", + "liquity", + "mirror", + "pancakeswap", + "raydium", + "sushi", + "tarot", + "traderjoe", + "tulip", + "ubeswap", + "uniswap", + "venus", + "yearn", +] +VAULT_KINDS = [ + "lp", + "single", + "noimploss", + "stable", +] + + +def _risk_mapper(risk_level: int) -> str: + """Helper methods + Parameters + ---------- + risk_level: int + number from range 0-4 represents risk factor for given vault + Returns + ------- + string: + text representation of risk + """ + + mappings = {0: "Non Eligible", 1: "Least", 2: "Low", 3: "Medium", 4: "High"} + return mappings.get(risk_level, "Non Eligible") + + +def _prepare_params(**kwargs) -> dict: + """Helper method, which handles preparation of parameters for requests to coindix api. + + Parameters + ---------- + kwargs: keyword arguments: chain, kind, protocol + + Returns + ------- + dict: + Prepared parameters for request + """ + + params = {"sort": "-apy", "tvl": "1m", "kind": "all"} + mapping = {"chain": CHAINS, "protocol": PROTOCOLS, "kind": VAULT_KINDS} + for key, value in kwargs.items(): + category = mapping.get(key, []) + if value in category: + params.update({key: value}) + return {k: v.lower() for k, v in params.items()} + + +def get_defi_vaults( + chain: Optional[str] = None, + protocol: Optional[str] = None, + kind: Optional[str] = None, +) -> pd.DataFrame: + """Get DeFi Vaults Information. DeFi Vaults are pools of funds with an assigned strategy which main goal is to + maximize returns of its crypto assets. [Source: https://coindix.com/] + + Parameters + ---------- + chain: str + Blockchain - one from list [ + 'ethereum', 'polygon', 'avalanche', 'bsc', 'terra', 'fantom', + 'moonriver', 'celo', 'heco', 'okex', 'cronos', 'arbitrum', 'eth', + 'harmony', 'fuse', 'defichain', 'solana', 'optimism' + ] + protocol: str + DeFi protocol - one from list: [ + 'aave', 'acryptos', 'alpaca', 'anchor', 'autofarm', 'balancer', 'bancor', + 'beefy', 'belt', 'compound', 'convex', 'cream', 'curve', 'defichain', 'geist', + 'lido', 'liquity', 'mirror', 'pancakeswap', 'raydium', 'sushi', 'tarot', 'traderjoe', + 'tulip', 'ubeswap', 'uniswap', 'venus', 'yearn' + ] + kind: str + Kind/type of vault - one from list: ['lp','single','noimploss','stable'] + + Returns + ------- + pd.DataFrame + Top 100 DeFi Vaults for given chain/protocol sorted by APY. + """ + + params = _prepare_params(chain=chain, protocol=protocol, kind=kind) + response = requests.get("https://apiv2.coindix.com/search", params=params) + if not 200 <= response.status_code < 300: + raise Exception(f"Coindix api exception: {response.text}") + + try: + data = response.json()["data"] + if len(data) == 0: + return pd.DataFrame() + df = pd.DataFrame(data)[VAULTS_FILTERS].fillna("NA") + df["risk"] = df["risk"].apply(lambda x: _risk_mapper(x)) + return df + except Exception as e: + raise ValueError(f"Invalid Response: {response.text}") from e diff --git a/gamestonk_terminal/cryptocurrency/defi/coindix_view.py b/gamestonk_terminal/cryptocurrency/defi/coindix_view.py new file mode 100644 index 00000000000..26a2ad87582 --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/defi/coindix_view.py @@ -0,0 +1,95 @@ +"""Coindix view""" +__docformat__ = "numpy" + +import os +from typing import Optional + +from tabulate import tabulate +from gamestonk_terminal.cryptocurrency.defi import coindix_model +from gamestonk_terminal.helper_funcs import export_data, long_number_format +from gamestonk_terminal import feature_flags as gtff + + +def display_defi_vaults( + chain: Optional[str] = None, + protocol: Optional[str] = None, + kind: Optional[str] = None, + top: int = 10, + sortby: str = "apy", + descend: bool = False, + link: bool = False, + export: str = "", +) -> None: + """Display Top DeFi Vaults - pools of funds with an assigned strategy which main goal is to + maximize returns of its crypto assets. [Source: https://coindix.com/] + + Parameters + ---------- + chain: str + Blockchain - one from list [ + 'ethereum', 'polygon', 'avalanche', 'bsc', 'terra', 'fantom', + 'moonriver', 'celo', 'heco', 'okex', 'cronos', 'arbitrum', 'eth', + 'harmony', 'fuse', 'defichain', 'solana', 'optimism' + ] + protocol: str + DeFi protocol - one from list: [ + 'aave', 'acryptos', 'alpaca', 'anchor', 'autofarm', 'balancer', 'bancor', + 'beefy', 'belt', 'compound', 'convex', 'cream', 'curve', 'defichain', 'geist', + 'lido', 'liquity', 'mirror', 'pancakeswap', 'raydium', 'sushi', 'tarot', 'traderjoe', + 'tulip', 'ubeswap', 'uniswap', 'venus', 'yearn' + ] + kind: str + Kind/type of vault - one from list: ['lp','single','noimploss','stable'] + top: int + Number of records to display + sortby: str + Key by which to sort data + descend: bool + Flag to sort data descending + link: bool + Flag to show links + export : str + Export dataframe data to csv,json,xlsx file + """ + + df = coindix_model.get_defi_vaults(chain=chain, protocol=protocol, kind=kind) + df_data = df.copy() + if df.empty: + print( + f"Couldn't find any vaults for " + f"{'' if not chain else 'chain: ' + chain}{'' if not protocol else ', protocol: ' + protocol}" + f"{'' if not kind else ', kind:' + kind}" + ) + + df = df.sort_values(by=sortby, ascending=descend) + df["tvl"] = df["tvl"].apply(lambda x: long_number_format(x)) + df["apy"] = df["apy"].apply( + lambda x: f"{str(round(x * 100, 2))} %" if isinstance(x, (int, float)) else x + ) + df.columns = [x.title() for x in df.columns] + df.rename(columns={"Apy": "APY (%)", "Tvl": "TVL ($)"}, inplace=True) + + if link is True: + df.drop("Link", axis=1, inplace=True) + + if gtff.USE_TABULATE_DF: + print( + tabulate( + df.head(top), + headers=df.columns, + floatfmt=".2f", + showindex=False, + tablefmt="fancy_grid", + stralign="right", + ), + "\n", + ) + else: + print(df.to_string, "\n") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "vaults", + df_data, + ) diff --git a/gamestonk_terminal/cryptocurrency/defi/defi_controller.py b/gamestonk_terminal/cryptocurrency/defi/defi_controller.py index e0cb667429f..8e3f5063990 100644 --- a/gamestonk_terminal/cryptocurrency/defi/defi_controller.py +++ b/gamestonk_terminal/cryptocurrency/defi/defi_controller.py @@ -6,8 +6,8 @@ import argparse from typing import List from prompt_toolkit.completion import NestedCompleter +from gamestonk_terminal.cryptocurrency.defi import graph_model, coindix_model from gamestonk_terminal.parent_classes import BaseController -from gamestonk_terminal.cryptocurrency.defi import graph_model from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.menu import session from gamestonk_terminal.helper_funcs import ( @@ -24,6 +24,7 @@ from gamestonk_terminal.cryptocurrency.defi import ( llama_view, substack_view, graph_view, + coindix_view, ) @@ -43,6 +44,7 @@ class DefiController(BaseController): "pools", "swaps", "stats", + "vaults", ] def __init__(self, queue: List[str] = None): @@ -56,6 +58,10 @@ class DefiController(BaseController): choices["pairs"]["-s"] = {c: {} for c in graph_model.PAIRS_FILTERS} choices["pools"]["-s"] = {c: {} for c in graph_model.POOLS_FILTERS} choices["swaps"]["-s"] = {c: {} for c in graph_model.SWAPS_FILTERS} + choices["vaults"]["-s"] = {c: {} for c in coindix_model.VAULTS_FILTERS} + choices["vaults"]["-k"] = {c: {} for c in coindix_model.VAULT_KINDS} + choices["vaults"]["-c"] = {c: {} for c in coindix_model.CHAINS} + choices["vaults"]["-p"] = {c: {} for c in coindix_model.PROTOCOLS} self.completer = NestedCompleter.from_nested_dict(choices) def print_help(self): @@ -68,9 +74,10 @@ Overview: tvl Total value locked of DeFi protocols newsletter Recent DeFi related newsletters dpi DeFi protocols listed on DefiPulse - funding Funding reates - current or last 30 days average + funding Funding rates - current or last 30 days average borrow DeFi borrow rates - current or last 30 days average lending DeFi ending rates - current or last 30 days average + vaults Top DeFi Vaults on different blockchains [Source: Coindix] Uniswap: tokens Tokens trade-able on Uniswap stats Base statistics about Uniswap @@ -622,3 +629,100 @@ Uniswap: descend=ns_parser.descend, export=ns_parser.export, ) + + def call_vaults(self, other_args: List[str]): + """Process swaps command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="vaults", + description=""" + Display Top DeFi Vaults. + [Source: https://coindix.com/] + """, + ) + + parser.add_argument( + "-c", + "--chain", + dest="chain", + type=str, + help="Blockchain name e.g. ethereum, terra", + default=None, + choices=coindix_model.CHAINS, + required=False, + ) + + parser.add_argument( + "-p", + "--protocol", + dest="protocol", + type=str, + help="DeFi protocol name e.g. aave, uniswap", + default=None, + choices=coindix_model.PROTOCOLS, + required=False, + ) + + parser.add_argument( + "-k", + "--kind", + dest="kind", + type=str, + help="Kind/type of vault e.g. lp, single, noimploss, stable", + default=None, + choices=coindix_model.VAULT_KINDS, + required=False, + ) + + parser.add_argument( + "-t", + "--top", + dest="limit", + type=check_positive, + help="Number of records to display", + default=10, + ) + + parser.add_argument( + "-s", + "--sort", + dest="sortby", + type=str, + help="Sort by given column. Default: timestamp", + default="apy", + choices=coindix_model.VAULTS_FILTERS, + ) + + parser.add_argument( + "--descend", + action="store_false", + help="Flag to sort in descending order (lowest first)", + dest="descend", + default=False, + ) + + parser.add_argument( + "-l", + "--links", + action="store_false", + help="Flag to show vault link", + dest="link", + default=True, + ) + + ns_parser = parse_known_args_and_warn( + parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED + ) + + if ns_parser: + coindix_view.display_defi_vaults( + chain=ns_parser.chain, + kind=ns_parser.kind, + protocol=ns_parser.protocol, + top=ns_parser.limit, + sortby=ns_parser.sortby, + descend=ns_parser.descend, + link=ns_parser.link, + export=ns_parser.export, + ) diff --git a/website/content/cryptocurrency/defi/vaults/_index.md b/website/content/cryptocurrency/defi/vaults/_index.md new file mode 100644 index 00000000000..6c643f5ef85 --- /dev/null +++ b/website/content/cryptocurrency/defi/vaults/_index.md @@ -0,0 +1,30 @@ +``` +usage: vaults [-c {ethereum,polygon,avalanche,bsc,terra,fantom,moonriver,celo,heco,okex,cronos,arbitrum,eth,harmony,fuse,defichain,solana,optimism}] + [-p {aave,acryptos,alpaca,anchor,autofarm,balancer,bancor,beefy,belt,compound,convex,cream,curve,defichain,geist,lido,liquity,mirror,pancakeswap,raydium,sushi,tarot,traderjoe,tulip,ubeswap,uniswap,ven +us,yearn}] + [-k {lp,single,noimploss,stable}] [-t LIMIT] [-s {name,chain,protocol,apy,tvl,risk,link}] [--descend] [-l] [-h] [--export {csv,json,xlsx}] +``` + +Display Top DeFi Vaults. [Source: https://coindix.com/] + +``` +optional arguments: + -c {ethereum,polygon,avalanche,bsc,terra,fantom,moonriver,celo,heco,okex,cronos,arbitrum,eth,harmony,fuse,defichain,solana,optimism}, --chain {ethereum,polygon,avalanche,bsc,terra,fantom,moonriver,celo,heco,okex, +cronos,arbitrum,eth,harmony,fuse,defichain,solana,optimism} + Blockchain name e.g. ethereum, terra (default: None) + -p {aave,acryptos,alpaca,anchor,autofarm,balancer,bancor,beefy,belt,compound,convex,cream,curve,defichain,geist,lido,liquity,mirror,pancakeswap,raydium,sushi,tarot,traderjoe,tulip,ubeswap,uniswap,venus,yearn}, -- +protocol {aave,acryptos,alpaca,anchor,autofarm,balancer,bancor,beefy,belt,compound,convex,cream,curve,defichain,geist,lido,liquity,mirror,pancakeswap,raydium,sushi,tarot,traderjoe,tulip,ubeswap,uniswap,venus,yearn} + DeFi protocol name e.g. aave, uniswap (default: None) + -k {lp,single,noimploss,stable}, --kind {lp,single,noimploss,stable} + Kind/type of vault e.g. lp, single, noimploss, stable (default: None) + -t LIMIT, --top LIMIT + Number of records to display (default: 10) + -s {name,chain,protocol,apy,tvl,risk,link}, --sort {name,chain,protocol,apy,tvl,risk,link} + Sort by given column. Default: timestamp (default: apy) + --descend Flag to sort in descending order (lowest first) (default: False) + -l, --links Flag to show vault link (default: True) + -h, --help show this help message (default: False) + --export {csv,json,xlsx} + Export raw data into csv, json, xlsx (default: ) + +``` \ No newline at end of file diff --git a/website/data/menu/main.yml b/website/data/menu/main.yml index 73ef7382c35..2454564f014 100755 --- a/website/data/menu/main.yml +++ b/website/data/menu/main.yml @@ -862,6 +862,35 @@ main: ref: "/common/technical_analysis/obv" - name: fib ref: "/common/technical_analysis/fib" + - name: defi + ref: "/cryptocurrency/defi" + sub: + - name: llama + ref: "/cryptocurrency/defi/llama" + - name: tvl + ref: "/cryptocurrency/defi/tvl" + - name: newsletter + ref: "/cryptocurrency/defi/newsletter" + - name: dpi + ref: "/cryptocurrency/defi/dpi" + - name: funding + ref: "/cryptocurrency/defi/funding" + - name: borrow + ref: "/cryptocurrency/defi/borrow" + - name: lending + ref: "/cryptocurrency/defi/lending" + - name: vaults + ref: "/cryptocurrency/defi/vaults" + - name: tokens + ref: "/cryptocurrency/defi/tokens" + - name: stats + ref: "/cryptocurrency/defi/stats" + - name: pairs + ref: "/cryptocurrency/defi/pairs" + - name: pools + ref: "/cryptocurrency/defi/pools" + - name: swaps + ref: "/cryptocurrency/defi/swaps" - name: economy ref: "/economy" sub: