From a04e833e7e6e098a9ae40f7d115d15dc32edb186 Mon Sep 17 00:00:00 2001 From: jose-donato <43375532+jose-donato@users.noreply.github.com> Date: Wed, 27 Oct 2021 11:11:49 +0100 Subject: [PATCH] Open crypto insterest by exchange [Source: bybt.com] (#868) * fix: small issues * fix: repeated code * feat: added new oi feature; needed to refactor a bit * fix: test coingecko fixed * fix: pylint issue * fix: added entry to main.yml * fix: added image to hugo markdown --- gamestonk_terminal/config_terminal.py | 3 + .../cryptocurrency/crypto_controller.py | 5 +- .../cryptocurrency/cryptocurrency_helpers.py | 19 +- .../due_diligence/bybt_model.py | 45 ++++ .../cryptocurrency/due_diligence/bybt_view.py | 63 ++++++ .../due_diligence/dd_controller.py | 76 ++++++- .../due_diligence/pycoingecko_model.py | 17 +- .../cryptocurrency/onchain/glassnode_view.py | 7 - .../technical_analysis/ta_controller.py | 1 - .../test_coinbase/test_trading_pairs.yaml | 208 ++++++++++++++++++ .../test_coingecko_crypto.py | 2 +- .../cryptocurrency/due_diligence/oi/_index.md | 25 +++ website/data/menu/main.yml | 2 + 13 files changed, 436 insertions(+), 37 deletions(-) create mode 100644 gamestonk_terminal/cryptocurrency/due_diligence/bybt_model.py create mode 100644 gamestonk_terminal/cryptocurrency/due_diligence/bybt_view.py create mode 100644 tests/cassettes/test_cryptocurrency/test_coinbase/test_trading_pairs.yaml create mode 100644 website/content/cryptocurrency/due_diligence/oi/_index.md diff --git a/gamestonk_terminal/config_terminal.py b/gamestonk_terminal/config_terminal.py index 6b7d8f4cca0..0a88581800e 100644 --- a/gamestonk_terminal/config_terminal.py +++ b/gamestonk_terminal/config_terminal.py @@ -103,5 +103,8 @@ API_WHALE_ALERT_KEY = os.getenv("GT_API_WHALE_ALERT_KEY") or "REPLACE_ME" # https://docs.glassnode.com/basic-api/api-key#how-to-get-an-api-key API_GLASSNODE_KEY = os.getenv("GT_API_GLASSNODE_KEY") or "REPLACE_ME" +# https://bybt.gitbook.io/bybt/api-key +API_BYBT_KEY = os.getenv("GT_API_BYBT_KEY") or "REPLACE_ME" + # https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API API_ETHPLORER_KEY = os.getenv("GT_API_ETHPLORER_KEY") or "freekey" diff --git a/gamestonk_terminal/cryptocurrency/crypto_controller.py b/gamestonk_terminal/cryptocurrency/crypto_controller.py index 248529ad341..50f8b195063 100644 --- a/gamestonk_terminal/cryptocurrency/crypto_controller.py +++ b/gamestonk_terminal/cryptocurrency/crypto_controller.py @@ -89,6 +89,7 @@ class CryptoController: self.crypto_parser = argparse.ArgumentParser(add_help=False, prog="crypto") self.crypto_parser.add_argument("cmd", choices=self.CHOICES) + self.symbol = "" self.current_coin = "" self.current_df = pd.DataFrame() self.current_currency = "" @@ -226,7 +227,7 @@ What do you want to do? if arg in other_args: other_args.remove(arg) - self.current_coin, self.source = load( + self.current_coin, self.source, self.symbol = load( coin=ns_parser.coin, source=ns_parser.source ) @@ -725,7 +726,7 @@ What do you want to do? def call_dd(self, _): """Process dd command""" if self.current_coin: - dd = dd_controller.menu(self.current_coin, self.source) + dd = dd_controller.menu(self.current_coin, self.source, self.symbol) if dd is False: self.print_help() else: diff --git a/gamestonk_terminal/cryptocurrency/cryptocurrency_helpers.py b/gamestonk_terminal/cryptocurrency/cryptocurrency_helpers.py index 6b5451b99c1..8b8bc6600c2 100644 --- a/gamestonk_terminal/cryptocurrency/cryptocurrency_helpers.py +++ b/gamestonk_terminal/cryptocurrency/cryptocurrency_helpers.py @@ -144,7 +144,7 @@ def _create_closest_match_df( def load( coin: str, source: str, -) -> Tuple[Union[Optional[str], pycoingecko_model.Coin], Any]: +) -> Tuple[Union[Optional[str], pycoingecko_model.Coin], Optional[Any], Optional[Any]]: """Load cryptocurrency from given source. Available sources are: CoinGecko, CoinPaprika, Coinbase and Binance. Loading coin from Binance and CoinPaprika means validation if given coins exists in chosen source, @@ -161,16 +161,17 @@ def load( Returns ------- - Tuple[Union[str, pycoingecko_model.Coin], Any] + Tuple[Union[str, pycoingecko_model.Coin], str, str] - str or Coin object for provided coin - str with source of the loaded data. CoinGecko, CoinPaprika, or Binance + - str with """ current_coin = "" # type: Optional[Any] if source == "cg": - current_coin = pycoingecko_model.Coin(coin) - return current_coin, source + coingecko = pycoingecko_model.Coin(coin) + return coingecko, source, coingecko.symbol if source == "bin": parsed_coin = coin.upper() @@ -179,13 +180,13 @@ def load( print(f"Coin found : {current_coin}\n") else: print(f"Couldn't find coin with symbol {current_coin}\n") - return current_coin, source + return current_coin, source, parsed_coin if source == "cp": paprika_coins = get_list_of_coins() paprika_coins_dict = dict(zip(paprika_coins.id, paprika_coins.symbol)) - current_coin, _ = coinpaprika_model.validate_coin(coin, paprika_coins_dict) - return current_coin, source + current_coin, symbol = coinpaprika_model.validate_coin(coin, paprika_coins_dict) + return current_coin, source, symbol if source == "cb": coinbase_coin = coin.upper() @@ -196,9 +197,9 @@ def load( print(f"Coin found : {current_coin}\n") else: print(f"Couldn't find coin with symbol {current_coin}\n") - return current_coin, source + return current_coin, source, coin - return current_coin, None + return current_coin, None, None # TODO: Find better algorithm then difflib.get_close_matches to find most similar coins diff --git a/gamestonk_terminal/cryptocurrency/due_diligence/bybt_model.py b/gamestonk_terminal/cryptocurrency/due_diligence/bybt_model.py new file mode 100644 index 00000000000..340d1a03c47 --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/due_diligence/bybt_model.py @@ -0,0 +1,45 @@ +import json +import datetime as dt +import pandas as pd +import requests +from gamestonk_terminal import config_terminal as cfg + + +def get_open_interest_per_exchange(symbol: str, interval: int) -> pd.DataFrame: + """Returns open interest by exchange for a certain symbol + [Source: https://bybt.gitbook.io] + + Parameters + ---------- + symbol : str + Crypto Symbol to search open interest futures (e.g., BTC) + interval : int + Interval frequency (e.g., 0) + + Returns + ------- + pd.DataFrame + open interest by exchange and price + """ + + url = f"http://open-api.bybt.com/api/pro/v1/futures/openInterest/chart?&symbol={symbol.upper()}&interval={interval}" + + headers = {"bybtSecret": cfg.API_BYBT_KEY} + + response = requests.request("GET", url, headers=headers) + + if response.status_code == 200: + res_json = json.loads(response.text) + if res_json["msg"] == "success": + data = res_json["data"] + time = data["dateList"] + time_new = [] + for elem in time: + time_actual = dt.datetime.utcfromtimestamp(elem / 1000) + time_new.append(time_actual) + df = pd.DataFrame( + data={"date": time_new, "price": data["priceList"], **data["dataMap"]} + ) + df = df.set_index("date") + return df + return pd.DataFrame() diff --git a/gamestonk_terminal/cryptocurrency/due_diligence/bybt_view.py b/gamestonk_terminal/cryptocurrency/due_diligence/bybt_view.py new file mode 100644 index 00000000000..90949d707c4 --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/due_diligence/bybt_view.py @@ -0,0 +1,63 @@ +import os +from matplotlib import pyplot as plt, dates as mdates +import pandas as pd + +from gamestonk_terminal.cryptocurrency.due_diligence.bybt_model import ( + get_open_interest_per_exchange, +) +from gamestonk_terminal.helper_funcs import export_data, plot_autoscale +from gamestonk_terminal import config_plot as cfgPlot + + +def display_open_interest(symbol: str, interval: int, export: str) -> None: + """Displays open interest by exchange for a certain cryptocurrency + [Source: https://bybt.gitbook.io] + + Parameters + ---------- + symbol : str + Crypto symbol to search open interest (e.g., BTC) + interval : int + Interval frequency (e.g., 0) + export : str + Export dataframe data to csv,json,xlsx file""" + df = get_open_interest_per_exchange(symbol, interval) + if df.empty: + print("Error in glassnode request") + else: + plot_data(df, symbol) + print("") + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "oi", + df, + ) + + +def plot_data(df: pd.DataFrame, symbol: str): + df_price = df[["price"]].copy() + + df_without_price = df.drop("price", 1) + _, ax1 = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI) + plt.plot(df_without_price.index, df_without_price / 1e9) + plt.legend(df_without_price.columns) + ax2 = ax1.twinx() + plt.grid() + plt.plot(df_price.index, df_price, lw=3, c="k") + plt.legend([f"{symbol} price"], loc=1) + + ax1.set_ylabel( + "Estimated notional value of all open futures positions [$ billions]" + ) + ax2.set_ylabel(f"{symbol} Price [$]") + + plt.xlim([df_price.index[0], df_price.index[-1]]) + plt.title(f"Exchange {symbol} Futures Open Interest") + + plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d")) + plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=1)) + plt.gcf().autofmt_xdate() + + plt.show() diff --git a/gamestonk_terminal/cryptocurrency/due_diligence/dd_controller.py b/gamestonk_terminal/cryptocurrency/due_diligence/dd_controller.py index 6fa9e460d26..d924fc10d6a 100644 --- a/gamestonk_terminal/cryptocurrency/due_diligence/dd_controller.py +++ b/gamestonk_terminal/cryptocurrency/due_diligence/dd_controller.py @@ -10,6 +10,7 @@ from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.menu import session from gamestonk_terminal.cryptocurrency.due_diligence import ( + bybt_view, pycoingecko_view, coinpaprika_view, binance_view, @@ -30,15 +31,14 @@ import gamestonk_terminal.config_terminal as cfg class DueDiligenceController: - CHOICES = [ - "?", - "cls", - "help", - "q", - "quit", - "chart", + CHOICES = ["?", "cls", "help", "q", "quit", "chart"] + + CHOICES_COMMANDS = [ + "oi", ] + CHOICES += CHOICES_COMMANDS + SPECIFIC_CHOICES = { "cp": [ "events", @@ -72,7 +72,7 @@ class DueDiligenceController: "bin": binance_view, } - def __init__(self, coin=None, source=None): + def __init__(self, coin=None, source=None, symbol=None): """CONSTRUCTOR""" self._dd_parser = argparse.ArgumentParser(add_help=False, prog="dd") @@ -81,6 +81,7 @@ class DueDiligenceController: self.current_currency = None self.current_df = pd.DataFrame() self.source = source + self.symbol = symbol self.CHOICES.extend(self.SPECIFIC_CHOICES[self.source]) @@ -94,6 +95,9 @@ Due Diligence: ?/help show this menu again q quit this menu, and shows back to main menu quit quit to abandon the program + +Bybt: + oi open interest per exchange """ if self.source == "cp": help_text += """ @@ -180,6 +184,58 @@ Coinbase: """Process Quit command - quit the program.""" return True + def call_oi(self, other_args): + """Process oi command""" + if self.symbol: + assert isinstance(self.symbol, str) + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="oi", + description=""" + Displays open interest by exchange for a certain asset + [Source: https://bybt.gitbook.io] + """, + ) + + parser.add_argument( + "-i", + "--interval", + dest="interval", + type=int, + help="Frequency interval. Default: 0", + default=0, + choices=[0, 1, 2, 4], + ) + + parser.add_argument( + "--export", + choices=["csv", "json", "xlsx"], + default="", + type=str, + dest="export", + help="Export dataframe data to csv,json,xlsx file", + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + + if not ns_parser: + return + + bybt_view.display_open_interest( + symbol=self.symbol.upper(), + interval=ns_parser.interval, + export=ns_parser.export, + ) + + except Exception as e: + print(e) + else: + print( + "No coin selected. Use 'load' to load the coin you want to look at.\n" + ) + def call_info(self, other_args): """Process info command""" if self.current_coin: @@ -1364,10 +1420,10 @@ Coinbase: ) -def menu(coin=None, source=None): +def menu(coin=None, source=None, symbol=None): source = source if source else "cg" - dd_controller = DueDiligenceController(coin=coin, source=source) + dd_controller = DueDiligenceController(coin=coin, source=source, symbol=symbol) dd_controller.print_help() while True: diff --git a/gamestonk_terminal/cryptocurrency/due_diligence/pycoingecko_model.py b/gamestonk_terminal/cryptocurrency/due_diligence/pycoingecko_model.py index 9ea1abcd101..4667d92eb73 100644 --- a/gamestonk_terminal/cryptocurrency/due_diligence/pycoingecko_model.py +++ b/gamestonk_terminal/cryptocurrency/due_diligence/pycoingecko_model.py @@ -1,7 +1,7 @@ """CoinGecko model""" __docformat__ = "numpy" -from typing import Union, Any, Dict, List, Optional +from typing import Tuple, Union, Any, Dict, List, Optional import regex as re import pandas as pd from pycoingecko import CoinGeckoAPI @@ -44,7 +44,7 @@ class Coin: def __init__(self, symbol: str): self.client = CoinGeckoAPI() self._coin_list = self.client.get_coins_list() - self.coin_symbol = self._validate_coin(symbol) + self.coin_symbol, self.symbol = self._validate_coin(symbol) if self.coin_symbol: self.coin: Dict[Any, Any] = self._get_coin_info() @@ -52,7 +52,7 @@ class Coin: def __str__(self): return f"{self.coin_symbol}" - def _validate_coin(self, symbol: str) -> str: + def _validate_coin(self, search_coin: str) -> Tuple[Optional[Any], Optional[Any]]: """Validate if given coin symbol or id exists in list of available coins on CoinGecko. If yes it returns coin id. [Source: CoinGecko] @@ -63,19 +63,22 @@ class Coin: Returns ------- - str - id of the coin on CoinGecko service. + Tuple[str, str] + - str with coin + - str with symbol """ coin = None + symbol = None for dct in self._coin_list: - if symbol.lower() in list(dct.values()): + if search_coin.lower() in list(dct.values()): coin = dct.get("id") + symbol = dct.get("symbol") print(f"Coin found : {coin} with symbol {symbol}\n") break if not coin: raise ValueError(f"Could not find coin with the given id: {symbol}\n") - return coin + return coin, symbol def coin_list(self) -> list: """List all available coins [Source: CoinGecko] diff --git a/gamestonk_terminal/cryptocurrency/onchain/glassnode_view.py b/gamestonk_terminal/cryptocurrency/onchain/glassnode_view.py index b862c595093..8e0dc48cfdf 100644 --- a/gamestonk_terminal/cryptocurrency/onchain/glassnode_view.py +++ b/gamestonk_terminal/cryptocurrency/onchain/glassnode_view.py @@ -1,6 +1,5 @@ import os from matplotlib import pyplot as plt -import pandas as pd from gamestonk_terminal.cryptocurrency.onchain.glassnode_model import ( get_active_addresses, ) @@ -45,15 +44,9 @@ def display_active_addresses( def plot_data(df, asset): - df = df.set_index("t") - df.index = pd.to_datetime(df.index, unit="s") - df = df.loc[df.index > "2010-1-1"] - df.reset_index(inplace=True) - _, main_ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI) main_ax.plot(df["t"], df["v"], linewidth=0.5) - main_ax.set_yscale("log") main_ax.grid(True) main_ax.set_title(f"Active {asset} addresses over time") diff --git a/gamestonk_terminal/cryptocurrency/technical_analysis/ta_controller.py b/gamestonk_terminal/cryptocurrency/technical_analysis/ta_controller.py index 835eca769ce..09ee809ea20 100644 --- a/gamestonk_terminal/cryptocurrency/technical_analysis/ta_controller.py +++ b/gamestonk_terminal/cryptocurrency/technical_analysis/ta_controller.py @@ -71,7 +71,6 @@ class TechnicalAnalysisController: self.interval = interval self.stock = stock self.stock["Adj Close"] = stock["Close"] - print(self.stock) self.ta_parser = argparse.ArgumentParser(add_help=False, prog="ta") self.ta_parser.add_argument( diff --git a/tests/cassettes/test_cryptocurrency/test_coinbase/test_trading_pairs.yaml b/tests/cassettes/test_cryptocurrency/test_coinbase/test_trading_pairs.yaml new file mode 100644 index 00000000000..2de7cb59bb0 --- /dev/null +++ b/tests/cassettes/test_cryptocurrency/test_coinbase/test_trading_pairs.yaml @@ -0,0 +1,208 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + User-Agent: + - python-requests/2.26.0 + method: GET + uri: https://api.pro.coinbase.com/products + response: + body: + string: !!binary | + H4sIAAAAAAAAA+1da3PbxpL9Kyx9thUCJPjINxCEKFziQQOgAnFry8VYckoVmvTqkcfd2v++NaDi + 2Ll9hOnRiIyC9keNKI3VNTPdp0+f81//e3JzdfL9SVievw2X+cmbkx/Xd9fvPzzc3l5vP/y+Xzl5 + c/I/D7v7b7/85zd/utm+v7v59/XJ9yfd0263646/rKx/+2PFGXS7X37OzfbD7fWn6+39/iPOH9// + l6/v/6nVq5u7z5v17++360/X+z19t9+A+t2f1rc/X9+///iwvbqrPzjqq5X1b39dcbz6B9aLtz/d + bN9fb9c/bq6vTr7/uN7cXb85+fjb+7t79aUPu5vtl6/W/4vNzefP65+u33++vv1wvb1f/7T/7zqP + mzx5c/J5d3f/frfd/P7lg5ubTzd/+dKH9fbD9ebbr93frq9utj+9v7q5+3Y7d/fr+we18d12c7O9 + PvnjK+8/Xd/d7Xdw8uZk/fDh/ma3ff9pd3X9+NH/e/MY1mVRvl0W05KI67Ioqbh+/d3fBHZMRdX1 + Hv/zZFxhYImQLovyu8ffTcTUAQHlxfP+9qEhnF1HM5z7H3XgaEZZWYFTqpb0j6njUSe09+X/TkcT + xJMIptqN0QFt2/kMzlfofAbnK8b57BEBHTeEE5xOIp7B+erFD+frD+aZnycqmkQw1RKIJnhF+0RA + PcfgDSXCqXbz3f5XSzhhONMlCGa61A9l79TBVy37aKJ8KF1KOJvCGZbB20kZkBluQAX0q2/+9mw6 + Q+psPv1ymuS4wXf7LZBPqPqUC2Lb+0fFNXu47YRl0NndXl3fdn7c7X6+66xvrzvb3a+dm23n48Nm + 8/bx53dU4E87sdrUm87+r9JZb686d/e7z/sfsP/serOpP7/+ZX2zUTs61UukWXn0EdNo1lXw5be3 + I4tehPkCJV1qjVUVdcnrfdS181Cr7TyReHl/u6v9KAGd+NNZmKOQ7lefH9SRpeRrvx+JalNUnSgN + zsGdW69xbt0eVR95Y0uXbr0dycCaM7A6noE+yPj1d2uhjO7IMsr4uANuVNsU1h+Csxm6fNUa6+ql + bl633zV4T4mAqt3IxduIGacRSnXTiAVi9Mlo2i19l2nEvnlb1wb4IV6mPqh+6zVW/Uv3d4YNBTAr + Sao3ZVr+ui2KbFWu3s4mCyKuVUnix19987cX74AKqlFDgL58q3L13f6XkwEdupIl7auZtAQ9nkla + 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 + B5bwGXVI31mL6dEkOBfLAhTQ//5/3YKYNQFbAgA= + headers: + Access-Control-Allow-Headers: + - Content-Type, Accept, cb-session, cb-fp, cb-form-factor + Access-Control-Allow-Methods: + - GET,POST,DELETE,PUT + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - cb-before, cb-after, cb-gdpr + Access-Control-Max-Age: + - '7200' + CF-Cache-Status: + - HIT + CF-RAY: + - 6a4888296c0003c2-LIS + Cache-Control: + - public, max-age=5, must-revalidate + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 27 Oct 2021 02:35:35 GMT + Etag: + - W/"25b01-aN09APztSve2s3qUAs3Utjcwcfc" + Expect-CT: + - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15552000; includeSubDomains + Transfer-Encoding: + - chunked + Vary: + - Accept-Encoding + X-Content-Type-Options: + - nosniff + X-Dns-Prefetch-Control: + - 'off' + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_cryptocurrency/test_coingecko_crypto.py b/tests/test_cryptocurrency/test_coingecko_crypto.py index 400b20e1bbc..4eb11cb5094 100644 --- a/tests/test_cryptocurrency/test_coingecko_crypto.py +++ b/tests/test_cryptocurrency/test_coingecko_crypto.py @@ -34,7 +34,7 @@ def get_bitcoin(mock_load): with open("tests/data/btc_usd_test_data.json", encoding="utf8") as f: sample_return = json.load(f) mock_load.return_value = sample_return - coin, _ = load(coin="bitcoin", source="cg") + coin, _, _ = load(coin="bitcoin", source="cg") return coin diff --git a/website/content/cryptocurrency/due_diligence/oi/_index.md b/website/content/cryptocurrency/due_diligence/oi/_index.md new file mode 100644 index 00000000000..bb01f968754 --- /dev/null +++ b/website/content/cryptocurrency/due_diligence/oi/_index.md @@ -0,0 +1,25 @@ +``` +usage: oi [-i {0, 1, 2, 4}] + [--export {csv,json,xlsx}] [-h] +``` + +Display the open interest per exchange for a certain cryptocurrency. [Source: https://bybt.gitbook.io] +Crypto must be loaded (e.g., `load btc`) before running this feature. + +The total Bitcoin futures open interest across cryptocurrency exchanges, where open interest is calculated as the estimated notional value of all open futures positions, or the aggregate dollar value of outstanding contract specified BTC deliverables. Includes the largest exchanges with trustworthy reporting of exchange volume metrics. + +Interval: + +- ALL: 0 +- 1H: 2 +- 4H: 1 +- 12H: 4 + +``` +optional arguments: + -i INTERV --interval INTERV Interval frequency (default: 0) + --export {csv,json,xlsx} Export dataframe data to csv,json,xlsx file (default: ) + -h, --help show this help message (default: False) +``` + +Open Interest across crypto exchanges diff --git a/website/data/menu/main.yml b/website/data/menu/main.yml index 5dd83d06942..6c709ee205e 100755 --- a/website/data/menu/main.yml +++ b/website/data/menu/main.yml @@ -791,6 +791,8 @@ main: - name: due diligence ref: "/cryptocurrency/due_diligence" sub: + - name: oi + ref: "/cryptocurrency/due_diligence/oi" - name: info ref: "/cryptocurrency/due_diligence/info" - name: market