From cace78c242ed6c506d3e0fdf4c38cd7b353fc2fd Mon Sep 17 00:00:00 2001 From: jose-donato <43375532+jose-donato@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:04:44 +0100 Subject: [PATCH] Replace crypto exchange apis with CCXT (#2158) * ccxt added * updated text * added docs, tests * updated requirements * didier requests --- i18n/en.yml | 3 +- .../due_diligence/ccxt_model.py | 70 ++++++ .../cryptocurrency/due_diligence/ccxt_view.py | 86 +++++++ .../due_diligence/dd_controller.py | 221 +++++++----------- poetry.lock | 86 +++++++ pyproject.toml | 1 + requirements-full.txt | 3 + requirements.txt | 3 + scripts/test_crypto_dd.openbb | 2 + .../due_diligence/test_dd_controller.py | 15 +- .../test_dd_controller/test_print_help.txt | 5 +- .../terminal/crypto/dd/cbbook/_index.md | 17 -- .../content/terminal/crypto/dd/ob/_index.md | 12 + .../terminal/crypto/dd/trades/_index.md | 57 +---- website/data/menu/main.yml | 4 +- 15 files changed, 370 insertions(+), 215 deletions(-) create mode 100644 openbb_terminal/cryptocurrency/due_diligence/ccxt_model.py create mode 100644 openbb_terminal/cryptocurrency/due_diligence/ccxt_view.py delete mode 100644 website/content/terminal/crypto/dd/cbbook/_index.md create mode 100644 website/content/terminal/crypto/dd/ob/_index.md diff --git a/i18n/en.yml b/i18n/en.yml index 93ef5dbbc43..46a5b0b0ac2 100644 --- a/i18n/en.yml +++ b/i18n/en.yml @@ -619,10 +619,9 @@ en: crypto/dd/ex: all exchanges where loaded coin is listed crypto/dd/twitter: tweets for loaded coin crypto/dd/events: events related to loaded coin - crypto/dd/binbook: order book crypto/dd/balance: coin balance - crypto/dd/cbbook: order book crypto/dd/trades: last trades + crypto/dd/ob: order book crypto/dd/stats: coin stats crypto/dd/mcapdom: market cap dominance crypto/dd/mt: messari timeseries e.g. twitter followers, circ supply, etc diff --git a/openbb_terminal/cryptocurrency/due_diligence/ccxt_model.py b/openbb_terminal/cryptocurrency/due_diligence/ccxt_model.py new file mode 100644 index 00000000000..45d78ed5463 --- /dev/null +++ b/openbb_terminal/cryptocurrency/due_diligence/ccxt_model.py @@ -0,0 +1,70 @@ +"""Ccxt model""" +__docformat__ = "numpy" + +from typing import Dict +import ccxt +import pandas as pd + + +def get_exchanges(): + """Helper method to get all the exchanges supported by ccxt + [Source: https://docs.ccxt.com/en/latest/manual.html] + + Parameters + ---------- + + Returns + ------- + List[str] + list of all the exchanges supported by ccxt + """ + return ccxt.exchanges + + +def get_orderbook(exchange_id: str, coin: str, vs: str) -> Dict: + """Returns orderbook for a coin in a given exchange + [Source: https://docs.ccxt.com/en/latest/manual.html] + + Parameters + ---------- + exchange_id : str + exchange id + coin : str + coin symbol + vs : str + currency to compare coin against + + Returns + ------- + Dict with bids and asks + """ + exchange_class = getattr(ccxt, exchange_id) + exchange = exchange_class() + ob = exchange.fetch_order_book(f"{coin.upper()}/{vs.upper()}") + return ob + + +def get_trades(exchange_id: str, coin: str, vs: str) -> pd.DataFrame: + """Returns trades for a coin in a given exchange + [Source: https://docs.ccxt.com/en/latest/manual.html] + + Parameters + ---------- + exchange_id : str + exchange id + coin : str + coin symbol + vs : str + currency to compare coin against + + Returns + ------- + pd.DataFrame + trades for a coin in a given exchange + """ + exchange_class = getattr(ccxt, exchange_id) + exchange = exchange_class() + trades = exchange.fetch_trades(f"{coin.upper()}/{vs.upper()}") + df = pd.DataFrame(trades, columns=["datetime", "price", "amount", "cost", "side"]) + df["datetime"] = pd.to_datetime(df["datetime"]) + return df diff --git a/openbb_terminal/cryptocurrency/due_diligence/ccxt_view.py b/openbb_terminal/cryptocurrency/due_diligence/ccxt_view.py new file mode 100644 index 00000000000..0ed4c0075e4 --- /dev/null +++ b/openbb_terminal/cryptocurrency/due_diligence/ccxt_view.py @@ -0,0 +1,86 @@ +"""Ccxt view""" +__docformat__ = "numpy" + +import os +from typing import List, Optional + +from matplotlib import pyplot as plt +import numpy as np +from openbb_terminal.cryptocurrency.cryptocurrency_helpers import plot_order_book +from openbb_terminal.cryptocurrency.dataframe_helpers import prettify_column_names +from openbb_terminal.cryptocurrency.due_diligence import ccxt_model +from openbb_terminal.helper_funcs import export_data, print_rich_table + + +def display_order_book( + exchange: str, + coin: str, + vs: str, + export: str = "", + external_axes: Optional[List[plt.Axes]] = None, +): + """Displays order book for a coin in a given exchange + [Source: https://docs.ccxt.com/en/latest/manual.html] + + Parameters + ---------- + exchange : str + exchange id + coin : str + coin symbol + vs : str + currency to compare coin against + export : str + Export dataframe data to csv,json,xlsx file + """ + market_book = ccxt_model.get_orderbook(exchange_id=exchange, coin=coin, vs=vs) + bids = np.asarray(market_book["bids"], dtype=float) + asks = np.asarray(market_book["asks"], dtype=float) + bids = np.insert(bids, 2, bids[:, 1].cumsum(), axis=1) + asks = np.insert(asks, 2, np.flipud(asks[:, 1]).cumsum(), axis=1) + plot_order_book( + bids, asks, f"{exchange.upper()}:{coin.upper()}/{vs.upper()}", external_axes + ) + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "ob", + market_book, + ) + + +def display_trades(exchange: str, coin: str, vs: str, top: int = 10, export: str = ""): + """Displays trades for a coin in a given exchange + [Source: https://docs.ccxt.com/en/latest/manual.html] + + Parameters + ---------- + exchange : str + exchange id + coin : str + coin symbol + vs : str + currency to compare coin against + top : int + number of trades to display + export : str + Export dataframe data to csv,json,xlsx file + """ + df = ccxt_model.get_trades(exchange_id=exchange, coin=coin, vs=vs) + df.rename(columns={"datetime": "date"}, inplace=True) + + df.columns = prettify_column_names(df.columns) + print_rich_table( + df.head(top), + headers=list(df.columns), + show_index=False, + title=f"Trades for {exchange.upper()}:{coin.upper()}/{vs.upper()}", + ) + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "trades", + df, + ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py b/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py index 3f648177928..febb453847d 100644 --- a/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py +++ b/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py @@ -10,14 +10,15 @@ from prompt_toolkit.completion import NestedCompleter from openbb_terminal.cryptocurrency import cryptocurrency_helpers from openbb_terminal import feature_flags as obbff -from openbb_terminal.cryptocurrency.crypto_controller import CRYPTO_SOURCES from openbb_terminal.cryptocurrency.overview import cryptopanic_model from openbb_terminal.cryptocurrency.due_diligence import ( binance_model, binance_view, - coinbase_model, + ccxt_model, + ccxt_view, coinbase_view, coinglass_model, + coinbase_model, coinglass_view, coinpaprika_view, glassnode_model, @@ -78,10 +79,10 @@ class DueDiligenceController(CryptoBaseController): "dev", ], "bin": [ - "binbook", "balance", ], - "cb": ["cbbook", "trades", "stats"], + "ccxt": ["ob", "trades"], + "cb": ["stats"], "mes": ["mcapdom", "links", "rm", "tk", "pi", "mt", "team", "gov", "fr", "inv"], "san": ["gh"], "cpanic": ["news"], @@ -114,11 +115,16 @@ class DueDiligenceController(CryptoBaseController): self.symbol = symbol self.messari_timeseries = [] df_mt = messari_model.get_available_timeseries() + self.ccxt_exchanges = ccxt_model.get_exchanges() + if not df_mt.empty: self.messari_timeseries = df_mt.index.to_list() if session and obbff.USE_PROMPT_TOOLKIT: choices: dict = {c: {} for c in self.controller_choices} - choices["load"]["--source"] = {c: None for c in CRYPTO_SOURCES.keys()} + choices["ob"] = {c: None for c in self.ccxt_exchanges} + choices["ob"]["-e"] = {c: None for c in self.ccxt_exchanges} + choices["trades"] = {c: None for c in self.ccxt_exchanges} + choices["trades"]["-e"] = {c: None for c in self.ccxt_exchanges} choices["active"]["-i"] = { c: None for c in glassnode_model.INTERVALS_ACTIVE_ADDRESSES } @@ -177,14 +183,13 @@ class DueDiligenceController(CryptoBaseController): mt.add_info("_market_") mt.add_cmd("market", "CoinGecko") mt.add_cmd("mkt", "CoinPaprika") - mt.add_cmd("binbook", "Binance") mt.add_cmd("balance", "Binance") - mt.add_cmd("cbbook", "Coinbase") - mt.add_cmd("trades", "Coinbase") mt.add_cmd("ex", "CoinPaprika") mt.add_cmd("oi", "Coinglass") mt.add_cmd("fundrate", "Coinglass") mt.add_cmd("eb", "Glassnode") + mt.add_cmd("trades", "CCXT") + mt.add_cmd("ob", "CCXT") mt.add_info("_metrics_") mt.add_cmd("mcapdom", "Messari") @@ -271,6 +276,36 @@ class DueDiligenceController(CryptoBaseController): else: console.print("Glassnode source does not support this symbol\n") + @log_start_end(log=logger) + def call_stats(self, other_args): + """Process stats command""" + coin = self.symbol.upper() + _, quotes = coinbase_model.show_available_pairs_for_given_symbol(coin) + + parser = argparse.ArgumentParser( + prog="stats", + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + description="Display coin stats", + ) + + parser.add_argument( + "--vs", + help="Quote currency (what to view coin vs)", + dest="vs", + type=str, + default="USDT" if "USDT" in quotes else quotes[0], + choices=quotes, + ) + + ns_parser = self.parse_known_args_and_warn( + parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED + ) + + if ns_parser: + pair = f"{coin}-{ns_parser.vs.upper()}" + coinbase_view.display_stats(pair, ns_parser.export) + @log_start_end(log=logger) def call_active(self, other_args: List[str]): """Process active command""" @@ -728,80 +763,92 @@ class DueDiligenceController(CryptoBaseController): pycoingecko_view.display_bc(cg_id, ns_parser.export) @log_start_end(log=logger) - def call_binbook(self, other_args): - """Process book command""" + def call_ob(self, other_args): + """Process order book command""" parser = argparse.ArgumentParser( - prog="binbook", + prog="ob", add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, description="Get the order book for selected coin", ) - limit_list = [5, 10, 20, 50, 100, 500, 1000, 5000] - coin = self.symbol.upper() - _, quotes = binance_model.show_available_pairs_for_given_symbol(coin) parser.add_argument( - "-l", - "--limit", - dest="limit", - help="Limit parameter. Adjusts the weight", - default=100, - type=int, - choices=limit_list, + "-e", + "--exchange", + help="Exchange to search for order book", + dest="exchange", + type=str, + default="binance", + choices=self.ccxt_exchanges, ) parser.add_argument( "--vs", help="Quote currency (what to view coin vs)", dest="vs", - type=str, - default="USDT", - choices=quotes, + type=str.lower, + default="usdt", + choices=["usdt", "usdc", "btc"], ) + if other_args and not other_args[0][0] == "-": + other_args.insert(0, "-e") + ns_parser = self.parse_known_args_and_warn( parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES ) + if ns_parser: - binance_view.display_order_book( - coin=coin, - limit=ns_parser.limit, - currency=ns_parser.vs, + ccxt_view.display_order_book( + ns_parser.exchange, + coin=self.symbol, + vs=ns_parser.vs, export=ns_parser.export, ) @log_start_end(log=logger) - def call_cbbook(self, other_args): - """Process cbbook command""" - coin = self.symbol.upper() + def call_trades(self, other_args): + """Process trades command""" parser = argparse.ArgumentParser( - prog="cbbook", + prog="trades", add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Get the order book for selected coin", + description="Get the latest trades for selected coin", ) - _, quotes = coinbase_model.show_available_pairs_for_given_symbol(coin) - if len(quotes) < 0: - console.print(f"Couldn't find any quoted coins for provided symbol {coin}") + parser.add_argument( + "-e", + "--exchange", + help="Exchange to search for order book", + dest="exchange", + type=str, + default="binance", + choices=self.ccxt_exchanges, + ) parser.add_argument( "--vs", help="Quote currency (what to view coin vs)", dest="vs", - type=str, - default="USDT" if "USDT" in quotes else quotes[0], - choices=quotes, + type=str.lower, + default="usdt", + choices=["usdt", "usdc", "btc"], ) + if other_args and not other_args[0][0] == "-": + other_args.insert(0, "-e") + ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES + parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, limit=10 ) + if ns_parser: - pair = f"{coin}-{ns_parser.vs.upper()}" - coinbase_view.display_order_book( - product_id=pair, + ccxt_view.display_trades( + ns_parser.exchange, + coin=self.symbol, + vs=ns_parser.vs, export=ns_parser.export, + top=ns_parser.limit, ) @log_start_end(log=logger) @@ -835,94 +882,6 @@ class DueDiligenceController(CryptoBaseController): coin=coin, currency=ns_parser.vs, export=ns_parser.export ) - @log_start_end(log=logger) - def call_trades(self, other_args): - """Process trades command""" - parser = argparse.ArgumentParser( - prog="trades", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Show last trades on Coinbase", - ) - coin = self.symbol.upper() - _, quotes = coinbase_model.show_available_pairs_for_given_symbol(coin) - if len(quotes) < 0: - console.print( - f"Couldn't find any quoted coins for provided symbol {self.symbol}" - ) - - parser.add_argument( - "--vs", - help="Quote currency (what to view coin vs)", - dest="vs", - type=str, - default="USDT" if "USDT" in quotes else quotes[0], - choices=quotes, - ) - - parser.add_argument( - "--side", - help="Side of trade: buy, sell, all", - dest="side", - type=str, - default="all", - choices=["all", "buy", "sell"], - ) - - parser.add_argument( - "-t", - "--top", - default=15, - dest="top", - help="Limit of records", - type=check_positive, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - pair = f"{coin}-{ns_parser.vs.upper()}" - if ns_parser.side.upper() == "all": - side = None - else: - side = ns_parser.side - - coinbase_view.display_trades( - product_id=pair, limit=ns_parser.top, side=side, export=ns_parser.export - ) - - @log_start_end(log=logger) - def call_stats(self, other_args): - """Process stats command""" - coin = self.symbol.upper() - _, quotes = coinbase_model.show_available_pairs_for_given_symbol(coin) - - parser = argparse.ArgumentParser( - prog="stats", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Display coin stats", - ) - - parser.add_argument( - "--vs", - help="Quote currency (what to view coin vs)", - dest="vs", - type=str, - default="USDT" if "USDT" in quotes else quotes[0], - choices=quotes, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - pair = f"{coin}-{ns_parser.vs.upper()}" - coinbase_view.display_stats(pair, ns_parser.export) - @log_start_end(log=logger) def call_ps(self, other_args): """Process ps command""" diff --git a/poetry.lock b/poetry.lock index 8ebb77a0e51..c2f9f51038a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,17 @@ category = "main" optional = true python-versions = ">=3.6" +[[package]] +name = "aiodns" +version = "3.0.0" +description = "Simple DNS resolver for asyncio" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycares = ">=4.0.0" + [[package]] name = "aiohttp" version = "3.8.1" @@ -459,6 +470,26 @@ category = "main" optional = true python-versions = "~=3.7" +[[package]] +name = "ccxt" +version = "1.86.3" +description = "A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +aiodns = {version = ">=1.1.1", markers = "python_version >= \"3.5.2\""} +aiohttp = {version = ">=3.8", markers = "python_version >= \"3.5.2\""} +certifi = ">=2018.1.18" +cryptography = ">=2.6.1" +requests = ">=2.18.4" +yarl = {version = "1.7.2", markers = "python_version >= \"3.5.2\""} + +[package.extras] +doc = ["Sphinx (==4.0)", "mistune (==0.8.4)", "m2r2 (==0.2.7)", "sphinx-rtd-theme (==0.5.2)", "readthedocs-sphinx-search (==0.1.0)"] +qa = ["flake8 (==3.7.9)"] + [[package]] name = "certifi" version = "2022.6.15" @@ -2727,6 +2758,20 @@ python-versions = "*" [package.dependencies] pyasn1 = ">=0.4.6,<0.5.0" +[[package]] +name = "pycares" +version = "4.1.2" +description = "Python interface for c-ares" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +cffi = ">=1.5.0" + +[package.extras] +idna = ["idna (>=2.1)"] + [[package]] name = "pycodestyle" version = "2.7.0" @@ -4657,6 +4702,10 @@ absl-py = [ {file = "absl-py-1.1.0.tar.gz", hash = "sha256:3aa39f898329c2156ff525dfa69ce709e42d77aab18bf4917719d6f260aa6a08"}, {file = "absl_py-1.1.0-py3-none-any.whl", hash = "sha256:db97287655e30336938f8058d2c81ed2be6af1d9b6ebbcd8df1080a6c7fcd24e"}, ] +aiodns = [ + {file = "aiodns-3.0.0-py3-none-any.whl", hash = "sha256:2b19bc5f97e5c936638d28e665923c093d8af2bf3aa88d35c43417fa25d136a2"}, + {file = "aiodns-3.0.0.tar.gz", hash = "sha256:946bdfabe743fceeeb093c8a010f5d1645f708a241be849e17edfb0e49e08cd6"}, +] aiohttp = [ {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"}, {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"}, @@ -4958,6 +5007,10 @@ cachetools = [ {file = "cachetools-5.2.0-py3-none-any.whl", hash = "sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db"}, {file = "cachetools-5.2.0.tar.gz", hash = "sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757"}, ] +ccxt = [ + {file = "ccxt-1.86.3-py2.py3-none-any.whl", hash = "sha256:7dde0d162ffa49e0696d253a2566da44b123b659bc3761b61844762e98e20e5a"}, + {file = "ccxt-1.86.3.tar.gz", hash = "sha256:7c8712c4f0efb058341690927119176cc4bafe924b5cea5bfc06516bc4fe2a01"}, +] certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, @@ -6471,6 +6524,39 @@ pyasn1-modules = [ {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"}, {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, ] +pycares = [ + {file = "pycares-4.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:71b99b9e041ae3356b859822c511f286f84c8889ec9ed1fbf6ac30fb4da13e4c"}, + {file = "pycares-4.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c000942f5fc64e6e046aa61aa53b629b576ba11607d108909727c3c8f211a157"}, + {file = "pycares-4.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b0e50ddc78252f2e2b6b5f2c73e5b2449dfb6bea7a5a0e21dfd1e2bcc9e17382"}, + {file = "pycares-4.1.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6831e963a910b0a8cbdd2750ffcdf5f2bb0edb3f53ca69ff18484de2cc3807c4"}, + {file = "pycares-4.1.2-cp310-cp310-win32.whl", hash = "sha256:ad7b28e1b6bc68edd3d678373fa3af84e39d287090434f25055d21b4716b2fc6"}, + {file = "pycares-4.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:27a6f09dbfb69bb79609724c0f90dfaa7c215876a7cd9f12d585574d1f922112"}, + {file = "pycares-4.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e5a060f5fa90ae245aa99a4a8ad13ec39c2340400de037c7e8d27b081e1a3c64"}, + {file = "pycares-4.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:056330275dea42b7199494047a745e1d9785d39fb8c4cd469dca043532240b80"}, + {file = "pycares-4.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0aa897543a786daba74ec5e19638bd38b2b432d179a0e248eac1e62de5756207"}, + {file = "pycares-4.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cbceaa9b2c416aa931627466d3240aecfc905c292c842252e3d77b8630072505"}, + {file = "pycares-4.1.2-cp36-cp36m-win32.whl", hash = "sha256:112e1385c451069112d6b5ea1f9c378544f3c6b89882ff964e9a64be3336d7e4"}, + {file = "pycares-4.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:c6680f7fdc0f1163e8f6c2a11d11b9a0b524a61000d2a71f9ccd410f154fb171"}, + {file = "pycares-4.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58a41a2baabcd95266db776c510d349d417919407f03510fc87ac7488730d913"}, + {file = "pycares-4.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a810d01c9a426ee8b0f36969c2aef5fb966712be9d7e466920beb328cd9cefa3"}, + {file = "pycares-4.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b266cec81dcea2c3efbbd3dda00af8d7eb0693ae9e47e8706518334b21f27d4a"}, + {file = "pycares-4.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8319afe4838e09df267c421ca93da408f770b945ec6217dda72f1f6a493e37e4"}, + {file = "pycares-4.1.2-cp37-cp37m-win32.whl", hash = "sha256:4d5da840aa0d9b15fa51107f09270c563a348cb77b14ae9653d0bbdbe326fcc2"}, + {file = "pycares-4.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:5632f21d92cc0225ba5ff906e4e5dec415ef0b3df322c461d138190681cd5d89"}, + {file = "pycares-4.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8fd1ff17a26bb004f0f6bb902ba7dddd810059096ae0cc3b45e4f5be46315d19"}, + {file = "pycares-4.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:439799be4b7576e907139a7f9b3c8a01b90d3e38af4af9cd1fc6c1ee9a42b9e6"}, + {file = "pycares-4.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:40079ed58efa91747c50aac4edf8ecc7e570132ab57dc0a4030eb0d016a6cab8"}, + {file = "pycares-4.1.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e190471a015f8225fa38069617192e06122771cce2b169ac7a60bfdbd3d4ab2"}, + {file = "pycares-4.1.2-cp38-cp38-win32.whl", hash = "sha256:2b837315ed08c7df009b67725fe1f50489e99de9089f58ec1b243dc612f172aa"}, + {file = "pycares-4.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:c7eba3c8354b730a54d23237d0b6445a2f68570fa68d0848887da23a3f3b71f3"}, + {file = "pycares-4.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2f5f84fe9f83eab9cd68544b165b74ba6e3412d029cc9ab20098d9c332869fc5"}, + {file = "pycares-4.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569eef8597b5e02b1bc4644b9f272160304d8c9985357d7ecfcd054da97c0771"}, + {file = "pycares-4.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e1489aa25d14dbf7176110ead937c01176ed5a0ebefd3b092bbd6b202241814c"}, + {file = "pycares-4.1.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dc942692fca0e27081b7bb414bb971d34609c80df5e953f6d0c62ecc8019acd9"}, + {file = "pycares-4.1.2-cp39-cp39-win32.whl", hash = "sha256:ed71dc4290d9c3353945965604ef1f6a4de631733e9819a7ebc747220b27e641"}, + {file = "pycares-4.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:ec00f3594ee775665167b1a1630edceefb1b1283af9ac57480dba2fb6fd6c360"}, + {file = "pycares-4.1.2.tar.gz", hash = "sha256:03490be0e7b51a0c8073f877bec347eff31003f64f57d9518d419d9369452837"}, +] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, diff --git a/pyproject.toml b/pyproject.toml index 79d6d791392..555b347a82e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,6 +102,7 @@ jupyterlab-code-formatter = {version = "^1.4.10", optional = true} jupyterlab-lsp = {version = "^3.10.1", optional = true} jedi-language-server = {version = "^0.35.1", optional = true} kaleido = {version = "<=0.2.1", optional = true} +ccxt = "^1.86.3" html5lib = "^1.1" tensorflow-macos = {version = "^2.9.2", optional = true} tensorflow-metal = {version = "^0.5.0", optional = true} diff --git a/requirements-full.txt b/requirements-full.txt index 2cccff53b57..0d484ef5b66 100644 --- a/requirements-full.txt +++ b/requirements-full.txt @@ -1,4 +1,5 @@ absl-py==1.1.0; python_version >= "3.7" +aiodns==3.0.0; python_version >= "3.5.2" aiohttp==3.8.1; python_version >= "3.6" aiosignal==1.2.0; python_version >= "3.6" alabaster==0.7.12; python_version >= "3.6" @@ -30,6 +31,7 @@ bleach==5.0.1; python_version >= "3.7" bs4==0.0.1 bt==0.2.9; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0") cachetools==5.2.0; python_version >= "3.7" and python_version < "4.0" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") +ccxt==1.86.3 certifi==2022.6.15; python_version >= "3.8" and python_version < "4" and (python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.7") and python_full_version >= "3.7.1" and python_full_version < "4.0.0" cffi==1.15.1; python_version >= "3.7" cfgv==3.3.1; python_full_version >= "3.6.1" and python_version >= "3.7" @@ -194,6 +196,7 @@ py==1.11.0; python_full_version >= "3.7.0" and python_version >= "3.7" and imple pyally==1.1.2 pyasn1-modules==0.2.8; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" pyasn1==0.4.8; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") or python_full_version >= "3.6.0" and python_version >= "3.7" and python_version < "4" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") +pycares==4.1.2; python_version >= "3.5.2" pycodestyle==2.7.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pycoingecko==2.2.0 pycparser==2.21; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" diff --git a/requirements.txt b/requirements.txt index 3599dc6f755..edb756b849a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +aiodns==3.0.0; python_version >= "3.5.2" aiohttp==3.8.1; python_version >= "3.6" aiosignal==1.2.0; python_version >= "3.6" alpha-vantage==2.3.1 @@ -23,6 +24,7 @@ black==22.6.0; python_full_version >= "3.6.2" bleach==5.0.1; python_version >= "3.7" bs4==0.0.1 bt==0.2.9; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0") +ccxt==1.86.3 certifi==2022.6.15; python_version >= "3.8" and python_version < "4" and (python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.7") and python_full_version >= "3.7.1" and python_full_version < "4.0.0" cffi==1.15.1; python_version >= "3.7" charset-normalizer==2.1.0; python_version >= "3.8" and python_version < "4" and python_full_version >= "3.7.1" and python_full_version < "4.0.0" @@ -152,6 +154,7 @@ ptyprocess==0.7.0; os_name != "nt" and python_version >= "3.8" and sys_platform pure-eval==0.2.2; python_full_version >= "3.6.2" and python_version >= "3.8" py==1.11.0; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.7.0" pyally==1.1.2 +pycares==4.1.2; python_version >= "3.5.2" pycoingecko==2.2.0 pycparser==2.21; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" pyerfa==2.0.0.1; python_version >= "3.8" diff --git a/scripts/test_crypto_dd.openbb b/scripts/test_crypto_dd.openbb index 3ee8fffc852..fd8486f0850 100644 --- a/scripts/test_crypto_dd.openbb +++ b/scripts/test_crypto_dd.openbb @@ -25,4 +25,6 @@ fr links gh news +ob +trades exit diff --git a/tests/openbb_terminal/cryptocurrency/due_diligence/test_dd_controller.py b/tests/openbb_terminal/cryptocurrency/due_diligence/test_dd_controller.py index dd694862ff5..b55b5e90361 100644 --- a/tests/openbb_terminal/cryptocurrency/due_diligence/test_dd_controller.py +++ b/tests/openbb_terminal/cryptocurrency/due_diligence/test_dd_controller.py @@ -373,16 +373,16 @@ def test_call_func_expect_queue(expected_queue, func, queue, mocker): dict(), ), ( - "call_binbook", + "call_ob", [], - "binance_view.display_order_book", + "ccxt_view.display_order_book", [], dict(), ), ( - "call_cbbook", + "call_trades", [], - "coinbase_view.display_order_book", + "ccxt_view.display_trades", [], dict(), ), @@ -393,13 +393,6 @@ def test_call_func_expect_queue(expected_queue, func, queue, mocker): [], dict(), ), - ( - "call_trades", - [], - "coinbase_view.display_trades", - [], - dict(), - ), ( "call_stats", [], diff --git a/tests/openbb_terminal/cryptocurrency/due_diligence/txt/test_dd_controller/test_print_help.txt b/tests/openbb_terminal/cryptocurrency/due_diligence/txt/test_dd_controller/test_print_help.txt index 7c9da447a36..bb274a0836d 100644 --- a/tests/openbb_terminal/cryptocurrency/due_diligence/txt/test_dd_controller/test_print_help.txt +++ b/tests/openbb_terminal/cryptocurrency/due_diligence/txt/test_dd_controller/test_print_help.txt @@ -16,14 +16,13 @@ Overview: Market: market market stats about loaded coin [CoinGecko] mkt all markets for loaded coin [CoinPaprika] - binbook order book [Binance] balance coin balance [Binance] - cbbook order book [Coinbase] - trades last trades [Coinbase] ex all exchanges where loaded coin is listed [CoinPaprika] oi open interest per exchange [Coinglass] fundrate funding rate per exchange [Coinglass] eb total balance held on exchanges (in percentage and units) [Glassnode] + trades last trades [CCXT] + ob order book [CCXT] Metrics: mcapdom market cap dominance [Messari] active active addresses [Glassnode] diff --git a/website/content/terminal/crypto/dd/cbbook/_index.md b/website/content/terminal/crypto/dd/cbbook/_index.md deleted file mode 100644 index cafb9c3e950..00000000000 --- a/website/content/terminal/crypto/dd/cbbook/_index.md +++ /dev/null @@ -1,17 +0,0 @@ -``` -usage: book [-l {5,10,20,50,100,500,1000,5000}] [--vs {USDT,TUSD,USDC,BUSD,NGN,RUB,TRY,EUR,IDRT,GBP,UAH,BIDR,AUD,DAI,BRL,VAI,USDP}] - [--export {csv,json,xlsx}] [-h] -``` - -Get the order book for selected coin - -``` -optional arguments: - --vs {USDC,UST,USDT,EUR,GBP,USD} - Quote currency (what to view coin vs) (default: USDT) - -h, --help show this help message (default: False) - --export {csv,json,xlsx,png,jpg,pdf,svg} - Export raw data into csv, json, xlsx and figure into png, jpg, pdf, svg (default: ) -``` - -![cbbook](https://user-images.githubusercontent.com/46355364/154059918-6d3b17e8-4e33-4e07-9d7d-9826564561b8.png) diff --git a/website/content/terminal/crypto/dd/ob/_index.md b/website/content/terminal/crypto/dd/ob/_index.md new file mode 100644 index 00000000000..ba7dd822548 --- /dev/null +++ b/website/content/terminal/crypto/dd/ob/_index.md @@ -0,0 +1,12 @@ +`usage: ob [-e EXCHANGE] [--vs {usdt,usdc,btc}] [-h] [--export EXPORT]` + +Get the order book for selected coin + +``` +optional arguments: + -e {aax,ascendex,bequant,bibox,bigone,binance,binancecoinm,binanceus,binanceusdm,bit2c,bitbank,bitbay,bitbns,bitcoincom,bitfinex,bitfinex2,bitflyer,bitforex,bitget,bithumb,bitmart,bitmex,bitopro,bitpanda,bitrue,bitso,bitstamp,bitstamp1,bittrex,bitvavo,bkex,bl3p,blockchaincom,btcalpha,btcbox,btcmarkets,btctradeua,btcturk,buda,bw,bybit,bytetrade,cdax,cex,coinbase,coinbaseprime,coinbasepro,coincheck,coinex,coinfalcon,coinflex,coinmate,coinone,coinspot,crex24,cryptocom,currencycom,delta,deribit,digifinex,eqonex,exmo,flowbtc,fmfwio,ftx,ftxus,gateio,gemini,hitbtc,hitbtc3,hollaex,huobi,huobijp,huobipro,idex,independentreserve,indodax,itbit,kraken,kucoin,kucoinfutures,kuna,latoken,lbank,lbank2,liquid,luno,lykke,mercado,mexc,mexc3,ndax,novadax,oceanex,okcoin,okex,okex5,okx,paymium,phemex,poloniex,probit,qtrade,ripio,stex,therock,tidebit,tidex,timex,upbit,vcc,wavesexchange,wazirx,whitebit,woo,xena,yobit,zaif,zb,zipmex,zonda}, --exchange {aax,ascendex,bequant,bibox,bigone,binance,binancecoinm,binanceus,binanceusdm,bit2c,bitbank,bitbay,bitbns,bitcoincom,bitfinex,bitfinex2,bitflyer,bitforex,bitget,bithumb,bitmart,bitmex,bitopro,bitpanda,bitrue,bitso,bitstamp,bitstamp1,bittrex,bitvavo,bkex,bl3p,blockchaincom,btcalpha,btcbox,btcmarkets,btctradeua,btcturk,buda,bw,bybit,bytetrade,cdax,cex,coinbase,coinbaseprime,coinbasepro,coincheck,coinex,coinfalcon,coinflex,coinmate,coinone,coinspot,crex24,cryptocom,currencycom,delta,deribit,digifinex,eqonex,exmo,flowbtc,fmfwio,ftx,ftxus,gateio,gemini,hitbtc,hitbtc3,hollaex,huobi,huobijp,huobipro,idex,independentreserve,indodax,itbit,kraken,kucoin,kucoinfutures,kuna,latoken,lbank,lbank2,liquid,luno,lykke,mercado,mexc,mexc3,ndax,novadax,oceanex,okcoin,okex,okex5,okx,paymium,phemex,poloniex,probit,qtrade,ripio,stex,therock,tidebit,tidex,timex,upbit,vcc,wavesexchange,wazirx,whitebit,woo,xena,yobit,zaif,zb,zipmex,zonda} + Exchange to search for order book (default: binance) + --vs {usdt,usdc,btc} Quote currency (what to view coin vs) (default: usdt) + -h, --help show this help message (default: False) + --export EXPORT Export raw data into csv, json, xlsx and figure into png, jpg, pdf, svg (default: ) +``` diff --git a/website/content/terminal/crypto/dd/trades/_index.md b/website/content/terminal/crypto/dd/trades/_index.md index 5af4ca05a0e..4b5f8474ee3 100644 --- a/website/content/terminal/crypto/dd/trades/_index.md +++ b/website/content/terminal/crypto/dd/trades/_index.md @@ -1,55 +1,14 @@ -``` -usage: trades [--vs {EUR,USDC,GBP,USD,USDT,UST}] [--side {all,buy,sell}] [-t TOP] [--export {csv,json,xlsx}] [-h] -``` +`usage: trades [-e EXCHANGE] [--vs {usdt,usdc,btc}] [-h] [--export EXPORT] [-l LIMIT]` -Show last trades on Coinbase +Get the latest trades for selected coin ``` optional arguments: - --vs {EUR,USDC,GBP,USD,USDT,UST} - Quote currency (what to view coin vs) (default: USDT) - --side {all,buy,sell} - Side of trade: buy, sell, all (default: all) - -t TOP, --top TOP Limit of records (default: 15) - --export {csv,json,xlsx} - Export dataframe data to csv,json,xlsx file (default: ) + -e {aax,ascendex,bequant,bibox,bigone,binance,binancecoinm,binanceus,binanceusdm,bit2c,bitbank,bitbay,bitbns,bitcoincom,bitfinex,bitfinex2,bitflyer,bitforex,bitget,bithumb,bitmart,bitmex,bitopro,bitpanda,bitrue,bitso,bitstamp,bitstamp1,bittrex,bitvavo,bkex,bl3p,blockchaincom,btcalpha,btcbox,btcmarkets,btctradeua,btcturk,buda,bw,bybit,bytetrade,cdax,cex,coinbase,coinbaseprime,coinbasepro,coincheck,coinex,coinfalcon,coinflex,coinmate,coinone,coinspot,crex24,cryptocom,currencycom,delta,deribit,digifinex,eqonex,exmo,flowbtc,fmfwio,ftx,ftxus,gateio,gemini,hitbtc,hitbtc3,hollaex,huobi,huobijp,huobipro,idex,independentreserve,indodax,itbit,kraken,kucoin,kucoinfutures,kuna,latoken,lbank,lbank2,liquid,luno,lykke,mercado,mexc,mexc3,ndax,novadax,oceanex,okcoin,okex,okex5,okx,paymium,phemex,poloniex,probit,qtrade,ripio,stex,therock,tidebit,tidex,timex,upbit,vcc,wavesexchange,wazirx,whitebit,woo,xena,yobit,zaif,zb,zipmex,zonda}, --exchange {aax,ascendex,bequant,bibox,bigone,binance,binancecoinm,binanceus,binanceusdm,bit2c,bitbank,bitbay,bitbns,bitcoincom,bitfinex,bitfinex2,bitflyer,bitforex,bitget,bithumb,bitmart,bitmex,bitopro,bitpanda,bitrue,bitso,bitstamp,bitstamp1,bittrex,bitvavo,bkex,bl3p,blockchaincom,btcalpha,btcbox,btcmarkets,btctradeua,btcturk,buda,bw,bybit,bytetrade,cdax,cex,coinbase,coinbaseprime,coinbasepro,coincheck,coinex,coinfalcon,coinflex,coinmate,coinone,coinspot,crex24,cryptocom,currencycom,delta,deribit,digifinex,eqonex,exmo,flowbtc,fmfwio,ftx,ftxus,gateio,gemini,hitbtc,hitbtc3,hollaex,huobi,huobijp,huobipro,idex,independentreserve,indodax,itbit,kraken,kucoin,kucoinfutures,kuna,latoken,lbank,lbank2,liquid,luno,lykke,mercado,mexc,mexc3,ndax,novadax,oceanex,okcoin,okex,okex5,okx,paymium,phemex,poloniex,probit,qtrade,ripio,stex,therock,tidebit,tidex,timex,upbit,vcc,wavesexchange,wazirx,whitebit,woo,xena,yobit,zaif,zb,zipmex,zonda} + Exchange to search for order book (default: binance) + --vs {usdt,usdc,btc} Quote currency (what to view coin vs) (default: usdt) -h, --help show this help message (default: False) -``` - -Example: -``` -2022 Feb 15, 07:48 (✨) /crypto/dd/ $ trades -┌─────────────────────────────┬────────────────┬────────────┬──────┐ -│ time │ price │ size │ side │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:48:49.429769Z │ 44210.63000000 │ 0.04045752 │ sell │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:48:49.429769Z │ 44207.34000000 │ 0.02260937 │ sell │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:48:31.30732Z │ 44184.04000000 │ 0.00238891 │ buy │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:48:25.51977Z │ 44179.84000000 │ 0.00000216 │ sell │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:48:24.96678Z │ 44179.84000000 │ 0.02262000 │ sell │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:45.136476Z │ 44183.84000000 │ 0.00046768 │ buy │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:40.197541Z │ 44198.63000000 │ 0.00000891 │ buy │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:36.791631Z │ 44198.63000000 │ 0.00011859 │ buy │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:36.791631Z │ 44198.63000000 │ 0.00074141 │ buy │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:28.468736Z │ 44201.98000000 │ 0.00013180 │ buy │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:19.90752Z │ 44209.80000000 │ 0.05338614 │ sell │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:19.90752Z │ 44207.83000000 │ 0.02260811 │ sell │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:14.403975Z │ 44206.47000000 │ 0.00027010 │ sell │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:13.700917Z │ 44206.47000000 │ 0.00027077 │ sell │ -├─────────────────────────────┼────────────────┼────────────┼──────┤ -│ 2022-02-15T12:47:12.41306Z │ 44211.76000000 │ 0.25000000 │ buy │ -└─────────────────────────────┴────────────────┴────────────┴──────┘ + --export EXPORT Export raw data into csv, json, xlsx and figure into png, jpg, pdf, svg (default: ) + -l LIMIT, --limit LIMIT + Number of entries to show in data. (default: 10) ``` diff --git a/website/data/menu/main.yml b/website/data/menu/main.yml index 2aa545f703d..fe0de409227 100755 --- a/website/data/menu/main.yml +++ b/website/data/menu/main.yml @@ -752,6 +752,8 @@ main: ref: "/terminal/crypto/dd/fundrate" - name: oi ref: "/terminal/crypto/dd/oi" + - name: ob + ref: "/terminal/crypto/dd/ob" - name: info ref: "/terminal/crypto/dd/info" - name: market @@ -782,8 +784,6 @@ main: ref: "/terminal/crypto/dd/twitter" - name: events ref: "/terminal/crypto/dd/events" - - name: cbbook - ref: "/terminal/crypto/dd/cbbook" - name: balance ref: "/terminal/crypto/dd/balance" - name: trades